具有接口继承的模型绑定不起作用

时间:2018-07-24 20:09:34

标签: javascript c# inheritance modelbinders

我正在尝试使用自定义模型绑定程序将数据发送到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”类型的对象的类,那么我将不得不开始处理所有情况,这将是混乱的。

是否有实现此目的的标准方法?

0 个答案:

没有答案