ASP.NET Core'da Özel Model Bağlama
Tarafından Kirk Larkin
Model bağlama, denetleyici eylemlerinin HTTP istekleri yerine model türleriyle (yöntem bağımsız değişkenleri olarak geçirilir) doğrudan çalışmasını sağlar. Gelen istek verileri ve uygulama modelleri arasındaki eşleme, model bağlayıcıları tarafından işlenir. Geliştiriciler, özel model bağlayıcıları uygulayarak yerleşik model bağlama işlevselliğini genişletebilir (ancak genellikle kendi sağlayıcınızı yazmanız gerekmez).
Örnek kodu görüntüleme veya indirme (indirme)
Varsayılan model bağlayıcı sınırlamaları
Varsayılan model bağlayıcıları yaygın .NET Core veri türlerinin çoğunu destekler ve çoğu geliştiricinin gereksinimlerini karşılamalıdır. İstekten gelen metin tabanlı girişi doğrudan model türlerine bağlamayı beklerler. Bağlamadan önce girişi dönüştürmeniz gerekebilir. Örneğin, model verilerini aramak için kullanılabilecek bir anahtarınız olduğunda. Anahtara göre veri getirmek için özel model bağlayıcısı kullanabilirsiniz.
Model bağlama basit ve karmaşık türler
Model bağlama, üzerinde çalıştığı türler için belirli tanımları kullanır. Basit bir tür, veya TryParse
yöntemi kullanılarak TypeConverter tek bir dizeden dönüştürülür. Karmaşık bir tür birden çok giriş değerinden dönüştürülür. Çerçeve, veya TypeConverter
TryParse
varlığını temel alarak farkı belirler. Bir tür dönüştürücüsü oluşturmanızı veya dış kaynaklar veya birden çok giriş gerektirmeyen bir string
SomeType
dönüştürme için kullanmanızı TryParse
öneririz.
Model bağlayıcısının dizelerden dönüştürebileceği türlerin listesi için bkz . Basit türler.
Kendi özel model bağlayıcınızı oluşturmadan önce, mevcut model bağlayıcılarının nasıl uygulandığını gözden geçirmeye değer. ByteArrayModelBinder Base64 ile kodlanmış dizeleri bayt dizilerine dönüştürmek için hangilerinin kullanılabileceğini düşünün. Bayt dizileri genellikle dosya veya veritabanı BLOB alanları olarak depolanır.
ByteArrayModelBinder ile çalışma
İkili verileri temsil etmek için Base64 ile kodlanmış dizeler kullanılabilir. Örneğin, bir görüntü dize olarak kodlanabilir. Örnek, Base64String.txt'da base64 kodlu dize olarak bir görüntü içerir.
ASP.NET Core MVC, base64 ile kodlanmış bir dize alabilir ve bunu bayt dizisine dönüştürmek için bir ByteArrayModelBinder
kullanabilir. Bağımsız ByteArrayModelBinderProvider değişkenleri ile ByteArrayModelBinder
eşlerbyte[]
:
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;
}
Kendi özel model bağlayıcınızı oluştururken, kendi IModelBinderProvider
türünüzü uygulayabilir veya kullanabilirsiniz ModelBinderAttribute.
Aşağıdaki örnekte base64 ile kodlanmış bir dizeyi bir dosyasına dönüştürmek ve sonucu bir dosyaya byte[]
kaydetmek için nasıl kullanılacağı ByteArrayModelBinder
gösterilmektedir:
[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);
}
Kod açıklamalarının İngilizce dışındaki dillere çevirisini görmek isterseniz, bunu bu GitHub tartışma konusunda bize bildirin.
Curl gibi bir araç kullanarak base64 ile kodlanmış bir dizeyi önceki api yöntemine GÖNDERebilirsiniz.
Bağlayıcı, istek verilerini uygun şekilde adlandırılmış özelliklere veya bağımsız değişkenlere bağlayabildiği sürece model bağlama başarılı olur. Aşağıdaki örnekte, görünüm modeliyle nasıl kullanılacağı ByteArrayModelBinder
gösterilmektedir:
[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; }
}
Özel model bağlayıcı örneği
Bu bölümde şunları sağlayan özel bir model bağlayıcısı uygulayacağız:
- Gelen istek verilerini kesin olarak belirlenmiş anahtar bağımsız değişkenlerine dönüştürür.
- İlişkili varlığı getirmek için Entity Framework Core kullanır.
- İlişkili varlığı eylem yöntemine bağımsız değişken olarak geçirir.
Aşağıdaki örnek modelde özniteliğini ModelBinder
Author
kullanır:
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; }
}
}
Yukarıdaki kodda ModelBinder
özniteliği, eylem parametrelerini bağlamak Author
için kullanılacak türü IModelBinder
belirtir.
Aşağıdaki AuthorEntityBinder
sınıf, Entity Framework Core ve kullanarak Author
bir veri kaynağından varlık getirerek bir authorId
parametre bağlar:
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;
}
}
Not
Önceki AuthorEntityBinder
sınıf, özel model bağlayıcısını göstermek için tasarlanmıştır. sınıfı, arama senaryosu için en iyi yöntemleri göstermek üzere tasarlanmamıştır. Arama için öğesini bağlayın authorId
ve veritabanını bir eylem yönteminde sorgulayın. Bu yaklaşım model bağlama hatalarını durumlardan NotFound
ayırır.
Aşağıdaki kod, bir eylem yönteminde öğesinin AuthorEntityBinder
nasıl kullanılacağını gösterir:
[HttpGet("get/{author}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
özniteliği, ModelBinder
varsayılan kuralları kullanmayan parametrelere uygulamak AuthorEntityBinder
için kullanılabilir:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Bu örnekte, bağımsız değişkenin adı varsayılan authorId
olmadığından özniteliği kullanılarak ModelBinder
parametresinde belirtilir. Eylem yönteminde varlığı aramaya kıyasla hem denetleyici hem de eylem yöntemi basitleştirilmiştir. Entity Framework Core kullanarak yazarı getirme mantığı model bağlayıcısına taşınır. Modele bağlanan Author
birkaç yönteminiz olduğunda bu önemli bir basitleştirme olabilir.
Özniteliği tek tek model özelliklerine (görünüm modelinde olduğu gibi) veya eylem yöntemi parametrelerine uygulayarak ModelBinder
yalnızca bu tür veya eylem için belirli bir model bağlayıcısı veya model adı belirtebilirsiniz.
ModelBinderProvider Uygulama
Özniteliği uygulamak yerine uygulayabilirsiniz IModelBinderProvider
. Yerleşik çerçeve bağlayıcıları bu şekilde uygulanır. Bağlayıcınızın üzerinde çalıştığı türü belirttiğinizde, ciltçinizin kabul ettiğiniz girişi değil, ürettiği bağımsız değişkenin türünü belirtirsiniz. Aşağıdaki bağlayıcı sağlayıcısı ile AuthorEntityBinder
çalışır. MVC'nin sağlayıcı koleksiyonuna eklendiğinde veya -typed parametrelerinde Author
Author
özniteliğini ModelBinder
kullanmanız gerekmez.
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;
}
}
}
Not: Yukarıdaki kod bir
BinderTypeModelBinder
döndürür.BinderTypeModelBinder
model bağlayıcıları için fabrika görevi görür ve bağımlılık ekleme (DI) sağlar. içinAuthorEntityBinder
DI'nin erişmesi EF Coregerekir. Model bağlayıcınız DI hizmetlerini gerektiriyorsa kullanınBinderTypeModelBinder
.
Özel model bağlayıcı sağlayıcısı kullanmak için içine ekleyin ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AuthorContext>(options => options.UseInMemoryDatabase("Authors"));
services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new AuthorEntityBinderProvider());
});
}
Model bağlayıcıları değerlendirilirken sağlayıcı koleksiyonu sırayla inceleniyor. Giriş modeliyle eşleşen bir bağlayıcı döndüren ilk sağlayıcı kullanılır. Böylece sağlayıcınızı koleksiyonun sonuna eklemek, özel bağlayıcınızın bir şansı olmadan önce yerleşik bir model bağlayıcısının çağrılmasıyla sonuçlanabilir. Bu örnekte özel sağlayıcı, her zaman eylem bağımsız değişkenleri için kullanıldığından emin olmak için Author
koleksiyonun başına eklenir.
Polimorfik model bağlama
Türetilmiş türlerin farklı modellerine bağlama, polimorfik model bağlama olarak bilinir. İstek değerinin belirli türetilmiş model türüne bağlanması gerektiğinde çok biçimli özel model bağlaması gerekir. Polimorfik model bağlama:
- Tüm dillerle birlikte çalışabilecek şekilde tasarlanmış bir REST API için tipik değildir.
- İlişkili modeller hakkında mantık yürütmeyi zorlaştırır.
Ancak, bir uygulama çok biçimli model bağlaması gerektiriyorsa, bir uygulama aşağıdaki kod gibi görünebilir:
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,
};
}
}
}
Öneriler ve en iyi yöntemler
Özel model bağlayıcıları:
- Durum kodlarını ayarlamaya veya sonuçları döndürmeye çalışmamalıdır (örneğin, 404 Bulunamadı). Model bağlama başarısız olursa, eylem yönteminin içindeki bir eylem filtresi veya mantık hatayı işlemelidir.
- Tekrarlanan kodu ve eylem yöntemlerinden çapraz kesme endişelerini ortadan kaldırmak için en kullanışlı olanlardır.
- Genellikle bir dizeyi özel bir türe dönüştürmek için kullanılmamalıdır; TypeConverter genellikle daha iyi bir seçenektir.
Tarafından Steve Smith
Model bağlama, denetleyici eylemlerinin HTTP istekleri yerine model türleriyle (yöntem bağımsız değişkenleri olarak geçirilir) doğrudan çalışmasını sağlar. Gelen istek verileri ve uygulama modelleri arasındaki eşleme, model bağlayıcıları tarafından işlenir. Geliştiriciler, özel model bağlayıcıları uygulayarak yerleşik model bağlama işlevselliğini genişletebilir (ancak genellikle kendi sağlayıcınızı yazmanız gerekmez).
Örnek kodu görüntüleme veya indirme (indirme)
Varsayılan model bağlayıcı sınırlamaları
Varsayılan model bağlayıcıları yaygın .NET Core veri türlerinin çoğunu destekler ve çoğu geliştiricinin gereksinimlerini karşılamalıdır. İstekten gelen metin tabanlı girişi doğrudan model türlerine bağlamayı beklerler. Bağlamadan önce girişi dönüştürmeniz gerekebilir. Örneğin, model verilerini aramak için kullanılabilecek bir anahtarınız olduğunda. Anahtara göre veri getirmek için özel model bağlayıcısı kullanabilirsiniz.
Model bağlama gözden geçirmesi
Model bağlama, üzerinde çalıştığı türler için belirli tanımları kullanır. Basit bir tür , girişteki tek bir dizeden dönüştürülür. Karmaşık bir tür birden çok giriş değerinden dönüştürülür. Çerçeve, bir TypeConverter
varlığını temel alarak farkı belirler. Dış kaynak gerektirmeyen basit string
>SomeType
bir eşlemeniz varsa bir tür dönüştürücü oluşturmanızı öneririz.
Kendi özel model bağlayıcınızı oluşturmadan önce, mevcut model bağlayıcılarının nasıl uygulandığını gözden geçirmeye değer. ByteArrayModelBinder Base64 ile kodlanmış dizeleri bayt dizilerine dönüştürmek için hangilerinin kullanılabileceğini düşünün. Bayt dizileri genellikle dosya veya veritabanı BLOB alanları olarak depolanır.
ByteArrayModelBinder ile çalışma
İkili verileri temsil etmek için Base64 ile kodlanmış dizeler kullanılabilir. Örneğin, bir görüntü dize olarak kodlanabilir. Örnek, Base64String.txt'da base64 kodlu dize olarak bir görüntü içerir.
ASP.NET Core MVC, base64 ile kodlanmış bir dize alabilir ve bunu bayt dizisine dönüştürmek için bir ByteArrayModelBinder
kullanabilir. Bağımsız ByteArrayModelBinderProvider değişkenleri ile ByteArrayModelBinder
eşlerbyte[]
:
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(byte[]))
{
return new ByteArrayModelBinder();
}
return null;
}
Kendi özel model bağlayıcınızı oluştururken, kendi IModelBinderProvider
türünüzü uygulayabilir veya kullanabilirsiniz ModelBinderAttribute.
Aşağıdaki örnekte base64 ile kodlanmış bir dizeyi bir dosyasına dönüştürmek ve sonucu bir dosyaya byte[]
kaydetmek için nasıl kullanılacağı ByteArrayModelBinder
gösterilmektedir:
[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);
}
Curl gibi bir araç kullanarak base64 ile kodlanmış bir dizeyi önceki api yöntemine GÖNDERebilirsiniz.
Bağlayıcı, istek verilerini uygun şekilde adlandırılmış özelliklere veya bağımsız değişkenlere bağlayabildiği sürece model bağlama başarılı olur. Aşağıdaki örnekte, görünüm modeliyle nasıl kullanılacağı ByteArrayModelBinder
gösterilmektedir:
[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; }
}
Özel model bağlayıcı örneği
Bu bölümde şunları sağlayan özel bir model bağlayıcısı uygulayacağız:
- Gelen istek verilerini kesin olarak belirlenmiş anahtar bağımsız değişkenlerine dönüştürür.
- İlişkili varlığı getirmek için Entity Framework Core kullanır.
- İlişkili varlığı eylem yöntemine bağımsız değişken olarak geçirir.
Aşağıdaki örnek modelde özniteliğini ModelBinder
Author
kullanır:
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; }
}
}
Yukarıdaki kodda ModelBinder
özniteliği, eylem parametrelerini bağlamak Author
için kullanılacak türü IModelBinder
belirtir.
Aşağıdaki AuthorEntityBinder
sınıf, Entity Framework Core ve kullanarak Author
bir veri kaynağından varlık getirerek bir authorId
parametre bağlar:
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;
}
}
Not
Önceki AuthorEntityBinder
sınıf, özel model bağlayıcısını göstermek için tasarlanmıştır. sınıfı, arama senaryosu için en iyi yöntemleri göstermek üzere tasarlanmamıştır. Arama için öğesini bağlayın authorId
ve veritabanını bir eylem yönteminde sorgulayın. Bu yaklaşım model bağlama hatalarını durumlardan NotFound
ayırır.
Aşağıdaki kod, bir eylem yönteminde öğesinin AuthorEntityBinder
nasıl kullanılacağını gösterir:
[HttpGet("get/{author}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
özniteliği, ModelBinder
varsayılan kuralları kullanmayan parametrelere uygulamak AuthorEntityBinder
için kullanılabilir:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Bu örnekte, bağımsız değişkenin adı varsayılan authorId
olmadığından özniteliği kullanılarak ModelBinder
parametresinde belirtilir. Eylem yönteminde varlığı aramaya kıyasla hem denetleyici hem de eylem yöntemi basitleştirilmiştir. Entity Framework Core kullanarak yazarı getirme mantığı model bağlayıcısına taşınır. Modele bağlanan Author
birkaç yönteminiz olduğunda bu önemli bir basitleştirme olabilir.
Özniteliği tek tek model özelliklerine (görünüm modelinde olduğu gibi) veya eylem yöntemi parametrelerine uygulayarak ModelBinder
yalnızca bu tür veya eylem için belirli bir model bağlayıcısı veya model adı belirtebilirsiniz.
ModelBinderProvider Uygulama
Özniteliği uygulamak yerine uygulayabilirsiniz IModelBinderProvider
. Yerleşik çerçeve bağlayıcıları bu şekilde uygulanır. Bağlayıcınızın üzerinde çalıştığı türü belirttiğinizde, ciltçinizin kabul ettiğiniz girişi değil, ürettiği bağımsız değişkenin türünü belirtirsiniz. Aşağıdaki bağlayıcı sağlayıcısı ile AuthorEntityBinder
çalışır. MVC'nin sağlayıcı koleksiyonuna eklendiğinde veya -typed parametrelerinde Author
Author
özniteliğini ModelBinder
kullanmanız gerekmez.
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;
}
}
}
Not: Yukarıdaki kod bir
BinderTypeModelBinder
döndürür.BinderTypeModelBinder
model bağlayıcıları için fabrika görevi görür ve bağımlılık ekleme (DI) sağlar. içinAuthorEntityBinder
DI'nin erişmesi EF Coregerekir. Model bağlayıcınız DI hizmetlerini gerektiriyorsa kullanınBinderTypeModelBinder
.
Özel model bağlayıcı sağlayıcısı kullanmak için içine ekleyin 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);
}
Model bağlayıcıları değerlendirilirken sağlayıcı koleksiyonu sırayla inceleniyor. Bağlayıcı döndüren ilk sağlayıcı kullanılır. Sağlayıcınızın koleksiyonun sonuna eklenmesi, özel bağlayıcınızın bir şansı olmadan önce yerleşik bir model bağlayıcısının çağrılmasıyla sonuçlanabilir. Bu örnekte özel sağlayıcı, eylem bağımsız değişkenleri için kullanıldığından emin olmak için Author
koleksiyonun başına eklenir.
Polimorfik model bağlama
Türetilmiş türlerin farklı modellerine bağlama, polimorfik model bağlama olarak bilinir. İstek değerinin belirli türetilmiş model türüne bağlanması gerektiğinde çok biçimli özel model bağlaması gerekir. Polimorfik model bağlama:
- Tüm dillerle birlikte çalışabilecek şekilde tasarlanmış bir REST API için tipik değildir.
- İlişkili modeller hakkında mantık yürütmeyi zorlaştırır.
Ancak, bir uygulama çok biçimli model bağlaması gerektiriyorsa, bir uygulama aşağıdaki kod gibi görünebilir:
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,
};
}
}
}
Öneriler ve en iyi yöntemler
Özel model bağlayıcıları:
- Durum kodlarını ayarlamaya veya sonuçları döndürmeye çalışmamalıdır (örneğin, 404 Bulunamadı). Model bağlama başarısız olursa, eylem yönteminin içindeki bir eylem filtresi veya mantık hatayı işlemelidir.
- Tekrarlanan kodu ve eylem yöntemlerinden çapraz kesme endişelerini ortadan kaldırmak için en kullanışlı olanlardır.
- Genellikle bir dizeyi özel bir türe dönüştürmek için kullanılmamalıdır; TypeConverter genellikle daha iyi bir seçenektir.
ASP.NET Core