扩展IQueryable查询的属性

时间:2010-07-23 10:13:33

标签: c# linq iqueryable

我有以下虚构的域类:

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语句的方法。

2 个答案:

答案 0 :(得分:1)

你遇到了一个典型的问题。有几种方法可以解决这个问题。如果使用某些自动化,则应排除该属性,如果使用普通映射(HBM文件),则可以忽略该属性。但是,根据LINQ声明,您可能仍然会出错,正如您所解释的那样。以下是一些解决方法:

  1. 务实:您对POCO的所有扩展都应该是方法,而不是属性。这反映了你在做什么:一个返回一些东西的动作。因此,使用Pony GetNewestPony()作为声明。同样地,我在POCO中使用GetFullName()解决了这个问题,partial结合了名字,姓名,中间名。
  2. 设计:最好保持您的POCO简单明了。这样他们就可以自动生成并且可以在不伤害任何人的情况下进化。只是gettors / settors就是这样。要解决您的问题,请:使用3.0中引入的C#:扩展方法(不,扩展属性不存在)。
  3. 体系结构:通常将DAO图层作为单独的图层放在与“层”图层分开的组件中。这样,业务逻辑(获取选择,全部,更新,所有CRUD等)都可以进入自己的DAO类。为了做到这一点,您需要过度使用和理解C#接口和泛化。这个概念是explained clearly and detailed by Billy McCafferty,后来发展成现在着名的S#arp Architecture
  4. 快速解决方法:使用nr 1或2.对于一个彻底的未来修复,改变你的架构,否则NHibernate会在后面咬你,而不是帮助你。

    修改
    下面,adriaanp使用扩展方法的痛点,我相信他是对的。因为我几乎总是使用自动生成的DTO,所以我希望它们与我手工介绍的任何方法完全分开。如果我想在对象上使用它们,而不是作为扩展方法,Microsoft为此目的引入了{{1}}类(在类本身中扩展自动生成的类,而不会丢失往返工程)。但是在这里:小心不要弄乱它,明确的文件名可以帮助很多。

答案 1 :(得分:1)

这不会完全解决您的问题,但也许可以提供帮助。 Using an NHibernate Formula to aid searching