Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Oleh Kirk Larkin
Pengikatan model memungkinkan aksi kontrol bekerja langsung dengan tipe model (diberikan sebagai argumen metode), daripada permintaan HTTP. Pemetaan antara data permintaan masuk dan model aplikasi ditangani oleh pengikat model. Para pengembang dapat memperluas fungsionalitas pengikatan model yang sudah ada dengan menerapkan pengikat model kustom (meskipun pada umumnya, Anda tidak perlu membuat penyedia Anda sendiri).
Melihat atau mengunduh kode sampel (cara mengunduh)
Keterbatasan pengikat model bawaan
Pengikat model default mendukung sebagian besar jenis data .NET Core umum dan harus memenuhi kebutuhan sebagian besar pengembang. Mereka berharap untuk mengikat input berbasis teks dari permintaan langsung ke jenis model. Anda mungkin perlu mengubah input sebelum mengikatnya. Misalnya, ketika Anda memiliki kunci yang dapat digunakan untuk mencari data model. Anda dapat menggunakan pengikat model kustom untuk mengambil data berdasarkan kunci.
Pengikatan model tipe sederhana dan kompleks
Pengikatan model menggunakan definisi tertentu untuk jenis yang dioperasikannya.
Jenis sederhana dikonversi dari satu string menggunakan metode TypeConverter atau TryParse.
Jenis kompleks dikonversi dari beberapa nilai input. Kerangka kerja menentukan perbedaan berdasarkan keberadaan TypeConverter atau TryParse. Sebaiknya buat pengonversi jenis atau gunakan TryParse untuk string konversi ke SomeType yang tidak memerlukan sumber daya eksternal atau beberapa input.
Lihat Jenis sederhana untuk daftar jenis yang dapat dikonversi oleh pengikat model dari string.
Sebelum membuat pengikat model kustom Anda sendiri, ada baiknya meninjau bagaimana pengikat model yang ada diterapkan. Pertimbangkan ByteArrayModelBinder yang dapat digunakan untuk mengonversi string yang dikodekan dalam base64 menjadi array byte. Array byte sering disimpan sebagai file atau bidang BLOB database.
Bekerja dengan ByteArrayModelBinder
String yang dikodekan Base64 dapat digunakan untuk mewakili data biner. Misalnya, gambar dapat dikodekan sebagai string. Sampel menyertakan gambar sebagai string yang dikodekan base64 di Base64String.txt.
ASP.NET Core MVC dapat mengambil string yang dikodekan base64 dan menggunakan ByteArrayModelBinder untuk mengonversinya menjadi array byte. Argumen ByteArrayModelBinderProvider memetakan byte[] ke ByteArrayModelBinder:
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(byte[]))
{
var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
return new ByteArrayModelBinder(loggerFactory);
}
return null;
}
Saat membuat binder model kustom Anda sendiri, Anda bisa menerapkan jenis IModelBinderProvider Anda sendiri, atau menggunakan ModelBinderAttribute.
Contoh berikut menunjukkan cara menggunakan ByteArrayModelBinder untuk mengonversi string yang dikodekan base64 menjadi byte[] dan menyimpan hasilnya ke file:
[HttpPost]
public void Post([FromForm] byte[] file, string filename)
{
// Don't trust the file name sent by the client. Use
// Path.GetRandomFileName to generate a safe random
// file name. _targetFilePath receives a value
// from configuration (the appsettings.json file in
// the sample app).
var trustedFileName = Path.GetRandomFileName();
var filePath = Path.Combine(_targetFilePath, trustedFileName);
if (System.IO.File.Exists(filePath))
{
return;
}
System.IO.File.WriteAllBytes(filePath, file);
}
Jika Anda ingin melihat komentar kode yang diterjemahkan ke bahasa selain bahasa Inggris, beri tahu kami dalam masalah diskusi GitHub ini.
Anda dapat MEMPOSTING string yang dikodekan base64 ke metode api sebelumnya menggunakan alat seperti curl.
Selama pengikat dapat mengikat data permintaan ke properti atau argumen bernama dengan tepat, pengikatan model akan berhasil. Contoh berikut menunjukkan cara menggunakan ByteArrayModelBinder dengan model tampilan:
[HttpPost("Profile")]
public void SaveProfile([FromForm] ProfileViewModel model)
{
// Don't trust the file name sent by the client. Use
// Path.GetRandomFileName to generate a safe random
// file name. _targetFilePath receives a value
// from configuration (the appsettings.json file in
// the sample app).
var trustedFileName = Path.GetRandomFileName();
var filePath = Path.Combine(_targetFilePath, trustedFileName);
if (System.IO.File.Exists(filePath))
{
return;
}
System.IO.File.WriteAllBytes(filePath, model.File);
}
public class ProfileViewModel
{
public byte[] File { get; set; }
public string FileName { get; set; }
}
Sampel pengikat model kustom
Di bagian ini, kita akan menerapkan pengikat model kustom yang:
- Mengonversi data permintaan masuk menjadi argumen kunci yang ditik dengan kuat.
- Menggunakan Entity Framework Core untuk mengambil entitas terkait.
- Meneruskan entitas terkait sebagai argumen ke metode tindakan.
Sampel berikut menggunakan ModelBinder atribut pada Author model:
using CustomModelBindingSample.Binders;
using Microsoft.AspNetCore.Mvc;
namespace CustomModelBindingSample.Data
{
[ModelBinder(BinderType = typeof(AuthorEntityBinder))]
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
public string GitHub { get; set; }
public string Twitter { get; set; }
public string BlogUrl { get; set; }
}
}
Dalam kode sebelumnya, ModelBinder atribut menentukan jenis IModelBinder yang harus digunakan untuk mengikat Author parameter tindakan.
Kelas berikut AuthorEntityBinder mengikat Author parameter dengan mengambil entitas dari sumber data menggunakan Entity Framework Core dan authorId:
public class AuthorEntityBinder : IModelBinder
{
private readonly AuthorContext _context;
public AuthorEntityBinder(AuthorContext context)
{
_context = context;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var modelName = bindingContext.ModelName;
// Try to fetch the value of the argument by name
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
var value = valueProviderResult.FirstValue;
// Check if the argument value is null or empty
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}
if (!int.TryParse(value, out var id))
{
// Non-integer arguments result in model state errors
bindingContext.ModelState.TryAddModelError(
modelName, "Author Id must be an integer.");
return Task.CompletedTask;
}
// Model will be null if not found, including for
// out of range id values (0, -3, etc.)
var model = _context.Authors.Find(id);
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Note
Kelas sebelumnya dimaksudkan untuk mengilustrasikan pengikat model kustom AuthorEntityBinder. Kelas tidak dimaksudkan untuk mengilustrasikan praktik terbaik untuk skenario pencarian. Untuk pencarian, ikat authorId dan kueri database dalam metode tindakan. Pendekatan ini memisahkan kegagalan pengikatan model dari NotFound kasus.
Kode berikut menunjukkan cara menggunakan AuthorEntityBinder dalam metode tindakan:
[HttpGet("get/{author}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Atribut ModelBinder dapat digunakan untuk menerapkan AuthorEntityBinder parameter ke yang tidak menggunakan konvensi default:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Dalam contoh ini, karena nama argumen bukan default authorId, itu ditentukan pada parameter menggunakan ModelBinder atribut . Baik kontroler maupun metode aksi disederhanakan jika dibandingkan dengan mencari entitas dalam metode aksi. Logika untuk mengambil penulis menggunakan Entity Framework Core dipindahkan ke pengikat model. Ini dapat menjadi penyederhanaan yang signifikan ketika Anda memiliki beberapa metode yang terhubung dengan model Author.
Anda dapat menerapkan ModelBinder atribut ke properti model individual (seperti pada viewmodel) atau parameter metode tindakan untuk menentukan pengikat model atau nama model tertentu hanya untuk jenis atau tindakan tersebut.
Menerapkan ModelBinderProvider
Alih-alih menerapkan atribut, Anda dapat menerapkan IModelBinderProvider. Ini adalah bagaimana pengikat kerangka kerja bawaan diimplementasikan. Saat Anda menentukan jenis binder yang dioperasikan, Anda menentukan jenis argumen yang dihasilkannya, bukan input yang diterima binder Anda. Penyedia pengikat berikut bekerja dengan AuthorEntityBinder. Saat ditambahkan ke kumpulan penyedia MVC, Anda tidak perlu menggunakan atribut ModelBinder pada parameter bertipe Author atau Author.
using CustomModelBindingSample.Data;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using System;
namespace CustomModelBindingSample.Binders
{
public class AuthorEntityBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(Author))
{
return new BinderTypeModelBinder(typeof(AuthorEntityBinder));
}
return null;
}
}
}
Catatan: Kode sebelumnya mengembalikan
BinderTypeModelBinder.BinderTypeModelBinderbertindak sebagai pabrik untuk pengikat model dan menyediakan injeksi dependensi (DI).AuthorEntityBindermengharuskan DI untuk mengakses EF Core. GunakanBinderTypeModelBinderjika pengikat model Anda memerlukan layanan dari DI.
Untuk menggunakan penyedia pengikat model kustom, tambahkan di ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AuthorContext>(options => options.UseInMemoryDatabase("Authors"));
services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new AuthorEntityBinderProvider());
});
}
Saat mengevaluasi pengikat model, sekumpulan penyedia diperiksa secara berurutan. Penyedia pertama yang mengembalikan pengikat yang cocok dengan model input digunakan. Menambahkan penyedia Anda ke akhir koleksi dapat mengakibatkan pengikat model bawaan dipanggil sebelum pengikat kustom Anda memiliki kesempatan. Dalam contoh ini, penyedia kustom ditambahkan ke awal koleksi untuk memastikannya selalu digunakan untuk argumen tindakan Author.
Pengikatan model polimorfik
Pengikatan ke berbagai model jenis turunan dikenal sebagai pengikatan model polimorfik. Pengikatan model kustom polimorfik diperlukan ketika nilai permintaan harus terikat ke jenis model turunan tertentu. Pengikatan model polimorfik:
- Tidak khas untuk REST API yang dirancang untuk beroperasi dengan semua bahasa.
- Menyulitkan alasan tentang model terikat.
Namun, jika aplikasi memerlukan pengikatan model polimorfik, implementasi mungkin terlihat seperti kode berikut:
public abstract class Device
{
public string Kind { get; set; }
}
public class Laptop : Device
{
public string CPUIndex { get; set; }
}
public class SmartPhone : Device
{
public string ScreenSize { get; set; }
}
public class DeviceModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType != typeof(Device))
{
return null;
}
var subclasses = new[] { typeof(Laptop), typeof(SmartPhone), };
var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();
foreach (var type in subclasses)
{
var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
}
return new DeviceModelBinder(binders);
}
}
public class DeviceModelBinder : IModelBinder
{
private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;
public DeviceModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
{
this.binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Device.Kind));
var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (modelTypeValue == "Laptop")
{
(modelMetadata, modelBinder) = binders[typeof(Laptop)];
}
else if (modelTypeValue == "SmartPhone")
{
(modelMetadata, modelBinder) = binders[typeof(SmartPhone)];
}
else
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
bindingContext.ActionContext,
bindingContext.ValueProvider,
modelMetadata,
bindingInfo: null,
bindingContext.ModelName);
await modelBinder.BindModelAsync(newBindingContext);
bindingContext.Result = newBindingContext.Result;
if (newBindingContext.Result.IsModelSet)
{
// Setting the ValidationState ensures properties on derived types are correctly
bindingContext.ValidationState[newBindingContext.Result.Model] = new ValidationStateEntry
{
Metadata = modelMetadata,
};
}
}
}
Rekomendasi dan praktik terbaik
Pengikat model khusus
- Tidak boleh mencoba mengatur kode status atau mengembalikan hasil (misalnya, 404 Tidak Ditemukan). Jika pengikatan model gagal, filter tindakan atau logika dalam metode tindakan itu sendiri harus menangani kegagalan.
- Paling berguna untuk menghilangkan kode berulang dan masalah lintas pemotongan dari metode tindakan.
- Biasanya tidak boleh digunakan untuk mengonversi string menjadi jenis kustom, TypeConverter biasanya merupakan opsi yang lebih baik.
Oleh Steve Smith
Pengikatan model memungkinkan aksi kontrol bekerja langsung dengan tipe model (diberikan sebagai argumen metode), daripada permintaan HTTP. Pemetaan antara data permintaan masuk dan model aplikasi ditangani oleh pengikat model. Para pengembang dapat memperluas fungsionalitas pengikatan model yang sudah ada dengan menerapkan pengikat model kustom (meskipun pada umumnya, Anda tidak perlu membuat penyedia Anda sendiri).
Melihat atau mengunduh kode sampel (cara mengunduh)
Keterbatasan pengikat model bawaan
Pengikat model default mendukung sebagian besar jenis data .NET Core umum dan harus memenuhi kebutuhan sebagian besar pengembang. Mereka berharap untuk mengikat input berbasis teks dari permintaan langsung ke jenis model. Anda mungkin perlu mengubah input sebelum mengikatnya. Misalnya, ketika Anda memiliki kunci yang dapat digunakan untuk mencari data model. Anda dapat menggunakan pengikat model kustom untuk mengambil data berdasarkan kunci.
Tinjauan pengikatan model
Pengikatan model menggunakan definisi tertentu untuk jenis yang dioperasikannya.
Jenis sederhana dikonversi dari satu string dalam input.
Jenis kompleks dikonversi dari beberapa nilai input. Kerangka kerja menentukan perbedaan berdasarkan keberadaan TypeConverter. Sebaiknya Anda membuat pengonversi jenis jika Anda memiliki pemetaan sederhana string->SomeType yang tidak memerlukan sumber daya eksternal.
Sebelum membuat pengikat model kustom Anda sendiri, ada baiknya meninjau bagaimana pengikat model yang ada diterapkan. Pertimbangkan ByteArrayModelBinder yang dapat digunakan untuk mengonversi string yang dikodekan dalam base64 menjadi array byte. Array byte sering disimpan sebagai file atau bidang BLOB database.
Bekerja dengan ByteArrayModelBinder
String yang dikodekan Base64 dapat digunakan untuk mewakili data biner. Misalnya, gambar dapat dikodekan sebagai string. Sampel menyertakan gambar sebagai string yang dikodekan base64 di Base64String.txt.
ASP.NET Core MVC dapat mengambil string yang dikodekan base64 dan menggunakan ByteArrayModelBinder untuk mengonversinya menjadi array byte. Argumen ByteArrayModelBinderProvider memetakan byte[] ke ByteArrayModelBinder:
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(byte[]))
{
return new ByteArrayModelBinder();
}
return null;
}
Saat membuat binder model kustom Anda sendiri, Anda bisa menerapkan jenis IModelBinderProvider Anda sendiri, atau menggunakan ModelBinderAttribute.
Contoh berikut menunjukkan cara menggunakan ByteArrayModelBinder untuk mengonversi string yang dikodekan base64 menjadi byte[] dan menyimpan hasilnya ke file:
[HttpPost]
public void Post([FromForm] byte[] file, string filename)
{
// Don't trust the file name sent by the client. Use
// Path.GetRandomFileName to generate a safe random
// file name. _targetFilePath receives a value
// from configuration (the appsettings.json file in
// the sample app).
var trustedFileName = Path.GetRandomFileName();
var filePath = Path.Combine(_targetFilePath, trustedFileName);
if (System.IO.File.Exists(filePath))
{
return;
}
System.IO.File.WriteAllBytes(filePath, file);
}
Anda dapat MEMPOSTING string yang dikodekan base64 ke metode api sebelumnya menggunakan alat seperti curl.
Selama pengikat dapat mengikat data permintaan ke properti atau argumen bernama dengan tepat, pengikatan model akan berhasil. Contoh berikut menunjukkan cara menggunakan ByteArrayModelBinder dengan model tampilan:
[HttpPost("Profile")]
public void SaveProfile([FromForm] ProfileViewModel model)
{
// Don't trust the file name sent by the client. Use
// Path.GetRandomFileName to generate a safe random
// file name. _targetFilePath receives a value
// from configuration (the appsettings.json file in
// the sample app).
var trustedFileName = Path.GetRandomFileName();
var filePath = Path.Combine(_targetFilePath, trustedFileName);
if (System.IO.File.Exists(filePath))
{
return;
}
System.IO.File.WriteAllBytes(filePath, model.File);
}
public class ProfileViewModel
{
public byte[] File { get; set; }
public string FileName { get; set; }
}
Sampel pengikat model kustom
Di bagian ini, kita akan menerapkan pengikat model kustom yang:
- Mengonversi data permintaan masuk menjadi argumen kunci yang ditik dengan kuat.
- Menggunakan Entity Framework Core untuk mengambil entitas terkait.
- Meneruskan entitas terkait sebagai argumen ke metode tindakan.
Sampel berikut menggunakan ModelBinder atribut pada Author model:
using CustomModelBindingSample.Binders;
using Microsoft.AspNetCore.Mvc;
namespace CustomModelBindingSample.Data
{
[ModelBinder(BinderType = typeof(AuthorEntityBinder))]
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
public string GitHub { get; set; }
public string Twitter { get; set; }
public string BlogUrl { get; set; }
}
}
Dalam kode sebelumnya, ModelBinder atribut menentukan jenis IModelBinder yang harus digunakan untuk mengikat Author parameter tindakan.
Kelas berikut AuthorEntityBinder mengikat Author parameter dengan mengambil entitas dari sumber data menggunakan Entity Framework Core dan authorId:
public class AuthorEntityBinder : IModelBinder
{
private readonly AppDbContext _db;
public AuthorEntityBinder(AppDbContext db)
{
_db = db;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var modelName = bindingContext.ModelName;
// Try to fetch the value of the argument by name
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
var value = valueProviderResult.FirstValue;
// Check if the argument value is null or empty
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}
if (!int.TryParse(value, out var id))
{
// Non-integer arguments result in model state errors
bindingContext.ModelState.TryAddModelError(
modelName, "Author Id must be an integer.");
return Task.CompletedTask;
}
// Model will be null if not found, including for
// out of range id values (0, -3, etc.)
var model = _db.Authors.Find(id);
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Note
Kelas sebelumnya dimaksudkan untuk mengilustrasikan pengikat model kustom AuthorEntityBinder. Kelas tidak dimaksudkan untuk mengilustrasikan praktik terbaik untuk skenario pencarian. Untuk pencarian, ikat authorId dan kueri database dalam metode tindakan. Pendekatan ini memisahkan kegagalan pengikatan model dari NotFound kasus.
Kode berikut menunjukkan cara menggunakan AuthorEntityBinder dalam metode tindakan:
[HttpGet("get/{author}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Atribut ModelBinder dapat digunakan untuk menerapkan AuthorEntityBinder parameter ke yang tidak menggunakan konvensi default:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Dalam contoh ini, karena nama argumen bukan default authorId, itu ditentukan pada parameter menggunakan ModelBinder atribut . Baik kontroler maupun metode aksi disederhanakan jika dibandingkan dengan mencari entitas dalam metode aksi. Logika untuk mengambil penulis menggunakan Entity Framework Core dipindahkan ke pengikat model. Ini dapat menjadi penyederhanaan yang signifikan ketika Anda memiliki beberapa metode yang terhubung dengan model Author.
Anda dapat menerapkan ModelBinder atribut ke properti model individual (seperti pada viewmodel) atau parameter metode tindakan untuk menentukan pengikat model atau nama model tertentu hanya untuk jenis atau tindakan tersebut.
Menerapkan ModelBinderProvider
Alih-alih menerapkan atribut, Anda dapat menerapkan IModelBinderProvider. Ini adalah bagaimana pengikat kerangka kerja bawaan diimplementasikan. Saat Anda menentukan jenis binder yang dioperasikan, Anda menentukan jenis argumen yang dihasilkannya, bukan input yang diterima binder Anda. Penyedia pengikat berikut bekerja dengan AuthorEntityBinder. Saat ditambahkan ke kumpulan penyedia MVC, Anda tidak perlu menggunakan atribut ModelBinder pada parameter bertipe Author atau Author.
using CustomModelBindingSample.Data;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using System;
namespace CustomModelBindingSample.Binders
{
public class AuthorEntityBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(Author))
{
return new BinderTypeModelBinder(typeof(AuthorEntityBinder));
}
return null;
}
}
}
Catatan: Kode sebelumnya mengembalikan
BinderTypeModelBinder.BinderTypeModelBinderbertindak sebagai pabrik untuk pengikat model dan menyediakan injeksi dependensi (DI).AuthorEntityBindermengharuskan DI untuk mengakses EF Core. GunakanBinderTypeModelBinderjika pengikat model Anda memerlukan layanan dari DI.
Untuk menggunakan penyedia pengikat model kustom, tambahkan di ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase("App"));
services.AddMvc(options =>
{
// add custom binder to beginning of collection
options.ModelBinderProviders.Insert(0, new AuthorEntityBinderProvider());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Saat mengevaluasi pengikat model, sekumpulan penyedia diperiksa secara berurutan. Penyedia pertama yang mengembalikan biner akan digunakan. Menambahkan penyedia Anda ke akhir koleksi dapat mengakibatkan pengikat model bawaan dipanggil sebelum pengikat kustom Anda memiliki kesempatan. Dalam contoh ini, penyedia kustom ditambahkan ke awal koleksi untuk memastikannya digunakan untuk Author argumen tindakan.
Pengikatan model polimorfik
Pengikatan ke berbagai model jenis turunan dikenal sebagai pengikatan model polimorfik. Pengikatan model kustom polimorfik diperlukan ketika nilai permintaan harus terikat ke jenis model turunan tertentu. Pengikatan model polimorfik:
- Tidak khas untuk REST API yang dirancang untuk beroperasi dengan semua bahasa.
- Menyulitkan alasan tentang model terikat.
Namun, jika aplikasi memerlukan pengikatan model polimorfik, implementasi mungkin terlihat seperti kode berikut:
public abstract class Device
{
public string Kind { get; set; }
}
public class Laptop : Device
{
public string CPUIndex { get; set; }
}
public class SmartPhone : Device
{
public string ScreenSize { get; set; }
}
public class DeviceModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType != typeof(Device))
{
return null;
}
var subclasses = new[] { typeof(Laptop), typeof(SmartPhone), };
var binders = new Dictionary<Type, (ModelMetadata, IModelBinder)>();
foreach (var type in subclasses)
{
var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
}
return new DeviceModelBinder(binders);
}
}
public class DeviceModelBinder : IModelBinder
{
private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;
public DeviceModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
{
this.binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(Device.Kind));
var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (modelTypeValue == "Laptop")
{
(modelMetadata, modelBinder) = binders[typeof(Laptop)];
}
else if (modelTypeValue == "SmartPhone")
{
(modelMetadata, modelBinder) = binders[typeof(SmartPhone)];
}
else
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
bindingContext.ActionContext,
bindingContext.ValueProvider,
modelMetadata,
bindingInfo: null,
bindingContext.ModelName);
await modelBinder.BindModelAsync(newBindingContext);
bindingContext.Result = newBindingContext.Result;
if (newBindingContext.Result.IsModelSet)
{
// Setting the ValidationState ensures properties on derived types are correctly
bindingContext.ValidationState[newBindingContext.Result.Model] = new ValidationStateEntry
{
Metadata = modelMetadata,
};
}
}
}
Rekomendasi dan praktik terbaik
Pengikat model khusus
- Tidak boleh mencoba mengatur kode status atau mengembalikan hasil (misalnya, 404 Tidak Ditemukan). Jika pengikatan model gagal, filter tindakan atau logika dalam metode tindakan itu sendiri harus menangani kegagalan.
- Paling berguna untuk menghilangkan kode berulang dan masalah lintas pemotongan dari metode tindakan.
- Biasanya tidak boleh digunakan untuk mengonversi string menjadi jenis kustom, TypeConverter biasanya merupakan opsi yang lebih baik.
ASP.NET Core