我在尝试在NHibernate QueryOvers中重用ProjectionLists时遇到问题。我无法解决如何为不同的根实体重用事物。
对象模型大致表示为:
早餐一对多糕点多对零或一咖啡
两个单独的查询大致是:
session.QueryOver<Breakfast>()
.Where(b => b.Id == searchId)
.Inner.JoinQueryOver(b => b.Pastries, () => pastry)
.Left.JoinAlias(p => p.Coffee, () => coffee)
.Select(projections)
.TransformUsing(Transformers.AliasToBean<TargetDto>())
.List<TargetDto>();
session.QueryOver<Coffee>()
.Where(c => c.Id == searchId)
.Inner.JoinQueryOver(c => c.Pastries, () => pastry)
.Select(projections)
.TransformUsing(Transformers.AliasToBean<TargetDto>())
.List<TargetDto>();
我尝试重用的常见预测如下:
var projections = Projections.ProjectionList()
.Add(Projections.Property(() => pastry.Name, () => target.PastryName))
.Add(Projections.Property(() => coffee.Name, () => target.CoffeeName));
使用别名的这些投影适用于第一个查询(root:Breakfast),因为它们并不试图拉出该根实体上的属性。在第二个查询(根:咖啡)上,它爆炸说它无法找到咖啡。名称&#39;在咖啡上,因为它不喜欢别名。 QueryOver(()=&gt;咖啡)语法没有帮助,因为它实际上并没有注册咖啡&#39;作为一个别名,它只是用它来进行类型推断。哦,血腥的地狱就是问题所在。这是一个愚蠢的应用程序基础架构,它打破了别名语法,实际上并没有使用下面的别名版本。
第二个查询希望投影看起来像:
var projections = Projections.ProjectionList()
.Add(Projections.Property(() => pastry.Name, () => target.PastryName))
.Add(Projections.Property<Coffee>(c => c.Name, () => target.CoffeeName));
但是现在这与第一个查询不兼容。
有没有办法使这项工作,所以我可以在不知道根实体类型是什么的情况下投影属性?
答案 0 :(得分:1)
我认为您需要做的就是在coffee
电话中分配session.QueryOver<Coffee>
别名:
Coffee coffee = null;
session.QueryOver<Coffee>(() => coffee)
/* etc */
以下内容可能与您正在做的事情完全无关,但我想我会包含它,以防其他人编写传递QueryOver别名的代码。
我要谨慎一点 - 在不同的查询中重复使用别名可能会有点危险。
NHibernate接受表达式() => coffee
并从表达式中获取您正在使用的别名的名称(在本例中为"coffee"
),然后在生成的SQL中将其用作别名。这意味着,根据代码的结构,如果别名更改,这样的共享投影可能会中断。
例如,假设您有以下方法来返回一些共享投影:
public ProjectionList GetSharedProjections()
{
Coffee coffee = null;
TargetDTO target;
var projections = Projections.ProjectionList()
.Add(Projections.Property(() => coffee.CoffeeName)
.WithAlias(() => target.CoffeeName));
return projections;
}
然后你有一些代码调用你的帮助方法:
session.QueryOver<Coffee>(() => coffee)
.Select(GetSharedProjections());
只要您的别名匹配,一切都会正常工作。第二个人改变了任何一个别名,但查询将失败。
您可能想要将别名传递给这样的方法:
public ProjectionList GetSharedProjections(Coffee coffeeAlias)
{
/* Same as above except with "coffeeAlias"
}
然后传入你的别名:
session.QueryOver<Coffee>(() => coffee)
.Select(GetSharedProjections(coffee));
但这也行不通。请记住,NHibernate正在获取别名的名称,并直接在生成的SQL中使用它。上面的代码会尝试在生成的SQL中同时使用"coffee"
和"coffeeAlias"
,并且会失败。
正确执行此操作的一种方法(不希望没有人更改别名)是传递表达式并使用这些表达式使用正确的别名重建属性名称。
您将创建一个帮助方法,使用别名和属性构建属性访问:
public static PropertyProjection BuildProjection<T>(
Expression<Func<object>> aliasExpression,
Expression<Func<T, object>> propertyExpression)
{
string alias = ExpressionProcessor.FindMemberExpression(aliasExpression.Body);
string property = ExpressionProcessor.FindMemberExpression(propertyExpression.Body);
return Projections.Property(string.Format("{0}.{1}", alias, property));
}
然后,您可以更改GetSharedProjections
方法以表达式的形式获取别名:
public ProjectionList GetSharedProjection(Expression<Func<Coffee>> coffeeAlias)
{
TargetDTO target = null;
var projections = Projections.ProjectionList()
.Add(BuildProjection<Coffee>(coffeeAlias, c => c.CoffeeName))
.WithAlias(() => target.CoffeeName);
}
现在,调用您的方法将如下所示:
session.QueryOver<Coffee>(() => coffee)
.Select(GetSharedProjections(() => coffee));
当有人更改您的别名时,您就会被覆盖。您还可以在许多查询中安全地使用此方法,而无需担心别名变量的名称实际上是什么。
免责声明:以下是我个人博客的链接
您可以通过here这样的方式找到有关构建QueryOver查询的详细信息。