使用方法在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
from o in this.Session.Query<Order>
select new { Amount = o.OrderAmount, Customer = o.Customer.GetCard() };
目前你所要求的东西是不容易实现的,因为处理投影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
})
};