我正在尝试使用自定义模型绑定程序将数据发送到Controller时解决继承问题。我发现了几个博客文章和Stack Overflow文章,它们解释了类似的情况。例如:https://stackoverflow.com/questions/7222533/polymorphic-model-binding/7222639#7222639
我的测试表明,这些解决方案在某些情况下可以正常工作。但是,在某些情况下,这些解决方案将失败,并且到目前为止我还无法找到解决方案。
让我们尝试创建一个简单的方案(我为这个问题冗长而道歉,但我试图解释每个细节) 我们有一个枚举连接类型
public enum ConnectionType
{
ConnectionSQL,
ConnectionOracle
}
让我们引入继承
public interface IConnectionDatabase
{
ConnectionType Type { get; }
}
public class ConnectionSQLDatabase : IConnectionDatabase
{
public ConnectionType Type => ConnectionType.ConnectionSQL;
}
public class ConnectionOracleDatabase : IConnectionDatabase
{
public ConnectionType Type => ConnectionType.ConnectionOracle;
}
最后是一个 Class ,如下所示:
public class DataSource
{
public IConnectionDatabase DataBase { get; set; }
}
和控制器操作
public ActionResult AddDataSource(DataSource dataSource)
{
// more details not required
}
与该 Model Binder
一起使用时效果很好public class ConnectionDatabaseModelBinder : DefaultModelBinder
{
public override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType != null && bindingContext != null && modelType.Equals(typeof(IConnectionDatabase)))
{
try
{
// get the correct connection type
string connectionTypeString = bindingContext.ValueProvider.GetValue("Type");
ConnectionType connectionType = (ConnectionType)Enum.Parse(typeof(ConnectionType), connectionTypeString, true);
Type type = connectionType.BuildConnectionType(); // I did some logic here to give the correct class based on the Type
// Create an instance of the specified type
IConnectionDatabase connectionDatabase = ClassBuilder.CreateInstance<IConnectionDatabase>(type);
// Gets metadata for the specified model accessor and model type
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, type);
bindingContext.ModelMetadata.Model = connectionDatabase;
return connectionDatabase;
}
catch (Exception ex)
{
// throw Exception here
}
}
else
{
// use the default Model Binder
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
现在这似乎可行,当我使用来自JavaScript的数据通过POST请求调用Controller Action
var connection = {
Type = "ConnectionSQL"
}
在这种情况下,我在控制器操作中获得了类ConnectionSQLDatabase的实例。到目前为止一切都很好!
现在,如果我们添加另一个具有DataSource对象列表的Class
public class Environment
{
public List<DataSource> DataSourceList { get; set; }
}
并添加另一个控制器动作为
public ActionResult AddEnvironment(Environment environment)
{
// more details not required
}
和从JavaScript发送的数据为
var environment = {
DataSourceList = [
{ Type: "ConnectionSQL" },
{ Type: "ConnectionOracle" },
]
}
在这种情况下,请求仍然会碰到Model Binder,但是在CreateModel方法的第一行中失败,表示它无法从值提供者读取名为“ Type”的键
那是因为环境模型本身没有名为“ Type”的属性,但其子类却具有。
我也可以调整Model Binder以处理这种情况,但是可以说,如果我开始添加更多使用“ DataSource”类型的对象的类,那么我将不得不开始处理所有情况,这将是混乱的。
是否有实现此目的的标准方法?