Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Von Kirk Larkin
Mit der Modellbindung können Controlleraktionen direkt mit Modelltypen (übergeben als Methodenargumente) und nicht mit HTTP-Anforderungen arbeiten. Die Zuordnung zwischen eingehenden Anforderungsdaten und Anwendungsmodellen wird von Modellordnern behandelt. Entwickler können die integrierte Modellbindungsfunktionalität erweitern, indem sie benutzerdefinierte Modellbinder implementieren (in der Regel müssen Sie jedoch keinen eigenen Provider schreiben).
Anzeigen oder Herunterladen von Beispielcode (Anleitung zum Herunterladen)
Standardmodellbinderbeschränkungen
Die Standardmodellordner unterstützen die meisten gängigen .NET Core-Datentypen und sollten die meisten Entwickleranforderungen erfüllen. Sie erwarten, dass textbasierte Eingaben von der Anforderung direkt an Modelltypen gebunden werden. Möglicherweise müssen Sie die Eingabe vor der Bindung transformieren. Wenn Sie beispielsweise über einen Schlüssel verfügen, der zum Nachschlagen von Modelldaten verwendet werden kann. Sie können einen benutzerdefinierten Modellordner verwenden, um Daten basierend auf dem Schlüssel abzurufen.
Einfache und komplexe Typen der Modellbindung
Die Modellbindung verwendet spezifische Definitionen für die Typen, auf denen sie arbeitet. Ein einfacher Typ wird aus einer einzigen Zeichenfolge mithilfe von TypeConverter oder einer TryParse-Methode konvertiert. Ein komplexer Typ wird aus mehreren Eingabewerten konvertiert. Das Framework bestimmt den Unterschied auf Grundlage des Vorhandenseins eines TypeConverter- oder TryParse-Objekts. Es wird empfohlen, einen Typkonverter zu erstellen oder die TryParse-Funktion für eine string- oder SomeType-Konvertierung zu verwenden, für die keine externen Ressourcen oder mehrere Eingaben erforderlich sind.
Eine Liste der Typen, die vom Modellordner aus Zeichenfolgen konvertiert werden können, finden Sie unter "Einfache Typen ".
Bevor Sie einen eigenen benutzerdefinierten Modellordner erstellen, sollten Sie überprüfen, wie vorhandene Modellordner implementiert werden. Betrachten Sie ByteArrayModelBinder, die zum Konvertieren von base64-codierten Zeichenfolgen in Byte-Arrays verwendet werden kann. Die Bytearrays werden häufig als Dateien oder Datenbank-BLOB-Felder gespeichert.
Arbeiten mit ByteArrayModelBinder
Base64-codierte Zeichenfolgen können zum Darstellen von Binärdaten verwendet werden. Beispielsweise kann ein Bild als Zeichenfolge codiert werden. Das Beispiel enthält ein Bild als base64-codierte Zeichenfolge in Base64String.txt.
ASP.NET Core MVC kann eine base64-codierte Zeichenfolge nehmen und ein ByteArrayModelBinder verwenden, um sie in ein Byte-Array zu konvertieren. Die ByteArrayModelBinderProvider ordnet byte[] Argumente ByteArrayModelBinder zu:
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;
}
Beim Erstellen eines eigenen benutzerdefinierten Modell-Binders können Sie Ihren eigenen IModelBinderProvider-Typ implementieren oder den ModelBinderAttribute verwenden.
Das folgende Beispiel zeigt, wie Sie eine base64-codierte Zeichenfolge in ein ByteArrayModelBinder konvertieren und das Ergebnis in einer Datei speichern:
[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);
}
Wenn Sie möchten, dass Codekommentare in anderen Sprachen als Englisch angezeigt werden, informieren Sie uns in diesem GitHub-Issue.
Sie können eine base64-codierte Zeichenfolge in die vorherige API-Methode mit einem Tool wie curl posten.
Solange der Ordner Anforderungsdaten an entsprechend benannte Eigenschaften oder Argumente binden kann, ist die Modellbindung erfolgreich. Das folgende Beispiel zeigt die Verwendung von ByteArrayModelBinder mit einem Ansichtsmodell.
[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; }
}
Beispiel für benutzerdefinierte Modellbinder
In diesem Abschnitt implementieren wir einen benutzerdefinierten Modellordner, der:
- Konvertiert eingehende Anforderungsdaten in stark typierte Schlüsselargumente.
- Verwendet Entity Framework Core, um die zugeordnete Entität abzurufen.
- Übergibt die zugeordnete Entität als Argument an die Aktionsmethode.
Im folgenden Beispiel wird das ModelBinder Attribut für das Author Modell verwendet:
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; }
}
}
Im vorherigen Code gibt das ModelBinder-Attribut den Typ von IModelBinder an, der verwendet werden soll, um Author-Aktionsparameter zu binden.
Die folgende AuthorEntityBinder Klasse bindet einen Author-Parameter, indem sie die Entität mit Hilfe von Entity Framework Core aus einer Datenquelle abruft und einer 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
Die vorangehende AuthorEntityBinder Klasse soll einen benutzerdefinierten Modellordner veranschaulichen. Die Klasse soll keine bewährten Methoden für ein Nachschlageszenario veranschaulichen. Binden Sie das authorId für das Nachschlagen und fragen Sie die Datenbank in einer Aktionsmethode ab. Bei diesem Ansatz werden Modellbindungsfehler von NotFound Fällen getrennt.
Der folgende Code veranschaulicht die Verwendung der AuthorEntityBinder in einer Aktionsmethode:
[HttpGet("get/{author}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Das ModelBinder Attribut kann verwendet werden, um die AuthorEntityBinder Parameter anzuwenden, die keine Standardkonventionen verwenden:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Da in diesem Beispiel der Name des Arguments nicht der Standardwert authorIdist, wird er für den Parameter mithilfe des ModelBinder Attributs angegeben. Sowohl die Controller- als auch die Aktionsmethode sind im Vergleich zum Nachschlagen der Entität in der Aktionsmethode vereinfacht. Die Logik zum Abrufen des Autors mithilfe von Entity Framework Core wird in den Modellordner verschoben. Dies kann eine erhebliche Vereinfachung sein, wenn Sie über mehrere Methoden verfügen, die an das Author Modell gebunden sind.
Sie können das ModelBinder Attribut auf einzelne Modelleigenschaften (z. B. in einem Viewmodel) oder auf Aktionsmethodenparameter anwenden, um einen bestimmten Modellordner oder Modellnamen nur für diesen Typ oder diese Aktion anzugeben.
Ein ModelBinderProvider implementieren
Anstatt ein Attribut anzuwenden, können Sie implementieren IModelBinderProvider. So werden die integrierten Framework-Binder implementiert. Wenn Sie den Typ angeben, auf den sich Ihr Binder bezieht, geben Sie den Typ des Arguments an, das er erzeugt, nicht die Eingabe, die Ihr Binder akzeptiert. Der folgende Ordneranbieter arbeitet mit dem AuthorEntityBinder. Wenn es der Sammlung von Anbietern von MVC hinzugefügt wird, müssen Sie das ModelBinder Attribut nicht für Author oder Author-typierte Parameter verwenden.
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;
}
}
}
Hinweis: Der vorangehende Quellcode gibt einen
BinderTypeModelBinderzurück.BinderTypeModelBinderdient als Fabrik für Modellbinder und stellt Abhängigkeitsinjektion (Dependency Injection, DI) bereit.AuthorEntityBinderbenötigt DI, um auf EF Core zuzugreifen. Verwenden Sie diese OptionBinderTypeModelBinder, wenn Ihr Modellordner Dienste von DI benötigt.
Um einen benutzerdefinierten Modellbinderanbieter zu verwenden, fügen Sie ihn in ConfigureServices hinzu.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AuthorContext>(options => options.UseInMemoryDatabase("Authors"));
services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new AuthorEntityBinderProvider());
});
}
Bei der Auswertung von Modellbindungen wird die Sammlung der Provider in der festgelegten Reihenfolge untersucht. Der erste Anbieter, der einen Ordner zurückgibt, der dem Eingabemodell entspricht, wird verwendet. Wenn Sie Ihren Anbieter am Ende der Sammlung hinzufügen, kann dies dazu führen, dass ein integrierter Modell-Binder aufgerufen wird, bevor Ihr benutzerdefinierter Binder eine Chance hat. In diesem Beispiel wird der benutzerdefinierte Anbieter am Anfang der Auflistung hinzugefügt, um sicherzustellen, dass er immer für Author Aktionsargumente verwendet wird.
Polymorphe Modellbindung
Die Bindung an verschiedene Modelle abgeleiteter Typen wird als polymorphe Modellbindung bezeichnet. Polymorphe benutzerdefinierte Modellbindung ist erforderlich, wenn der Anforderungswert an den spezifischen abgeleiteten Modelltyp gebunden werden muss. Polymorphe Modellbindung:
- Ist nicht typisch für eine REST API, die für die Interoperabilität mit allen Sprachen konzipiert ist.
- Macht es schwierig, über die gebundenen Modelle zu gründen.
Wenn eine App jedoch eine polymorphe Modellbindung erfordert, kann eine Implementierung wie der folgende Code aussehen:
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,
};
}
}
}
Empfehlungen und bewährte Methoden
Benutzerdefinierte Modellbindungen:
- Es sollte nicht versucht werden, Statuscodes festzulegen oder Ergebnisse zurückzugeben (z. B. 404 Nicht gefunden). Wenn die Modellbindung fehlschlägt, sollte ein Aktionsfilter oder eine Logik innerhalb der Aktionsmethode selbst den Fehler behandeln.
- Sind am nützlichsten, um sich wiederholenden Code und übergreifende Bedenken aus Aktionsmethoden zu beseitigen.
- Normalerweise sollte eine Zeichenfolge nicht in einen benutzerdefinierten Typ konvertiert werden, ein TypeConverter ist in der Regel die bessere Wahl.
Von Steve Smith
Mit der Modellbindung können Controlleraktionen direkt mit Modelltypen (übergeben als Methodenargumente) und nicht mit HTTP-Anforderungen arbeiten. Die Zuordnung zwischen eingehenden Anforderungsdaten und Anwendungsmodellen wird von Modellordnern behandelt. Entwickler können die integrierte Modellbindungsfunktionalität erweitern, indem sie benutzerdefinierte Modellbinder implementieren (in der Regel müssen Sie jedoch keinen eigenen Provider schreiben).
Anzeigen oder Herunterladen von Beispielcode (Anleitung zum Herunterladen)
Standardmodellbinderbeschränkungen
Die Standardmodellordner unterstützen die meisten gängigen .NET Core-Datentypen und sollten die meisten Entwickleranforderungen erfüllen. Sie erwarten, dass textbasierte Eingaben von der Anforderung direkt an Modelltypen gebunden werden. Möglicherweise müssen Sie die Eingabe vor der Bindung transformieren. Wenn Sie beispielsweise über einen Schlüssel verfügen, der zum Nachschlagen von Modelldaten verwendet werden kann. Sie können einen benutzerdefinierten Modellordner verwenden, um Daten basierend auf dem Schlüssel abzurufen.
Modellbindungsüberprüfung
Die Modellbindung verwendet spezifische Definitionen für die Typen, auf denen sie arbeitet. Ein einfacher Typ wird aus einer einzelnen Zeichenfolge in der Eingabe konvertiert. Ein komplexer Typ wird aus mehreren Eingabewerten konvertiert. Das Framework bestimmt den Unterschied basierend auf dem Vorhandensein eines TypeConverter. Es wird empfohlen, einen Typkonverter zu erstellen, wenn Sie über eine einfache string>SomeType Zuordnung verfügen, die keine externen Ressourcen erfordert.
Bevor Sie einen eigenen benutzerdefinierten Modellordner erstellen, sollten Sie überprüfen, wie vorhandene Modellordner implementiert werden. Betrachten Sie ByteArrayModelBinder, die zum Konvertieren von base64-codierten Zeichenfolgen in Byte-Arrays verwendet werden kann. Die Bytearrays werden häufig als Dateien oder Datenbank-BLOB-Felder gespeichert.
Arbeiten mit ByteArrayModelBinder
Base64-codierte Zeichenfolgen können zum Darstellen von Binärdaten verwendet werden. Beispielsweise kann ein Bild als Zeichenfolge codiert werden. Das Beispiel enthält ein Bild als base64-codierte Zeichenfolge in Base64String.txt.
ASP.NET Core MVC kann eine base64-codierte Zeichenfolge nehmen und ein ByteArrayModelBinder verwenden, um sie in ein Byte-Array zu konvertieren. Die ByteArrayModelBinderProvider ordnet byte[] Argumente ByteArrayModelBinder zu:
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(byte[]))
{
return new ByteArrayModelBinder();
}
return null;
}
Beim Erstellen eines eigenen benutzerdefinierten Modell-Binders können Sie Ihren eigenen IModelBinderProvider-Typ implementieren oder den ModelBinderAttribute verwenden.
Das folgende Beispiel zeigt, wie Sie eine base64-codierte Zeichenfolge in ein ByteArrayModelBinder konvertieren und das Ergebnis in einer Datei speichern:
[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);
}
Sie können eine base64-codierte Zeichenfolge in die vorherige API-Methode mit einem Tool wie curl posten.
Solange der Ordner Anforderungsdaten an entsprechend benannte Eigenschaften oder Argumente binden kann, ist die Modellbindung erfolgreich. Das folgende Beispiel zeigt die Verwendung von ByteArrayModelBinder mit einem Ansichtsmodell.
[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; }
}
Beispiel für benutzerdefinierte Modellbinder
In diesem Abschnitt implementieren wir einen benutzerdefinierten Modellordner, der:
- Konvertiert eingehende Anforderungsdaten in stark typierte Schlüsselargumente.
- Verwendet Entity Framework Core, um die zugeordnete Entität abzurufen.
- Übergibt die zugeordnete Entität als Argument an die Aktionsmethode.
Im folgenden Beispiel wird das ModelBinder Attribut für das Author Modell verwendet:
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; }
}
}
Im vorherigen Code gibt das ModelBinder-Attribut den Typ von IModelBinder an, der verwendet werden soll, um Author-Aktionsparameter zu binden.
Die folgende AuthorEntityBinder Klasse bindet einen Author-Parameter, indem sie die Entität mit Hilfe von Entity Framework Core aus einer Datenquelle abruft und einer 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
Die vorangehende AuthorEntityBinder Klasse soll einen benutzerdefinierten Modellordner veranschaulichen. Die Klasse soll keine bewährten Methoden für ein Nachschlageszenario veranschaulichen. Binden Sie das authorId für das Nachschlagen und fragen Sie die Datenbank in einer Aktionsmethode ab. Bei diesem Ansatz werden Modellbindungsfehler von NotFound Fällen getrennt.
Der folgende Code veranschaulicht die Verwendung der AuthorEntityBinder in einer Aktionsmethode:
[HttpGet("get/{author}")]
public IActionResult Get(Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Das ModelBinder Attribut kann verwendet werden, um die AuthorEntityBinder Parameter anzuwenden, die keine Standardkonventionen verwenden:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
if (author == null)
{
return NotFound();
}
return Ok(author);
}
Da in diesem Beispiel der Name des Arguments nicht der Standardwert authorIdist, wird er für den Parameter mithilfe des ModelBinder Attributs angegeben. Sowohl die Controller- als auch die Aktionsmethode sind im Vergleich zum Nachschlagen der Entität in der Aktionsmethode vereinfacht. Die Logik zum Abrufen des Autors mithilfe von Entity Framework Core wird in den Modellordner verschoben. Dies kann eine erhebliche Vereinfachung sein, wenn Sie über mehrere Methoden verfügen, die an das Author Modell gebunden sind.
Sie können das ModelBinder Attribut auf einzelne Modelleigenschaften (z. B. in einem Viewmodel) oder auf Aktionsmethodenparameter anwenden, um einen bestimmten Modellordner oder Modellnamen nur für diesen Typ oder diese Aktion anzugeben.
Ein ModelBinderProvider implementieren
Anstatt ein Attribut anzuwenden, können Sie implementieren IModelBinderProvider. So werden die integrierten Framework-Binder implementiert. Wenn Sie den Typ angeben, auf den sich Ihr Binder bezieht, geben Sie den Typ des Arguments an, das er erzeugt, nicht die Eingabe, die Ihr Binder akzeptiert. Der folgende Ordneranbieter arbeitet mit dem AuthorEntityBinder. Wenn es der Sammlung von Anbietern von MVC hinzugefügt wird, müssen Sie das ModelBinder Attribut nicht für Author oder Author-typierte Parameter verwenden.
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;
}
}
}
Hinweis: Der vorangehende Quellcode gibt einen
BinderTypeModelBinderzurück.BinderTypeModelBinderdient als Fabrik für Modellbinder und stellt Abhängigkeitsinjektion (Dependency Injection, DI) bereit.AuthorEntityBinderbenötigt DI, um auf EF Core zuzugreifen. Verwenden Sie diese OptionBinderTypeModelBinder, wenn Ihr Modellordner Dienste von DI benötigt.
Um einen benutzerdefinierten Modellbinderanbieter zu verwenden, fügen Sie ihn in ConfigureServices hinzu.
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);
}
Bei der Auswertung von Modellbindungen wird die Sammlung der Provider in der festgelegten Reihenfolge untersucht. Der erste Anbieter, der einen Sammelordner zurückgibt, wird verwendet. Wenn Sie Ihren Anbieter am Ende der Sammlung hinzufügen, kann dies dazu führen, dass ein integrierter Modellordner aufgerufen wird, bevor Ihr benutzerdefinierter Ordner die Möglichkeit hat. In diesem Beispiel wird der benutzerdefinierte Anbieter am Anfang der Auflistung hinzugefügt, um sicherzustellen, dass er für Author Aktionsargumente verwendet wird.
Polymorphe Modellbindung
Die Bindung an verschiedene Modelle abgeleiteter Typen wird als polymorphe Modellbindung bezeichnet. Polymorphe benutzerdefinierte Modellbindung ist erforderlich, wenn der Anforderungswert an den spezifischen abgeleiteten Modelltyp gebunden werden muss. Polymorphe Modellbindung:
- Ist nicht typisch für eine REST API, die für die Interoperabilität mit allen Sprachen konzipiert ist.
- Macht es schwierig, über die gebundenen Modelle zu gründen.
Wenn eine App jedoch eine polymorphe Modellbindung erfordert, kann eine Implementierung wie der folgende Code aussehen:
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,
};
}
}
}
Empfehlungen und bewährte Methoden
Benutzerdefinierte Modellbindungen:
- Es sollte nicht versucht werden, Statuscodes festzulegen oder Ergebnisse zurückzugeben (z. B. 404 Nicht gefunden). Wenn die Modellbindung fehlschlägt, sollte ein Aktionsfilter oder eine Logik innerhalb der Aktionsmethode selbst den Fehler behandeln.
- Sind am nützlichsten, um sich wiederholenden Code und übergreifende Bedenken aus Aktionsmethoden zu beseitigen.
- Normalerweise sollte eine Zeichenfolge nicht in einen benutzerdefinierten Typ konvertiert werden, ein TypeConverter ist in der Regel die bessere Wahl.