LINQ to entities - 传递模型的最佳实践

时间:2010-07-12 12:57:32

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

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

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

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

interface IUserRepository
{
    UserOrderTotal[] GetOrderTotalsForAllUsers();
}

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

interface IUserRepository
{
    User[] GetOrderTotalsForAllUsers();
}

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

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

1 个答案:

答案 0 :(得分: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

我希望这会有所帮助。