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

本文关键字:嵌套 dto 投影 LINQ 使用方法 | 更新日期: 2023-09-27 18:10:23

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

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);
    }
}

不幸的是,异常NotSupportedException抛出消息MemberInit。在调查过程中,我发现Linq'Visitors' selectclausehql提名人.cs的评论,HQL不支持New和MemberInit表达式。

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

UPDATE:我不想让IQueryable从IQueryable但我正在寻找解决方案,允许我从客户提取卡在任何地方,例如,我有订单实体与客户的参考,我想调用查询如下:

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

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

目前你所要求的东西是不容易实现的,因为处理投影NHibernate使用ClientSideResultOperator额外的HQL(见QueryModelVisitor .VisitSelectClause方法)

查询

的示例
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方法。你可以手动完成,或者使用delegatedecomcompiler。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
            }) 
        };