使用方法在LINQ中投影嵌套的DTO

时间:2013-04-21 12:20:28

标签: c# nhibernate linq-to-nhibernate

我们假设我们有以下实体:

public class Customer
{
    public virtual int Id { get; set; }

    public virtual string FirstName { get; set; }

    public virtual string LastName { get; set; }
}

此外还有以下扩展方法:

public static string GetCard(this Customer @this)
   {
         throw new InvalidOperationException("Use only in IQueryable");
   }

现在,我想执行此查询:

var q = from c in this.Session.Query<Customer>()
        select new { Id = c.Id, InfoCard = c.GetCard() };

我虽然在hql生成器之后创建和注册就足够了:

class GetCardGenerator : BaseHqlGeneratorForMethod
{
    public GetCardGenerator()
    {
        SupportedMethods = new[]
        {
            ReflectionHelper.GetMethodDefinition(() => CustomerExtensions.GetCard(null))
        };
    }

    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        Expression<Func<Customer, Card>> definition = c => new Card
            {
                FirstName = c.FirstName,
                LastName = c.LastName,
                FullName = c.FirstName + " " + c.LastName
            };

        return visitor.Visit(definition);
    }
}

不幸的是,抛出消息MemberInit会引发异常NotSupportedException。在投资期间,我在Linq \ Visitors \ SelectClauseHqlNominator.cs中发现了HQL不支持New和MemberInit表达式的评论。

我的问题是:是否可以创建将在LINQ查询的select子句中使用的方法,并将用于创建和填充DTO对象?

更新:我不想制作IQueryable&lt; Card&gt;来自IQueryable&lt; Customer&gt;但是我正在寻找允许我在任何地方从客户那里提取卡片的解决方案,例如我有一个引用Customer的Order实体,我想调用类似的查询:

from o in this.Session.Query<Order>
select new  { Amount = o.OrderAmount, Customer = o.Customer.GetCard() };

1 个答案:

答案 0 :(得分:4)

目前你要求的东西并不容易实现,因为要处理预测NHibernate使用除HQL之外的ClientSideResultOperator(参见QueryModelVisitor .VisitSelectClause methd)

例如,对于以下查询

var q = from c in this.Session.Query<Customer>()
        select new Card
        {
            FirstName = c.FirstName,
            LastName = c.LastName,
            FullName = c.FirstName + " " + c.LastName
        };

NHibernate将投影转换为基于数组的投影

var q = from c in this.Session.Query<Customer>()
        select new object[]
        {
            c.FirstName,
            c.LastName,
            c.FirstName + " " + c.LastName
        };

并添加以下转换结果

Expression<Func<object[], Card>> projectionExpression = array => new Card
{
    FirstName = (string)array[0],
    LastName = (string)array[1],
    FullName = (string)array[2]
};

可能仅适用于以下简单案例:

var q = from c in this.Session.Query<Customer>()
        select c.GetCart();

替代解决方案

我可以建议预处理查询表达式为内联GetCart方法。您可以手动执行,也可以使用DelegateDecompiler DelegateDecompiler 具有扩展方法.Decompile(this IQueryable<T> self),该方法查找用[Decomile][Computed]属性标记的方法和属性的表达式树,并内联这些方法和属性。

所以你可以关注

var q = (from c in this.Session.Query<Customer>()
         select new { Id = c.Id, InfoCard = c.GetCard() }).Decompile();

[Decompile]
public static string GetCard(this Customer @this)
{
    return new Card
        {
            FirstName = @this.FirstName,
            LastName = @this.LastName,
            FullName = @this.FirstName + " " + @this.LastName
        };
}

查询将转换为

var q = from c in this.Session.Query<Customer>()
        select new 
        { 
            Id = c.Id, 
            InfoCard = new Card
            {
                FirstName = c.FirstName,
                LastName = c.LastName,
                FullName = c.FirstName + " " + c.LastName
            }) 
        };