2013-05-30 99 views
0

我目前正在開發一個c#,MVC3,NHibernate(在MSSQL 2008)應用程序。 我會提供一個簡化的例子,因爲我認爲這是理解這一點的最好方法。NHibernate QueryOver複雜查詢

型號:

class Stock 
{ 
    public Company Company {get;set;} 
    public Product Product {get;set;} 
    public int quantity {get;set;} 
} 
class TransactionHeader 
{ 
    public Company FromCompany {get;set;} 
    public Company ToCompany {get;set;} 
    public IList<TransactionRow> Rows {get;set;} 
} 
class TransactionRows 
{ 
    public Product Product {get;set;} 
    public TransactionHeader Header {get;set;} 
    public int Quantity {get;set;} 
} 
class Company 
{ 
    public Country Country {get;set;} 
    public State State {get;set;} 
    public string Name {get;set;} 
} 

我的最終目標是產生含有列表:

產品名稱,公司名稱,國家名稱,國家名稱,當前庫存數量,進來的庫存數量,傳出庫存數量

我能爲當前股市與此查詢做到這一點:

var stockQuery = session.QueryOver<Stock>(() => s) 
    .JoinAlias<Company>(() => s.company,() => c, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
    .JoinAlias<State>(() => c.State,() => state, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
    .JoinAlias<Country>(() => c.Country,() => country, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
    .JoinAlias<Product>(() => s.product,() => p, NHibernate.SqlCommand.JoinType.InnerJoin, null); 
if (productId.HasValue) { stockQuery = stockQuery.Where(() => p.Id == productId); } 
var stock = stockQuery.SelectList(list => list 
      .Select(() => s.currentStock).WithAlias(() => rowVM.CurrentStock) 
      .Select(() => c.Description).WithAlias(() => rowVM.CompanyName) 
      .Select(() => state.Description).WithAlias(() => rowVM.StateName) 
      .Select(() => country.Description).WithAlias(() => rowVM.CountryName) 
      .Select(() => p.Description).WithAlias(() => rowVM.ProductName)) 
     .TransformUsing(Transformers.AliasToBean<StockLevelRowVM>()) 
     .List<StockLevelRowVM>(); 

我堅持的是如何添加基於TransactionHeader/TransactionRow數據的輸入和輸出股票。 這是我在一個查詢試圖讓進來的股票數量:

由多個屬性組似乎
  var incommingQuery = session.QueryOver<TransactionRow>(() => itr) 
       .JoinAlias<TransactionHeader>(() => itr.TransactionHeader,() => ith, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
       .JoinAlias<Company>(() => ith.ToCompany,() => c, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
       .JoinAlias<Product>(() => itr.Product,() => p, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
       .SelectList(list => list 
        .SelectGroup(() => new { ith.ToCompany, itr.Product }) 
        .Select(() => itr.Quantity).WithAlias(() => detail.Quantity) 
        .Select(() => p.Id).WithAlias(() => detail.ProductId) 
        .Select(() => c.Id).WithAlias(() => detail.CompanyId)) 

不工作:

Could not determine member from new <>f__AnonymousType3`2(ToCompany = value(<Stuff>).ith.ToCompany, Product = value(<Stuff>).itr.Product) 

因此,這導致我2個問題:

  1. 我對這件事情做錯了嗎?存儲過程會是一個更好的解決方案嗎?
  2. 如果這種方法很好,那麼我怎樣才能按多個字段進行分組?

編輯:

SELECT c.Description CompanyName, 
    p.Description ProductName, 
    cc.Description CountryName, 
    cs.Description StateName, 
    tmp.CurrentStock, 
    tmp.OutgoingStock, 
    tmp.IncommingStock 
FROM 
(
    SELECT results.CompanyId, 
     results.ProductId, 
     SUM(results.CurrentStock) CurrentStock, 
     SUM(results.IncommingStock) IncommingStock, 
     SUM(results.OutgoingStock) OutgoingStock 
    FROM (
     SELECT 
      c.Id CompanyId, 
      p.Id ProductId, 
      s.CurrentStock, 
      0 IncommingStock, 
      0 OutgoingStock 
     FROM Stock s 
     INNER JOIN Company c ON c.Id = s.Company 
     INNER JOIN [State] cs ON cs.Id = c.[State] 
     INNER JOIN Country cc ON cc.Id = cs.Country 
     INNER JOIN Product p ON p.Id = s.Product 
     WHERE (@CompanyId IS NULL OR c.Id = @CompanyId) AND 
      (@CountryId IS NULL OR cc.Id = @CountryId) AND 
      (@StateId IS NULL OR cs.Id = @StateId) AND 
      (@ProductId IS NULL OR p.Id = @ProductId) 
     UNION 
     SELECT 
      c.Id CompanyId, 
      p.Id ProductId, 
      0 CurrentStock, 
      0 IncommingStock, 
      tr.Quantity OutgoingStock 
     FROM TransactionRow tr 
     INNER JOIN TransactionHeader th ON th.Id = tr.TransactionHeader 
     INNER JOIN Company c ON c.Id = th.FromCompany 
     INNER JOIN [State] cs ON cs.Id = c.[State] 
     INNER JOIN Country cc ON cc.Id = cs.Country 
     INNER JOIN Product p ON p.Id = tr.Product 
     WHERE (@CompanyId IS NULL OR c.Id = @CompanyId) AND 
      (@CountryId IS NULL OR cc.Id = @CountryId) AND 
      (@StateId IS NULL OR cs.Id = @StateId) AND 
      (@ProductId IS NULL OR p.Id = @ProductId) 
     UNION 
     SELECT 
      c.Id CompanyId, 
      p.Id ProductId, 
      0 CurrentStock, 
      tr.Quantity IncommingStock, 
      0 OutgoingStock 
     FROM TransactionRow tr 
     INNER JOIN TransactionHeader th ON th.Id = tr.TransactionHeader 
     INNER JOIN Company c ON c.Id = th.ToCompany 
     INNER JOIN [State] cs ON cs.Id = c.[State] 
     INNER JOIN Country cc ON cc.Id = cs.Country 
     INNER JOIN Product p ON p.Id = tr.Product 
     WHERE (@CompanyId IS NULL OR c.Id = @CompanyId) AND 
      (@CountryId IS NULL OR cc.Id = @CountryId) AND 
      (@StateId IS NULL OR cs.Id = @StateId) AND 
      (@ProductId IS NULL OR p.Id = @ProductId) 
    ) results 
    GROUP BY results.CompanyId, results.ProductId 
) tmp 
INNER JOIN Company c ON c.Id = tmp.CompanyId 
INNER JOIN [State] cs ON cs.Id = c.[State] 
INNER JOIN Country cc ON cc.Id = cs.Country 
INNER JOIN Product p ON p.Id = tmp.CompanyId 

編輯2:排序SQL我想產生的實例所需的SQL的第二個版本

SELECT 
    c.Id CompanyId, 
    c.Description CompanyName, 
    p.Id ProductId, 
    p.Description ProductName, 
    cs.Description StateName, 
    cc.Description CountryName, 
    SUM(s.CurrentStock) CurrentStock, 
    SUM(ttr.Quantity) IncommingStock, 
    SUM(ftr.Quantity) OutgoingStock 
FROM Stock s 
INNER JOIN Company c ON c.Id = s.Company 
INNER JOIN [State] cs ON cs.Id = c.[State] 
INNER JOIN Country cc ON cc.Id = cs.Country 
INNER JOIN Product p ON p.Id = s.Product 
LEFT JOIN TransactionHeader fth ON fth.FromCompany = c.Id 
LEFT JOIN TransactionRow ftr ON ftr.TransactionHeader = fth.ID 
    AND ftr.Product = p.Id 
LEFT JOIN TransactionHeader tth ON tth.ToCompany = c.Id 
LEFT JOIN TransactionRow ttr ON ttr.TransactionHeader = tth.ID 
    AND ttr.Product = p.Id 
WHERE (@CompanyId IS NULL OR c.Id = @CompanyId) AND 
    (@CountryId IS NULL OR cc.Id = @CountryId) AND 
    (@StateId IS NULL OR cs.Id = @StateId) AND 
    (@ProductId IS NULL OR p.Id = @ProductId) 
GROUP BY p.Id, c.Id, c.Description, p.Description, 
    cs.Description, cc.Description 
+0

你能不能表現出你希望生成SQL?以這種方式提出QueryOver會更容易。 –

+0

@AndrewWhitaker我沒有任何SQL可以繼續,但敲了一些並將其添加到問題。它可能不是100%正確的,但應該有希望傳達我想要實現的目標。 – Steve

+0

嗯,你無法使用QueryOver來執行'UNION'或者從表格中選擇('FROM(SELECT ...)')。 –

回答

0

非常感謝安德魯,誰促使我寫出SQL,從而找出這個問題。

有幾件事情我是缺少安裝明智:

  1. 公司需要兩個額外的屬性:

    public virtual IList<TransactionHeader> TransactionsFromCompany { get; set; } 
    public virtual IList<TransactionHeader> TransactionsToCompany { get; set; } 
    
  2. 我的自動映射需要被重寫將它們映射到相應的列:

    .Override<Company>(m => 
    { 
        m.HasMany(c => c.TransactionsFromCompany) 
         .KeyColumn("FromCompany"); 
        m.HasMany(c => c.TransactionsToCompany) 
         .KeyColumn("ToCompany"); 
    } 
    
  3. 現在我可以寫查詢:

    StockLevelRowVM rowVM = null; 
    Stock s = null; 
    Company c = null; 
    State state = null; 
    Country country = null; 
    Product p = null; 
    TransactionHeader ith = null; 
    TransactionRow itr = null; 
    TransactionHeader oth = null; 
    TransactionRow otr = null; 
    
    var stockQuery = session.QueryOver<Stock>(() => s) 
        .JoinAlias<Company>(() => s.company,() => c, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
        .JoinAlias<State>(() => c.State,() => state, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
        .JoinAlias<Country>(() => c.Country,() => country, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
        .JoinAlias<Product>(() => s.product,() => p, NHibernate.SqlCommand.JoinType.InnerJoin, null) 
        .JoinAlias<TransactionHeader>(() => c.TransactionsFromCompany,() => oth, NHibernate.SqlCommand.JoinType.LeftOuterJoin, null) 
        .JoinAlias<TransactionRow>(() => oth.Rows,() => otr, NHibernate.SqlCommand.JoinType.LeftOuterJoin, null) 
        .JoinAlias<TransactionHeader>(() => c.TransactionsToCompany,() => ith, NHibernate.SqlCommand.JoinType.LeftOuterJoin, null) 
        .JoinAlias<TransactionRow>(() => ith.Rows,() => itr, NHibernate.SqlCommand.JoinType.LeftOuterJoin, null); 
    if (productId.HasValue) { stockQuery = stockQuery.Where(() => p.Id == productId); } 
    if (companyId.HasValue) { stockQuery = stockQuery.Where(() => c.Id == companyId); } 
    if (stateId.HasValue) { stockQuery = stockQuery.Where(() => state.Id == stateId); } 
    if (countryId.HasValue) { stockQuery = stockQuery.Where(() => country.Id == countryId); } 
    
    <call generic paging methods for IQueryOver> 
    
    result = stockQuery.SelectList(list => list 
          .SelectGroup(() => c.Id) 
          .SelectGroup(() => p.Id) 
          .SelectGroup(() => c.Description).WithAlias(() => rowVM.CompanyName) 
          .SelectGroup(() => state.Description).WithAlias(() => rowVM.StateName) 
          .SelectGroup(() => country.Description).WithAlias(() => rowVM.CountryName) 
          .SelectGroup(() => p.Description).WithAlias(() => rowVM.ProductName) 
          .SelectSum(() => s.currentStock).WithAlias(() => rowVM.CurrentStock) 
          .SelectSum(() => otr.Quantity).WithAlias(() => rowVM.OutgoingStock) 
          .SelectSum(() => itr.Quantity).WithAlias(() => rowVM.IncommingStock)) 
         .TransformUsing(Transformers.AliasToBean<StockLevelRowVM>()) 
         .List<StockLevelRowVM>().ToList();