我的应用程序通过以下方式进行操纵...
模型和子模型
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");
}
模型资料夹采用在我的“创建 View ”中定义的字符串并返回适当的子模型。
public class BaseModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType.Equals(typeof(BaseModelBinder)))
{
string typeValue = bindingContext.ValueProvider.GetValue("ModelType").AttemptedValue;
Type type = Type.GetType(modelType.Namespace + "." + typeValue, true);
object model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
Controller
[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>
}
子模型的编辑器模板
@model MyApp.Models.SubModel
// Form Stuff
这一切都很好,花花公子。我可以拉出“创建 View ”并显示所请求的子模型的适当模板,当我发布它时,“创建HttpPost”操作会将其作为BaseModel类型,并插入SubModel表和BaseModel表中。现在,我想为BaseModel和SubModels创建ViewModels以保持域模型的清洁。
问题
如果我更改 Controller 以返回ViewModel,则当我发布到Create Action时,它不是正确的Type,因为它不继承自BaseModel。
如何为BaseModel创建一个ViewModel,该ViewModel仅公开某些可以通过数据注释进行客户端验证的属性,还可以为SubModels创建SubViewModels,并使SibViewModel也为SubModel类型,因为SubModels继承了,因此它也将为BaseModel类型。从BaseModel?
我还希望SubViewModels继承BaseModelViewModel属性,因为每个SubModel都需要BaseModel的一部分。
查看此link ,有关泛型的答案可能是我要采用的路径,但不确定如何正确实现它。
我需要使用ViewModel,将其传递给Create [HttpPost] Controller ,并将其插入到BaseModel表中(或者根据ViewModel插入SubModel和BaseModel)。
文章很长,可能会混淆我的措辞方式,因为我很难描述有时要完成的事情。我一直在花费大量时间阅读ASP.NET MVC,似乎我读得越多,对设计模式的质疑就越多,因为要完成同一任务的选择太多,每个选择都有其优缺点,因此许多因素...我的想法重载。请帮忙 :]
最佳答案
如果我对您的理解正确,那你就不知道。这将要求您的SubViewModel从BaseViewModel和SubModel继承,而SubModel需要多重继承。 .NET中不提供该功能。
即使有可能,我也不会推荐它。我相信您要这样做的原因是要重用尽可能多的代码,而不必将SubViewModel维护为域模型的“镜像”。但这真的是正确的方法吗?
考虑一下:假设您的SubViewModel确实从您的域模型继承。这将导致所有属性和方法在 View 模型中可用,因此在 View 中可用。这也将导致所有将来的属性和方法在您的 View 中可用。在不更改域模型的情况下,您将无法再控制对 View 可用的属性和方法。您可以将新属性和方法添加到 View 模型中,而无需将其添加到域模型中,但是不能删除从域模型继承的属性和方法。我认为这是一个缺点。理想情况下,您希望 View 模型完全公开 View 完成其任务所需的数据,并且不再公开。请记住,“ View 模型”的想法是拥有一个对特定 View 及其应完成的任务有用的模型。
还有另一个缺点。要启用客户端验证,您需要使用“数据注释”属性来装饰属性。如果您的SubViewModel从您的域模型继承来重用属性,例如Name,ZipCode等,那么您需要将这些属性放在域模型中的属性上。因此,现在您的域模型中具有与MVC客户端验证相关的属性。如果您后来决定在不使用MVC的另一个项目中使用另一个技术重用您的域模型,那么这些属性几乎是无用的。 (我几乎说,因为在特定情况下,您可能会很幸运,并找到一些可以利用数据注释属性的替代验证框架,但这不能保证。)实际上,您会用某种方式“污染”您的域模型这与域无关。
现在,我并不是说验证与域无关,因为它肯定不是。但是,我会考虑使用一种比域数据注释属性更通用的域层验证方法。 (例如流利验证)
此外,如果您需要更复杂的验证(现在或将来),那么您会很快发现缺少的数据注释属性。您可以实现自己的属性,但是如果您需要更复杂的验证(例如条件验证),它就不会很漂亮。 (“如果其他属性,则此属性应遵循这些规则...”)
另外,如果稍后发现需要在MVC中使用另一个不使用数据注释属性的验证框架,则需要更改域模型以删除属性。
您在这里看到图案了吗?您想要更改UI(MVC)中的某些内容,因此您需要更改域模型中的某些内容。这确实不理想。您希望域层和UI层尽可能地分离。
我知道我可能在这里切线了,可能不是直接回答您的问题。但是我的意思是,我认为您应该重新考虑将域模型和MVC应用程序耦合在一起,方法是让 View 模型从域模型继承。
实际上,如果您这样做,上述问题将消失。