【问题标题】:Is my ASP.NET MVC application structured properly?我的 ASP.NET MVC 应用程序结构是否正确?
【发布时间】:2010-11-29 19:54:34
【问题描述】:

我一直在阅读教程(特别是使用 Linq-To-Entities 的教程)并且我了解基本概念,但是有些事情给我带来了问题。

教程通常只涉及简单的模型和表单,它们只使用基本的创建、更新和删除语句。我的稍微复杂一点,我不确定我是否以正确的方式进行处理,因为当涉及到处理六个数据库对象的关系时,教程就不再有帮助了。

对于post方法,执行CRUD操作的常用方式

entities.AddToTableSet(myClass);
entities.SaveChanges();

不会做我想做的事,因为完全实现的类没有发布到控制器方法。我可以发布单个字段、表单集合或多个 DTO 对象,然后调用服务或存储库上的方法来获取我从表单发布中收到的信息,以及它需要查询或创建自身的信息,然后从所有这些,创建我可以保存的数据库对象。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(int id, [Bind(Exclude = "Id")] ClassA classA,
                        [Bind(Exclude = "Id")]ClassB classB)
{
   // Validation occurs here

   if(!ModelState.IsValid)
      return View();

   try
   {
      _someRepositoryOrService.Add(id, classA, classB);
      return RedirectToAction("Index", new { id = id });
   }
   catch(Exception ex)
   {
      // Logging and exception handling occurs here
   }
}


public void Add(int id, ClassA classA, ClassB classB)
{
    EntityA eA = new EntityA
    {
        // Set a bunch of properties using the two classes and
        // whatever queries are needed
    };

    EntityB eB = new EntityB
    {
        // Set a bunch of properties using the two classes and
        // whatever queries are needed
    };

    _entity.AddToEntityASet(eA);
    _entity.AddToEntityBSet(eB);
    _entity.SaveChanges();
}

我是在正确处理这个问题,还是在混淆框架?我从来没有直接使用实体对象,每当我查询一个实体对象时,我都会将我需要的信息放在 DTO 中,并以此为基础。创作也是如此。这是允许的,还是我避免使用实体直接违背了使用框架的目的?

编辑:我也担心这种方法,因为它需要空构造函数来正确执行 LINQ 查询,因为此错误消息:

只有无参数的构造函数和 LINQ 支持初始化器 实体。

这没什么大不了的,因为我很少需要构造函数中的逻辑,但是没有构造函数而只有公共属性是一个问题吗?

【问题讨论】:

  • Linq to Entities 使用的 objectcontext 使用反射从数据映射创建对象,因此它很重要并且要求所有实体都有一个无参数的构造函数,以便可以初始化它,如果你想能够要序列化您的类型,您也需要一个公共构造函数

标签: asp.net-mvc entity-framework crud dto


【解决方案1】:

我会说使用 DTO 并使用您自己的数据访问方法和业务层包装实体框架是一个很好的方法。您最终可能会编写大量代码,但它比假装 Entity Framework 生成的代码是您的业务层更好的架构。

这些问题并不一定以任何方式与 ASP.NET MVC 相关联。 ASP.NET MVC 基本上没有提供有关如何进行模型/数据访问的指导,而且 ASP.NET MVC 的大多数示例和教程都不是具有生产价值的模型实现,而实际上只是极少的示例。

看来你是在正确的轨道上,继续前进。

最后,您主要将 Entity Framework 用作代码生成器,它不会生成非常有用的代码,因此您可能需要研究更符合您要求的其他代码生成器或工具或框架。

【讨论】:

  • 我真的只是在使用实体框架来尝试一些新的东西。我习惯使用 nHibernate。您对我应该使用的更好的生成器或框架有什么建议吗?
  • 我不知道有什么东西非常适合您的需求。其中一些是商业的,一些是免费的。排名不分先后:T4 模板、CodeSmith、LLBLGen Pro。
  • 这基本上是一个非常好的答案 (+1),但我不同意他使用实体框架作为代码生成器。更重要的是他正在使用实体框架将他的数据库映射到对象空间。即使您从未真正实现实体类型,这也很重要;当您将它们投影到其他类型时,您仍然会在 LINQ to Entities 查询中使用映射实体。
  • 我并不是要暗示他不应该使用 Linq 或不应该使用 ORM。他应该在有意义的范围内做这两件事。有提供不涉及实体框架的 Linq/ORM 功能的替代解决方案。 LLBLGen Pro 就是一个很好的例子。 LLBLGen Pro 提供以上所有功能:Linq、ORM 和代码生成。我可以争辩说,我提到的其他两个工具可以与 ORM 互操作并满足所有三个需求(在我看来,在这种情况下比实体框架更好)。
【解决方案2】:

_someRepositoryOrService.Add(id, classA, classB);

我会说您将存储库与表示层结合在一起。这不应该。您的存储库应仅与实体一起使用。接下来,注意您的 Add 方法

public void Add(int id, ClassA classA, ClassB classB)

打破了关注点分离 (SoC)。它执行两个任务:

  1. 将视图数据映射到实体中
  2. 保存到存储库

显然第一步应该在表示层完成。考虑为此使用模型绑定器。它还可以帮助您解决构造函数问题,因为您的模型绑定器可以了解构造要求。

还可以查看 Jimmy Bogard(ASP.NET MVC In Action 的合著者)关于 ViewModels 的出色 post。这可能会帮助您自动映射。它还提出了一种相反的技术——让你的控制器与实体一起工作,而不是 ViewModels!自定义动作过滤器和模型绑定器确实是消除不属于控制器的例程的关键,而是视图和控制器之间的基础设施细节。例如,here 是我自动化实体检索的方式。 Here 是我看到控制器应该做什么的方式。

这里的目标是让控制器满足于管理业务逻辑,抛开所有不属于您的业务的技术细节。您在这个问题中谈论的是技术限制,您让它们泄漏到您的代码中。但是您可以使用 MVC 工具将基础架构级别迁移到它们。

更新:不,存储库不应该处理表单数据,这就是我所说的“与呈现相结合”的意思。是的,存储库在控制器中,但它们不适用于表单数据。您可以(不是您应该)使表单与“存储库数据”(即实体)一起使用,这就是大多数示例所做的,例如NerdDinner - 但不是相反。这是因为一般的经验法则 - 较高层可以与较低层耦合(表示与存储库和实体耦合),但决不应该将低层耦合到较高层(实体依赖于存储库,存储库依赖于表单模型等)。

第一步应该在存储库中完成,没错——除了从 ClassX 到 EntityX 的映射不属于该步骤。这是映射问题 - 基础设施。例如,请参阅this 关于映射的问题,但通常如果您有两个层(UI 和存储库),他们不应该关心映射 - 映射器服务/助手应该。除了 Jimmy 的博客,您还可以阅读 ASP.NET MVC In Action 或简单地查看他们的CodeCampServer,了解他们如何使用传递给控制器​​构造函数的 IEntityMapper 接口进行映射(请注意,与 Jimmy Bogard 的 AutoMapper 相比,这是一种更加手动且工作量更少的方法)。

还有一件事。阅读领域驱动设计,查找文章,从中学习,但您不必遵循所有内容。这些是指导方针,而不是严格的解决方案。看看你的项目能不能处理,看看你能不能处理,等等。尝试应用这些技术,因为它们通常是进行开发的优秀且被认可的方法,但不要盲目接受它们 - 沿途学习比应用你不了解的东西要好。

【讨论】:

  • 为了耦合存储库,我不确定你的意思,在控制器中拥有一个存储库似乎也是大多数教程所做的。至于第一步在表现层做,不应该在repository做吗?我认为存储库的目的是担心处理表单数据到数据库的转换?
  • 也感谢您提供的示例和建议。我还没有时间实际进行更改,但这是朝着正确方向的一个很好的推动。
  • 查看更新。我建议慢慢重构,例如先做 _realEntityRepository.Add(EntityMapper.From(classA)); (或classA.MapToEntity()),然后涉及自定义ModelBinder(这是一个大话题),然后添加IoC容器(另一个大话题),等等。
猜你喜欢
  • 2017-04-01
  • 1970-01-01
  • 2015-09-16
  • 2020-10-16
  • 2011-10-05
  • 2017-04-15
  • 1970-01-01
  • 1970-01-01
  • 2010-10-28
相关资源
最近更新 更多