【问题标题】:LINQ to entities - best practices for passing modelsLINQ to entity - 传递模型的最佳实践
【发布时间】:2010-07-12 12:57:31
【问题描述】:

众所周知,大多数应用程序都有数据访问层,通常使用存储库类。通常我们希望存储库使用 强类型 对象,例如

interface IUserRespository
{
     User GetUserByID(int id);
     void AddUser(User u);
     ... and so on
}

但是,有时我们希望对数据库进行更复杂的查询,这涉及到分组和聚合。例如,我们想要获取所有用户的所有订单的总值(结果集将只有两列:UserName 和 TotalAmount)。现在我的问题是:存储库方法会是什么样子? LINQ to entity 网上有几十个例子如何使用 sum 和 group by 进行查询,但所有这些例子都返回匿名类型。那么,我们的存储库如何返回这样的查询结果呢?另一个常见的答案是:将它包装在一个类中。所以该方法可能如下所示:

interface IUserRepository
{
    UserOrderTotal[] GetOrderTotalsForAllUsers();
}

UserOrderTotal 必须是一个小类,具有查询返回的两个属性:UserName 和 TotalAmount。此类将在我们的一个 .cs 文件中的某处定义。这真的是唯一的解决方案吗?它看起来很糟糕,因为它在主要实体模型(edmx)之外的某个地方引入了一个新的“临时”模型。理想情况下,我想用一个名为 TotalAmount 的新字段来丰富我的用户模型,该字段只会在此查询中填充,因此界面可能如下所示:

interface IUserRepository
{
    User[] GetOrderTotalsForAllUsers();
}

它会返回所有字段设置为默认值的用户实体,名称和总计金额除外。我遇到的问题是我无法使用未映射到数据库列的字段来编译模型。我收到错误错误 3004:从第 2226 行开始映射片段时出现问题:没有为 Set User 中的属性 User.TotalAmount 指定映射。在以下情况下,具有键(PK)的实体将不会往返:实体类型为 [FPSoMeterModel.User ]

我做错了吗?这个问题是微不足道的,还是我问错了问题?为每个涉及聚合的查询(!)创建一个包装类似乎有点荒谬。似乎整个 LINQ 的东西都鼓励人们不要使用多层架构,而是在呈现数据的同一层中构建查询......你们如何处理这个问题,你使用“存储库”类吗? LINQ,如果是 - 你如何返回复杂的查询结果?

【问题讨论】:

    标签: design-patterns linq-to-entities repository-pattern


    【解决方案1】:

    相反,我不会考虑定义一个新的UserOrderTotal 类不好的做法。有两种方法可以查看此类对象。

    (1) 您可以将此UserOrderTotal 类视为Data Transfer Object (DTO)。这样,它就不是您域的一部分。 DTO 通常用于通过网络传输实体或以扁平形式向表示层提供域对象。我一直在使用它们。在这种情况下,对象被用作数据的投影:作为视图。 MVVM 模式使用相同的原理,其中 MVVM 的 ViewModel 部分是用户界面特定的 DTO。

    (2) 您可以将此UserOrderTotal 类视为您域的一部分。从领域驱动设计的角度来看,对象可以是用户语言的一部分。在这种情况下,将其定义为域对象将是合乎逻辑的结果。但不同之处在于它不会在 de Entity Framework 的 (EF) edmx 文件中定义。我认为在 EF 中可以定义没有映射到数据库表的实体,但我不知道这是否让事情变得更容易。我个人认为在 edmx 所在的项目中定义这些域对象之外的 edmx 文件没有问题。

    关于返回 User 对象数组:这似乎是个坏主意。这种方法有几个问题。首先,您的代码没有很好地传达其意图。您使用 User 对象,但将所有属性留空。对于尝试使用此代码的任何人来说,这种做法都很难遵循。您实际上是在滥用 User 对象而不是它:即聚合。

    除此之外,您还注意到 EF 无法处理您添加到模型中的这个额外属性。虽然通常可以向 EF 模型添加属性(通过使用部分类),但您应该对此非常保守。当你在 LINQ 查询中使用这些属性时,虽然事情会编译,但事情会在运行时失败。

    为每个查询创建一个包装类 (!)涉及聚合似乎 有点可笑。

    我不这么认为。 IMO 这是正确的做法。这是在多层架构中使用 EF 的结果。我必须承认,在单层中使用 EF 要容易得多,但从长远来看,它会变得非常混乱。项目越大,需要维护的时间越长,添加更多抽象层是合理的。

    你们是怎么处理这个问题的,是吗? 在 LINQ 中使用“存储库”类

    我设计的应用程序通常有一个服务/业务层将读取与突变分开。对于读取操作/查询,我使用带有返回域对象或 DTO 集合的方法的静态类。通常这样的“服务方法”特定于用户界面的特定部分。突变包含在“服务命令”中。用户界面创建一个服务命​​令并触发它。命令表示单个用例或用户故事,是一个原子逻辑。服务层将确保在执行命令时创建数据库事务(在需要时)。对于用户界面,使用命令通常如下所示:

    void UpdateButton_Click(object sender, EventArgs e)
    {
        if (!this.Page.IsValid)
        {
            return;
        }
    
        var command = new UpdateUserSettingsCommand();
    
        command.UserName = this.UserName.Text;
        command.MailAddress = this.MailAddress.Text;
    
        command.Execute();
    }
    

    我希望这一切都有意义。如果您想了解更多关于我如何使用 DTO 的信息,请阅读 this SO answer of mine

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-22
      • 1970-01-01
      • 1970-01-01
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多