NHibernate QueryOver复杂查询

时间:2013-05-30 12:26:29

标签: c# nhibernate

我目前正在开发一个c#,MVC3,NHibernate(在MSSQL 2008上)应用程序。 我将提供一个简化的示例,因为我认为这是理解这一点的最佳方式。

型号:

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)

所以这引出了两个问题:

  1. 我是不是错了?存储过程是否会成为更好的解决方案?
  2. 如果这种方法很好,那么我如何按多个字段进行分组?
  3. 编辑:我想要生成的SQL类型的示例:

    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的第二个版本

    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
    

1 个答案:

答案 0 :(得分: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();