As far as I knwo, if CountrySelectorViewModel's Model property is ICountry, you still need to check the model's property in the partial view to convert the icountry to Employee or Branch.
Besides, since the edit view which render the partial view is using CreateEmployeeViewModelData not CountrySelectorViewModel, you should set the input with the name instead of directly using asp.net core taghelper. The generated tag name is not right. You should set it as {Employee.Propertyname}.
At last, since you set the I ICountry as the property, if you want to bind it to the subclass, you should create a custom model binder to check the type, you should assign the right binder according its property.
Normally, we will add new property named kind in the ICountry and set it when you want to call the partial view.
More details about how to achieve your requirement, you could refer to below codes:
Since I don't know all your model property, I created them by myself. You should modify it according to your actually model.
Model:
ICountry:
public interface ICountry
{
public string Kind { get; set; }
}
Branch :
public class Branch : ICountry
{
public int BranchId { get; set; }
//[Required]
//public int CountryId { get; set; }
//public Country Country { get; set; }
private string _kind;
public string Kind { get => _kind; set => _kind = "Branch"; }
// More properties
}
Employee:
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 method:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options => { options.ModelBinderProviders.Insert(0, new ICountryModelBinderProvider()); });
}
Partial view:
@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>
Edit view:
<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>
Result:
