C# MVC 5 EF 6 TPT和ViewModels

C# MVC 5 EF 6 TPT和ViewModels,c#,asp.net-mvc,entity-framework,inheritance,mvvm,C#,Asp.net Mvc,Entity Framework,Inheritance,Mvvm,我的应用程序被以下方式操纵 模型与子模型 public class BaseModel { public int Id { get; set; } // Other required properties } public class SubModel : BaseModel { public string SomeString { get; set; } // Other SubModel properties } 在上下文中 protect

我的应用程序被以下方式操纵

模型与子模型

public class BaseModel
{
    public int Id { get; set; }        
    // Other required properties
}

public class SubModel : BaseModel
{
    public string SomeString { get; set; }
    // Other SubModel properties
}
在上下文中

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<SubModel>().ToTable("SubModel");
}
控制器

[Route("Create"), HttpGet]
public ActionResult Create(string modelType)
{
    // Get Model Type from modelType and convert into appropriate type
    // to retrieve the proper editor template.
    Type type = Type.GetType("MyApp.Models." + modelType);
    object model = Activator.CreateInstance(type);
    return View(model);
}

[Route("Create"), HttpPost]
public ActionResult Create(BaseModel baseModel)
{
    if (ModelState.IsValid)
    {
        var userId = Convert.ToInt32(IdentityExtensions.GetUserId(User.Identity));
        baseModel.UserId = userId;
        baseModel.DatePosted = DateTime.UtcNow;
        // If this is a SubModel, an insert is made into table BaseModel as well
        // as an insert into the SubModel table for SubModel specific properties
        // linked by the Id of the BaseModel as the SubModel Id
        db.BaseModel.Add(baseModel);
        db.SaveChanges();
        return RedirectToAction("List");
    }        
    return View(baseModel);
}
创建视图

@model MyApp.Models.BaseModel

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal">
        @Html.ValidationSummary(true)
        @Html.Hidden("ModelType", Model.GetType())
        @Html.EditorForModel()
    </div>
}
这些都很好用。我可以调出创建视图并为请求的子模型显示适当的模板,当我发布它时,Create-HttpPost操作将其视为BaseModel类型并插入子模型表和BaseModel表中。现在,我要做的是为我的基本模型和子模型创建ViewModels,以保持域模型干净

发行

如果我将控制器更改为返回ViewModel,当我发布到创建操作时,它的类型不正确,因为它不从BaseModel继承

如何为BaseModel创建一个ViewModel,该ViewModel只公开某些属性,我可以对这些属性进行数据注释以进行客户端验证,并为子模型创建子ViewModel,并且使SibViewModel也为SubModel类型,因为子模型继承自BaseModel,因此它也将为BaseModel类型

我还希望子视图模型继承BaseModelViewModel属性,因为每个子模型都需要BaseModel的一部分

综上所述,关于泛型的答案可能是我想要走的路,但不确定如何正确地实现它

我需要获取ViewModel,将其传递给Create[HttpPost]控制器,并根据ViewModel将其插入BaseModel表或子模型和BaseModel

这是一篇很长的帖子,可能会混淆我的措辞方式,因为我有时很难描述我试图完成的事情。我花了好几个小时在ASP.NET MVC上阅读,似乎我读得越多,我就越怀疑我的设计模式,因为有太多的替代方案来完成相同的任务,每个方案都有利弊,取决于太多的因素。。。我的头脑超负荷了。请帮忙:]

如何为BaseModel创建一个ViewModel,该ViewModel只公开某些属性,我可以对这些属性进行数据注释以进行客户端验证,并为子模型创建子ViewModel,并且使SibViewModel也为SubModel类型,因为子模型继承自BaseModel,因此它也将为BaseModel类型

如果我理解正确,你就不能。这将要求子视图模型继承BaseViewModel和子模型,这将需要多重继承。而这在.NET中是不可用的

即使有可能,我也不会推荐它。我相信您希望这样做的原因是尽可能多地重用代码,而不必维护子视图模型,使其成为域模型的“镜像”。但这真的是正确的方式吗

考虑一下:假设您的子视图模型确实继承了域模型。这将导致viewmodel中以及视图中的所有属性和方法都可用。它还将导致所有未来的属性和方法在视图中可用。在不更改域模型的情况下,您无法再控制视图可用的属性和方法。您可以向viewmodel添加新属性和方法,而无需将它们添加到域模型中,但无法删除从域模型继承的属性和方法。我认为这是一个缺点。理想情况下,您希望视图模型能够准确地公开视图完成其任务所需的数据,而不是更多。记住,“视图模型”的概念是要有一个对特定视图和它们应该完成的任务有用的模型

还有另一个缺点。要启用客户端验证,需要使用数据注释属性装饰属性。如果子视图模型继承自域模型以重用属性,如名称、ZipCode等,则需要将这些属性放在域模型的属性上。现在,您的域模型中有了与MVC客户端验证相关的属性。如果您后来决定在另一个使用另一种技术的项目中重用您的域模型,而您没有使用MVC,那么这些属性几乎是无用的。我说几乎是,因为在一个特定的场景中,您可能会幸运地找到一些替代的验证框架,可以利用数据注释属性,但实际上并不能保证,您会用一些与域无关的东西“污染”域模型

现在,我并不是说验证与域无关,因为它肯定不是。但是我会考虑在领域层中使用一种更一般的验证方法,而不是使用数据注释属性。例如,Fluent验证

此外,如果您现在或将来需要更复杂的验证,那么您很快就会发现缺少数据注释属性。您可以实现自己的属性,但是如果您需要更复杂的验证,那么它就不好看了 ,例如条件验证。”如果其他属性…,则此属性应遵循这些规则

此外,如果以后发现需要在MVC中使用另一个不使用数据注释属性的验证框架,则需要更改域模型以删除属性

你看到这里的图案了吗?您希望在UI MVC中更改某些内容,因此需要在域模型中更改某些内容。这实在不理想。您希望域层和UI层尽可能地解耦

我知道我可能在这里偏离了正轨,这可能不是直接回答你的问题。但我的观点是,我认为您应该重新考虑将您的域模型和MVC应用程序耦合在一起,让您的视图模型继承自您的域模型

事实上,如果你这样做了,你上面描述的问题就会消失

如何为BaseModel创建一个ViewModel,该ViewModel只公开某些属性,我可以对这些属性进行数据注释以进行客户端验证,并为子模型创建子ViewModel,并且使SibViewModel也为SubModel类型,因为子模型继承自BaseModel,因此它也将为BaseModel类型

如果我理解正确,你就不能。这将要求子视图模型继承BaseViewModel和子模型,这将需要多重继承。而这在.NET中是不可用的

即使有可能,我也不会推荐它。我相信您希望这样做的原因是尽可能多地重用代码,而不必维护子视图模型,使其成为域模型的“镜像”。但这真的是正确的方式吗

考虑一下:假设您的子视图模型确实继承了域模型。这将导致viewmodel中以及视图中的所有属性和方法都可用。它还将导致所有未来的属性和方法在视图中可用。在不更改域模型的情况下,您无法再控制视图可用的属性和方法。您可以向viewmodel添加新属性和方法,而无需将它们添加到域模型中,但无法删除从域模型继承的属性和方法。我认为这是一个缺点。理想情况下,您希望视图模型能够准确地公开视图完成其任务所需的数据,而不是更多。记住,“视图模型”的概念是要有一个对特定视图和它们应该完成的任务有用的模型

还有另一个缺点。要启用客户端验证,需要使用数据注释属性装饰属性。如果子视图模型继承自域模型以重用属性,如名称、ZipCode等,则需要将这些属性放在域模型的属性上。现在,您的域模型中有了与MVC客户端验证相关的属性。如果您后来决定在另一个使用另一种技术的项目中重用您的域模型,而您没有使用MVC,那么这些属性几乎是无用的。我说几乎是,因为在一个特定的场景中,您可能会幸运地找到一些替代的验证框架,可以利用数据注释属性,但实际上并不能保证,您会用一些与域无关的东西“污染”域模型

现在,我并不是说验证与域无关,因为它肯定不是。但是我会考虑在领域层中使用一种更一般的验证方法,而不是使用数据注释属性。例如,Fluent验证

此外,如果您现在或将来需要更复杂的验证,那么您很快就会发现缺少数据注释属性。你可以实现自己的属性,但如果你需要更复杂的验证,比如条件验证,那就不太好了如果其他属性…,则此属性应遵循这些规则

此外,如果以后发现需要在MVC中使用另一个不使用数据注释属性的验证框架,则需要更改域模型以删除属性

你看到这里的图案了吗?您希望在UI MVC中更改某些内容,因此需要在域模型中更改某些内容。这实在不理想。您希望域层和UI层尽可能地解耦

我知道我可能在这里偏离了正轨,这可能不是直接回答你的问题。但我的观点是,我认为您应该重新考虑将您的域模型和MVC应用程序耦合在一起,让您的视图模型继承自您的域模型

实际上,如果你这样做了,你上面描述的问题就会消失。

否认这只是一个预先评论

像任 他指出,看起来你的问题不是真正的问题

因为ViewModel应该封装模型而不是继承它。这就是MVVM的VM-M部分的工作原理

通常情况下,VM应该与此类似

public class VM
{
    M model;

    public M Model
    {
        get { return model; }
        set { model = value; }
    }

    public int ID
    {
        get { return model.ID; }
        set { model.ID = value; }
    }

    #region cTor
    public VM(M m)
    {
        this.Model = m;
    }
    #endregion
}
但要将模型层与ViewModle层分离,可以使用一个界面 因此,M可以是一个类或接口,具体取决于您的需要。

否认这只是一个预先评论

就像勒内指出的,看起来你的问题不是真正的问题

因为ViewModel应该封装模型而不是继承它。这就是MVVM的VM-M部分的工作原理

通常情况下,VM应该与此类似

public class VM
{
    M model;

    public M Model
    {
        get { return model; }
        set { model = value; }
    }

    public int ID
    {
        get { return model.ID; }
        set { model.ID = value; }
    }

    #region cTor
    public VM(M m)
    {
        this.Model = m;
    }
    #endregion
}
但要将模型层与ViewModle层分离,可以使用一个界面
因此,M可以是一个类或接口,具体取决于您的需要。

我明白您的意图,我将对此进行更多研究。谢谢你的帮助。我知道你要去哪里了,我会进一步研究的。谢谢你的帮助。
public class VM
{
    M model;

    public M Model
    {
        get { return model; }
        set { model = value; }
    }

    public int ID
    {
        get { return model.ID; }
        set { model.ID = value; }
    }

    #region cTor
    public VM(M m)
    {
        this.Model = m;
    }
    #endregion
}