【问题标题】:Expanding properties for an IQueryable query扩展 IQueryable 查询的属性
【发布时间】:2010-07-23 10:13:32
【问题描述】:

我有以下虚构的域类:

public class Pony
{
    public string Name { get; set; }
    public DateTime FoundDate { get; set; }
}

public class Person
{
    public ICollection<Pony> Ponies { get; private set; }

    public Pony NewestPony
    {
        get
        {
            return Ponies
                .OrderBy(pony => pony.FoundDate)
                .FirstOrDefault();
        }
    }
}

NewestPony 属性封装了一个域规则,该规则确定幸运者找到的最新小马。

我可以使用Enumerable 扩展方法来使用这个属性:

IEnumerable<Person> people = GetPeople();
people.Where(p => p.NewestPony != null);

这太棒了。

但是,people 是 SQL Server(在本例中为 NHibernate)的 IQueryable 外观,查询必须由查询提供程序处理并转换为 SQL。因为数据库中没有物理的“NewestPony”列,所以查询提供程序会阻塞查询。

为了解决这个问题,我可以扩展查询中的属性,将其替换为 get 实现:

IEnumerable<Person> people = GetPeople();
person.Where(p 
    => p.Ponies.OrderBy(pony => pony.FoundDate).FirstOrDefault() != null);

由于查询提供程序了解 SQL Server 中的关系,它现在可以评估此​​查询。

此解决方案虽然可行,但确实复制了之前封装的规则。我正在寻找一种避免这种重复行为的方法,甚至可能是一种扩展查询提供程序中的属性以允许它生成正确的 SQL 语句的方法。

【问题讨论】:

  • 您对该属性的映射设置是什么?因为在 NHibernate 中,如果您不映射某个属性,它将不会使用该属性为该属性生成 SQL。
  • 该属性没有映射;表中没有可将其映射到的列。
  • 你真的在使用 Linq to NHibernate 吗? ayende.com/Blog/archive/2009/07/26/…ayende.com/Blog/archive/2007/03/17/…。如果不是,请使用 ICriteria,或更改并使用 Linq2NH。
  • 我绝对在使用 LINQ to NHibernate。我不相信提供者有能力将派生成员扩展为表达式。 NHibernate 的 LINQ 提供者虽然有点偶然。我知道的所有提供商都会出现这个问题。
  • 虽然我一直都在毫不费力地使用它,但我将尝试模拟一个示例来说明您的错误(因为在我的情况下分离关注点可能会模糊我的视野)。如果您碰巧有一个说明问题的小示例项目,请务必在线发布它,我会从那里拿走,我可以看看。

标签: c# linq iqueryable


【解决方案1】:

您在这里遇到了一个典型的问题。有几种方法可以解决这个问题。如果您使用一些自动映射,则应排除该属性,如果您使用法线映射(HBM 文件),则可以忽略该属性。但是对于 LINQ 语句,正如您所解释的那样,它可能仍然会出错。以下是一些解决方法:

  1. 务实:您对 POCO 的所有扩展都应该是方法,而不是属性。这反映了你在做什么:一个返回一些东西的动作。因此,使用Pony GetNewestPony() 作为声明。同样,我在 POCO 中使用 GetFullName() 解决了这个问题,它结合了名字、中间名和姓氏。
  2. 设计:最好让您的 POCO 简单明了。这样它们就可以自动生成并且可以在不伤害任何人的情况下进化。只是gettors/settors,就是这样。然后解决您的问题:使用 3.0 中引入的 C#:扩展方法(不,扩展属性不存在)。
  3. 架构:通常将 DAO 层作为独立层放置在与域层不同的组件中。这样,业务逻辑(获取选择、全部、更新、所有 CRUD 等)都可以放在自己的 DAO 类中。要做到这一点,您需要过度使用和理解 C# 接口和泛化。这个概念是explained clearly and detailed by Billy McCafferty,后来演变成现在著名的S#arp Architecture

要快速修复:使用 nr 1 或 2。要进行彻底的面向未来的修复,请更改您的架构,否则 NHibernate 会在后面咬你,而不是帮助你。

编辑:
下面,adriaanp 谈到了使用扩展方法的一个痛点,我相信他是对的。因为我几乎总是使用自动生成的 DTO,所以我希望它们与我手动引入的任何方法完全分开。如果我希望它们在对象上,而不是作为扩展方法,Microsoft 正是为此目的引入了partial 类(在类本身中扩展自动生成的类,而不会丢失往返工程)。但这里也是如此:小心不要弄乱,清晰的文件名会有很大帮助。

【讨论】:

  • 我会非常小心#2。您最终可能会得到很多可能变成维护噩梦的扩展方法。在我看来,POCO 也应该包含行为,它们不仅仅是 DTO。
  • 1 & 2:查询提供者不能比属性更容易地解释方法或扩展;它仍然会尝试将它们转换为 SQL 语句。
  • @Programming Hero:我从未见过这种情况发生,尤其是。不使用扩展方法,因为它们绝不是类本身的一部分。我基本上使用方法 nr 3,有时使用扩展方法作弊(不会让它变得混乱,但它们通常最终会在更稳定的类中结束)并且不会遇到你描述的问题。也许我在这里遗漏了您的设计中与生俱来的一些不同之处?
  • 据我了解,查询提供程序将收到查询中方法的MethodCallExpression。然后它必须尝试评估该方法调用并将其表达为适当的 SQL 语句,这是它在派生属性上阻塞的点。
【解决方案2】:

这并不能完全解决您的问题,但也许会有所帮助。 Using an NHibernate Formula to aid searching

【讨论】:

  • 它不会直接解决我眼前的问题,但我可以看到这对于更简单的场景非常有用。
猜你喜欢
  • 2010-11-14
  • 1970-01-01
  • 2016-06-27
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
  • 2011-01-29
  • 2015-05-02
  • 2011-12-18
相关资源
最近更新 更多