2013-04-21 22 views
2

讓我們假設我們有以下實體:使用方法,以項目嵌套的DTO在LINQ

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 \ SelectClauseHqlNominator.cs中發現了HQL不支持New和MemberInit表達式的評論。

我的問題是:是否有可能創建將用於LINQ查詢的select子句的方法,並將用於創建和填充DTO對象?

更新:我不想從IQueryable的使IQueryable的<卡> <客戶>但是我正在尋找解決方案,使我從中提取客戶卡在任何地方,例如我有Order實體參照客戶,我想調用查詢,如:

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

回答

4

目前你所要求的事情是不容易實現,因爲處理的預測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] 
}; 

可能僅適用於以下情況simplier工作

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

替代解決方案

我可以建議預將查詢表達式處理爲內聯GetCart方法。您可以手動執行此操作,也可以使用DelegateDecompilerDelegateDecompiler具有擴展方法.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 
      }) 
     }; 
+0

我做的這種方式,但它不好玩寫同一塊在第三位的代碼; ) – Novakov 2013-04-21 13:22:04

+0

@Novakov查看更新。希望這會有所幫助。我會盡量在稍後回答原始問題。 – hazzik 2013-04-21 13:38:19

+0

我已更新問題以更好地顯示我在找什麼 – Novakov 2013-04-21 13:51:04