我们假设我们有以下实体:
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() };
答案 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
})
};