据我所知,如果 CountrySelectorViewModel 的 Model 属性是 ICountry,您仍然需要在局部视图中检查模型的属性才能将 icountry 转换为 Employee 或 Branch。
此外,由于渲染局部视图的编辑视图使用的是 CreateEmployeeViewModelData 而不是 CountrySelectorViewModel,因此您应该使用名称设置输入,而不是直接使用 asp.net core taghelper。生成的标签名称不对。您应该将其设置为 {Employee.Propertyname}。
最后,由于你设置了 ICountry 作为属性,如果你想绑定到子类,你应该创建一个自定义的模型绑定器来检查类型,你应该根据它的属性分配正确的绑定器。
通常情况下,我们会在 ICountry 中添加名为 kind 的新属性,并在您要调用局部视图时进行设置。
关于如何实现您的要求的更多细节,您可以参考以下代码:
由于我不知道您的所有模型属性,因此我自己创建了它们。你应该根据你的实际模型修改它。
型号:
国家:
public interface ICountry
{
public string Kind { get; set; }
}
分支:
公共类分支:ICountry
{
公共 int BranchId { 获取;放; }
//[Required]
//public int CountryId { get; set; }
//public Country Country { get; set; }
private string _kind;
public string Kind { get => _kind; set => _kind = "Branch"; }
// More properties
}
员工:
public class Employee : ICountry
{
public int EmployeeId { get; set; }
private string _kind;
public string Kind { get => _kind; set => _kind = "Employee"; }
}
CountrySelectorViewModel:
public class CountrySelectorViewModel
{
public int Id { get; set; }
public ICountry Model { get; set; }
//[Required]
//public ICollection<Country> Countries { get; set; } = new List<Country>();
}
CreateEmployeeViewModelData:
public class CreateEmployeeViewModelData
{
public ICountry Employee { get; set; }
}
ICountryModelBinderProvider:
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType != typeof(ICountry))
{
return null;
}
var subclasses = new[] { typeof(Employee), typeof(Branch), };
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 ICountryModelBinder(binders);
}
public class ICountryModelBinder : IModelBinder
{
private Dictionary<Type, (ModelMetadata, IModelBinder)> binders;
public ICountryModelBinder(Dictionary<Type, (ModelMetadata, IModelBinder)> binders)
{
this.binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var modelKindName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, nameof(ICountry.Kind));
var modelTypeValue = bindingContext.ValueProvider.GetValue(modelKindName).FirstValue;
//var modelTypeValue = bindingContext.ValueProvider.GetValue("Kind").FirstValue;
IModelBinder modelBinder;
ModelMetadata modelMetadata;
if (modelTypeValue == "Employee")
{
(modelMetadata, modelBinder) = binders[typeof(Employee)];
}
else if (modelTypeValue == "Branch")
{
(modelMetadata, modelBinder) = binders[typeof(Branch)];
}
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] = new ValidationStateEntry
{
Metadata = modelMetadata,
};
}
}
}
Startup.cs ConfigureServices 方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options => { options.ModelBinderProviders.Insert(0, new ICountryModelBinderProvider()); });
}
局部视图:
@model CountrySelectorViewModel
<div class="mb-3">
@*<label asp-for="Model.Country" class="control-label"></label>
<select asp-for="Model.CountryId" asp-items="@(new SelectList(Model.Countries, "Id", "Name", Model.Model?.CountryId))"></select>
<span asp-validation-for="Model.CountryId" class="text-danger"></span>*@
@if (Model.Model.GetType() == typeof(Employee))
{
<input name="Employee.EmployeeId" value="@((Model.Model as Employee).EmployeeId)" />
<input name="Employee.Kind" value="@Model.Model.Kind" />
}
@if (Model.Model.GetType() == typeof(Branch))
{
<input name="Employee.EmployeeId" value="@((Model.Model as Branch).BranchId)" />
<input name="Employee.Kind" value="@Model.Model.Kind" />
}
</div>
编辑视图:
<form asp-action="Edit">
<partial name="_CountrySelectorPartial" model="@(new CountrySelectorViewModel { Model = new Employee{EmployeeId = 1, Kind="Employee"}, Id = 1 })" />
<input type="submit" value="Click" />
</form>
<hr />
<form asp-action="Edit">
<partial name="_CountrySelectorPartial" model="@(new CountrySelectorViewModel { Model = new Branch{BranchId = 2,Kind="Branch"}, Id = 2 })" />
<input type="submit" value="Click" />
</form>
结果: