Vlastní vazby modelu v ASP.NET Core
Od Steve Smithe a Stevena Přilkina
Vazby modelu umožňují, aby akce kontroleru fungovaly přímo s typy modelů (předaly se jako argumenty metody) a ne s požadavky HTTP. Mapování mezi příchozími daty požadavků a aplikačními modely jsou zpracovávány važemi modelu. Vývojáři rozšiřují integrovanou funkci vazby modelu implementací vlastních važe modelů (obvykle ale nemusíte psát vlastního zprostředkovatele).
Zobrazení nebo stažení ukázkového kódu (stažení)
Výchozí omezení važe modelu
Výchozí vačky modelu podporují většinu běžných datových typů .NET Core a měly by splňovat potřeby většiny vývojářů. Očekávají, že textové vstupy z požadavku vytvoří vazbu přímo na typy modelů. Před vytvořením vazby možná budete muset vstup transformovat. Pokud máte například klíč, který můžete použít k vyhledávání dat modelu. K načtení dat na základě klíče můžete použít vlastní vazbu modelu.
Kontrola vazby modelu
Vazba modelu používá konkrétní definice pro typy, na které pracuje. Jednoduchý typ je převeden z jednoho řetězce ve vstupu. Komplexní typ je převeden z více vstupních hodnot. Rozhraní určuje rozdíl na základě existence TypeConverter . Doporučujeme vytvořit převaděč typů, pokud máte jednoduché mapování, string -> SomeType které nevyžaduje externí prostředky.
Před vytvořením vlastního važeře modelu je vhodné si zkontrolovat, jak se implementují stávající važeři modelů. Zvažte, který lze použít k převodu řetězců s kódováním ByteArrayModelBinder base64 na bajtová pole. Pole bajtů se často ukládají jako pole objektů blob databáze nebo souborů.
Práce s modelem ByteArrayModelBinder
K reprezentaci binárních dat je možné použít řetězce s kódováním Base64. Například obrázek lze zakódovat jako řetězec. Ukázka obsahuje obrázek jako řetězec s kódováním base64 v Base64String.txt.
ASP.NET Core MVC může vzít řetězec s kódováním base64 a použít k ByteArrayModelBinder jeho převodu na pole bajtů. Argumenty ByteArrayModelBinderProvider byte[] mapuje na 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;
}
Při vytváření vlastního važéru modelu můžete implementovat vlastní IModelBinderProvider typ nebo použít ModelBinderAttribute .
Následující příklad ukazuje, jak použít k převodu řetězce s kódováním ByteArrayModelBinder base64 na byte[] a uložení výsledku do souboru:
[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);
}
Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků než angličtiny, dejte nám vědět v tomto problému diskuze na GitHubu.
Do této metody api můžete pomocí nástroje, jako je Postman,odeslat řetězec s kódováním base64:

Pokud může vázání dat požadavku navázat na vhodně pojmenované vlastnosti nebo argumenty, vazba modelu bude úspěšná. Následující příklad ukazuje použití s ByteArrayModelBinder modelem zobrazení:
[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; }
}
Ukázka vázání vlastního modelu
V této části implementujeme vlastní važeř modelů, který:
- Převede příchozí data požadavku na argumenty klíče silného typu.
- Používá Entity Framework Core k načtení přidružené entity.
- Předá přidruženou entitu jako argument metodě akce.
Následující ukázka používá ModelBinder atribut v Author modelu:
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; }
}
}
V předchozím kódu atribut určuje typ, který se má použít k vytvoření vazby ModelBinder IModelBinder parametrů Author akce.
Následující třída AuthorEntityBinder vytvoří vazbu Author parametru načtením entity ze zdroje dat pomocí Entity Framework Core a 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;
}
}
Poznámka
Předchozí třída AuthorEntityBinder je určená k ilustraci vlastního važe modelu. Třída není určená k ilustraci osvědčených postupů pro scénář vyhledávání. Pro vyhledávání vytvořte vazbu databáze a authorId dotazte se na databázi v metodě akce. Tento přístup odděluje selhání vazby modelu od NotFound případů.
Následující kód ukazuje, jak použít AuthorEntityBinder metodu v akci:
[HttpGet("get/{authorId}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Atribut ModelBinder lze použít pro AuthorEntityBinder parametry, které nevyužít výchozí konvence:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Vzhledem k tomu, že v tomto příkladu není název argumentu výchozí, je zadaný v authorId parametru pomocí ModelBinder atributu . Metoda kontroleru i akce jsou zjednodušené v porovnání se hledáním entity v metodě akce. Logika pro načtení autora pomocí Entity Framework Core se přesune do vázání modelu. To může být značné zjednodušení, pokud máte několik metod, které se vztahují k Author modelu.
Atribut můžete použít u jednotlivých vlastností modelu (například u modelu viewmodel) nebo na parametry metody akce a zadat určitý vazač modelu nebo název modelu pouze pro ModelBinder tento typ nebo akci.
Implementace ModelBinderProvider
Místo použití atributu můžete implementovat IModelBinderProvider . Tímto způsobem se implementují integrované vázání architektury. Když zadáte typ, na který váš vazač pracuje, zadáte typ argumentu, který vytvoří, nikoli vstup, který váš vazač přijímá. Následující zprostředkovatel vázání pracuje s AuthorEntityBinder . Když se přidá do kolekce zprostředkovatelů MVC, nemusíte používat atribut u parametrů ModelBinder Author nebo Author -typed.
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;
}
}
}
Poznámka: Předchozí kód vrátí
BinderTypeModelBinder.BinderTypeModelBinderfunguje jako továrna pro važeři modelů a poskytuje injektáž závislostí (DI). ProAuthorEntityBinderpřístup k datům vyžaduje EF Core. Použijte,BinderTypeModelBinderpokud váš modelový važeř vyžaduje služby od diody.
Pokud chcete použít vlastního zprostředkovatele važe modelu, přidejte ho do ConfigureServices souboru :
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AuthorContext>(options => options.UseInMemoryDatabase("Authors"));
services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new AuthorEntityBinderProvider());
});
}
Při vyhodnocování važe modelů se kolekce zprostředkovatelů zkoumá v pořadí. Použije se první zprostředkovatel, který vrátí vazbu, která odpovídá vstupnímu modelu. Přidání poskytovatele na konec kolekce proto může vést k volání integrovaného važe modelu předtím, než bude mít váš vlastní vázač šanci. V tomto příkladu se na začátek kolekce přidá vlastní zprostředkovatel, aby se zajistilo, že se vždy používá pro Author argumenty akce.
Vazba polymorfního modelu
Vazba na různé modely odvozených typů se označuje jako vazba polymorfního modelu. Vazba polymorfního vlastního modelu se vyžaduje, když musí být hodnota požadavku svázaná s konkrétním typem odvozeného modelu. Vazba polymorfního modelu:
- Není typické pro aplikaci REST API která je navržená tak, aby vzájemně spolupracovat se všemi jazyky.
- Ztěžuje úvahy o vázaných modelech.
Pokud ale aplikace vyžaduje vazbu polymorfního modelu, implementace může vypadat jako následující kód:
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,
};
}
}
}
Recommendations a osvědčené postupy
Vlastní važečky modelů:
- Neměl by se pokoušet nastavit stavové kódy nebo vracet výsledky (například 404 – Nenašlé). Pokud vazba modelu selže, měl by selhání zvládnout filtr akcí nebo logika v samotné metodě akce.
- Jsou nejužitečnější pro eliminaci opakujících se kódů a křížové obavy z metod akcí.
- Obvykle by se neměl používat k převodu řetězce na vlastní typ, obvykle TypeConverter je lepší možností.
Od Steve Smith
Vazby modelu umožňují, aby akce kontroleru fungovaly přímo s typy modelů (předaly se jako argumenty metody) a ne s požadavky HTTP. Mapování mezi příchozími daty požadavků a aplikačními modely jsou zpracovávány važemi modelu. Vývojáři rozšiřují integrovanou funkci vazby modelu implementací vlastních važe modelů (obvykle ale nemusíte psát vlastního zprostředkovatele).
Zobrazení nebo stažení ukázkového kódu (stažení)
Výchozí omezení važe modelu
Výchozí vačky modelu podporují většinu běžných datových typů .NET Core a měly by splňovat potřeby většiny vývojářů. Očekávají, že textové vstupy z požadavku vytvoří vazbu přímo na typy modelů. Před vytvořením vazby možná budete muset vstup transformovat. Pokud máte například klíč, který můžete použít k vyhledávání dat modelu. K načtení dat na základě klíče můžete použít vlastní vazbu modelu.
Kontrola vazby modelu
Vazba modelu používá konkrétní definice pro typy, na které pracuje. Jednoduchý typ je převeden z jednoho řetězce ve vstupu. Komplexní typ je převeden z více vstupních hodnot. Rozhraní určuje rozdíl na základě existence TypeConverter . Doporučujeme vytvořit převaděč typů, pokud máte jednoduché mapování, string -> SomeType které nevyžaduje externí prostředky.
Před vytvořením vlastního važeře modelu je vhodné si zkontrolovat, jak se implementují stávající važeři modelů. Zvažte, který lze použít k převodu řetězců s kódováním ByteArrayModelBinder base64 na bajtová pole. Pole bajtů se často ukládají jako pole objektů blob databáze nebo souborů.
Práce s modelem ByteArrayModelBinder
K reprezentaci binárních dat je možné použít řetězce s kódováním Base64. Například obrázek lze zakódovat jako řetězec. Ukázka obsahuje obrázek jako řetězec s kódováním Base64 v Base64String.txt.
ASP.NET Core MVC může vzít řetězec s kódováním base64 a použít k ByteArrayModelBinder jeho převodu na pole bajtů. Argumenty ByteArrayModelBinderProvider byte[] mapuje na 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;
}
Při vytváření vlastního važéru modelu můžete implementovat vlastní IModelBinderProvider typ nebo použít ModelBinderAttribute .
Následující příklad ukazuje, jak použít k převodu řetězce s kódováním ByteArrayModelBinder base64 na byte[] a uložení výsledku do souboru:
[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);
}
Do této metody api můžete pomocí nástroje, jako je Postman,odeslat řetězec s kódováním base64:

Pokud může vázání dat požadavku navázat na vhodně pojmenované vlastnosti nebo argumenty, vazba modelu bude úspěšná. Následující příklad ukazuje použití s ByteArrayModelBinder modelem zobrazení:
[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; }
}
Ukázka vázání vlastního modelu
V této části implementujeme vlastní važeř modelů, který:
- Převede příchozí data požadavku na argumenty klíče silného typu.
- Používá Entity Framework Core k načtení přidružené entity.
- Předá přidruženou entitu jako argument metodě akce.
Následující ukázka používá ModelBinder atribut v Author modelu:
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; }
}
}
V předchozím kódu atribut určuje typ, který se má použít k vytvoření vazby ModelBinder IModelBinder parametrů Author akce.
Následující třída AuthorEntityBinder vytvoří vazbu parametru načtením entity ze zdroje dat pomocí Author Entity Framework Core a 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;
}
}
Poznámka
Předchozí třída AuthorEntityBinder je určená k ilustraci vlastního važe modelu. Třída není určená k ilustraci osvědčených postupů pro scénář vyhledávání. Pro vyhledávání vytvořte vazbu databáze a authorId dotazte se na databázi v metodě akce. Tento přístup odděluje selhání vazby modelu od NotFound případů.
Následující kód ukazuje, jak použít AuthorEntityBinder metodu v akci:
[HttpGet("get/{authorId}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Atribut ModelBinder lze použít pro AuthorEntityBinder parametry, které nevyužít výchozí konvence:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Vzhledem k tomu, že v tomto příkladu není název argumentu výchozí, je zadaný v authorId parametru pomocí ModelBinder atributu . Metoda kontroleru i akce jsou zjednodušené v porovnání se hledáním entity v metodě akce. Logika pro načtení autora pomocí Entity Framework Core se přesune do važe modelu. To může být značné zjednodušení, pokud máte několik metod, které se vztahují k Author modelu.
Atribut můžete použít u jednotlivých vlastností modelu (například u modelu viewmodel) nebo na parametry metody akce a zadat určitý vazač modelu nebo název modelu pouze pro ModelBinder tento typ nebo akci.
Implementace ModelBinderProvider
Místo použití atributu můžete implementovat IModelBinderProvider . Tímto způsobem se implementují integrované vázání architektury. Když zadáte typ, na který váš vazač pracuje, zadáte typ argumentu, který vytvoří, nikoli vstup, který váš vazač přijímá. Následující zprostředkovatel vázání pracuje s AuthorEntityBinder . Když se přidá do kolekce zprostředkovatelů MVC, nemusíte používat atribut u parametrů ModelBinder Author nebo Author -typed.
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;
}
}
}
Poznámka: Předchozí kód vrátí
BinderTypeModelBinder.BinderTypeModelBinderfunguje jako továrna pro važeři modelů a poskytuje injektáž závislostí (DI). ProAuthorEntityBinderpřístup k datům vyžaduje EF Core. Použijte,BinderTypeModelBinderpokud váš modelový važeř vyžaduje služby od diody.
Pokud chcete použít vlastního zprostředkovatele važe modelu, přidejte ho do ConfigureServices souboru :
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);
}
Při vyhodnocování važe modelů se kolekce zprostředkovatelů zkoumá v pořadí. Použije se první zprostředkovatel, který vrací vazbu. Přidání poskytovatele na konec kolekce může vést k volání integrovaného važe modelu předtím, než bude mít vlastní vázač šanci. V tomto příkladu se na začátek kolekce přidá vlastní zprostředkovatel, aby se zajistilo, že se používá pro Author argumenty akce.
Vazba polymorfního modelu
Vazba na různé modely odvozených typů se označuje jako vazba polymorfního modelu. Vazba polymorfního vlastního modelu se vyžaduje, když musí být hodnota požadavku svázaná s konkrétním typem odvozeného modelu. Vazba polymorfního modelu:
- Není typické pro aplikaci REST API která je navržená tak, aby vzájemně spolupracovat se všemi jazyky.
- Ztěžuje úvahy o vázaných modelech.
Pokud ale aplikace vyžaduje vazbu polymorfního modelu, implementace může vypadat jako následující kód:
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,
};
}
}
}
Recommendations a osvědčené postupy
Vlastní važečky modelů:
- Neměl by se pokoušet nastavit stavové kódy nebo vracet výsledky (například 404 – Nenašlé). Pokud vazba modelu selže, měl by selhání zvládnout filtr akcí nebo logika v samotné metodě akce.
- Jsou nejužitečnější pro eliminaci opakujících se kódů a křížové obavy z metod akcí.
- Obvykle by se neměl používat k převodu řetězce na vlastní typ, obvykle TypeConverter je lepší možností.