2014-05-15 89 views
9

我从移植子系统NHibernate的实体框架,并希望看到的最好方式端口下面的查询EF子查询与实体框架

var date = DateTime.Now; // It can be any day 
AccountBalanceByDate abbd = null; 
var lastBalanceDateByAccountQuery = QueryOver.Of<AccountBalanceByDate>() 
    .Where(x => x.AccountId == abbd.AccountId && x.Date < date) 
    .Select(Projections.Max<AccountBalanceByDate>(x => x.Date)); 

var lastBalances = session.QueryOver<AccountBalanceByDate>(() => abbd) 
    .WithSubquery.WhereProperty(x => x.Date).Eq(lastBalanceDateByAccountQuery) 
    .List(); 

帐户余额类是:

public class AccountBalanceByDate 
{ 
    public virtual int Id { get; set; } 
    public virtual int AccountId { get; set; } 
    public virtual DateTime Date { get; set; } 
    public virtual decimal Balance { get; set; } 
} 

该表是:

CREATE TABLE [dbo].[AccountBalanceByDate] 
(
    [Id]  int NOT NULL, 
    [AccountId] int NOT NULL, 
    [Date]  [datetime] NOT NULL, 
    [Balance] [decimal](19, 5) NOT NULL, 

    PRIMARY KEY CLUSTERED 
    (
     [Id] ASC 
    ) 
) 

的采样数据被(使用数字ID为了更好地理解):

Id | Date  | Account | Balance 
------------------------------------ 
1 | 2014-02-01 | 101  | 1390.00000 
2 | 2014-02-01 | 102  | 1360.00000 
3 | 2014-02-01 | 103  | 1630.00000 
4 | 2014-02-02 | 102  | 1370.00000 
5 | 2014-02-02 | 103  | 1700.00000 
6 | 2014-02-03 | 101  | 1490.00000 
7 | 2014-02-03 | 103  | 1760.00000 
8 | 2014-02-04 | 101  | 1530.00000 
9 | 2014-02-04 | 102  | 1540.00000 

The AccountBalanceByDate实体在特定日期持有账户余额。如果某一天没有交易,那一天将不会有AccountBalanceByDate,我们应该查看前几天以查看该帐户的余额。

如果我有日期2014年2月1日查询我应该得到:

No results 

如果我与日期查询2014年2月2日我应该得到:

1 | 2014-02-01 | 101  | 1390.00000 
2 | 2014-02-01 | 102  | 1360.00000 
3 | 2014-02-01 | 103  | 1630.00000 

如果我与日期查询2014年2月3日我应该得到:

1 | 2014-02-01 | 101  | 1390.00000 
4 | 2014-02-02 | 102  | 1370.00000 
5 | 2014-02-02 | 103  | 1700.00000 

如果我有日期2014年2月4日查询我应该得到:

4 | 2014-02-02 | 102  | 1370.00000 
6 | 2014-02-03 | 101  | 1490.00000 
7 | 2014-02-03 | 103  | 1760.00000 

如果我与日期查询2014-02-05我应该得到:

7 | 2014-02-03 | 103  | 1760.00000 
8 | 2014-02-04 | 101  | 1530.00000 
9 | 2014-02-04 | 102  | 1540.00000 

我可以在Entity Framework中使用原始SQL执行此操作,但它并不理想。

using (var context = new DbContext()) 
{ 
    var lastBalances = context.AccountBalanceByDate.SqlQuery(
     @"SELECT 
      * 
     FROM 
      [AccountBalanceByDate] AB 
     WHERE 
      DATE = (
       SELECT 
        MAX(Date) 
       FROM 
        [AccountBalanceByDate] 
       WHERE 
        AccountId = AB.AccountId AND DATE < @p0 
      )", date).ToList(); 
} 

最好是去数据库只是一个时间,像NHibernate的和原始SQL,但只使用LINQ ,这可能吗?

UPDATE:

在问题修复的结果。

SQL表示上GIST示例查询:https://gist.github.com/LawfulHacker/275ec363070f2513b887

上GIST实体框架样品:https://gist.github.com/LawfulHacker/9f7bd31a21363ee0b646

+0

自己的SQL查询不会产生你在你的问题给出如下结果!查询2014-02-01不会返回任何结果!首先修复你的查询。 –

+0

@AgentShark,请注意,我有一个引用顶部表的子查询,它为每一行都执行该操作。然后,为每一行获取其帐户ID并获取最大日期。它将所有结果返回给每个帐户的最大日期,每个帐户的最大日期可能不同。这是个问题,我的样本是正确的,当你指出它不正确时,我再次检查它。 – LawfulHacker

+0

我得到你想要做的,但我在SQL Server 2008中输入了你的确切数据并运行了你的查询。 '2014-02-01'不会返回任何内容。 '2014-02-02'返回Ids 3,2,1。 '2014-02-03'返回Ids 5,4,1。 '2014-02-04'返回Ids 7,4,6​​。 –

回答

18

下面的查询做的正是我需要的只有一个数据库查询:

var accountBalance = context 
    .AccountBalanceByDate 
    .Where(a => 
     a.Date == context.AccountBalanceByDate 
      .Where(b => b.AccountId == a.AccountId && b.Date < date).Max(b => b.Date)); 

感谢@AgentShark的帮助。

的代码是GIST:https://gist.github.com/LawfulHacker/9f7bd31a21363ee0b646

3

最后,一个解决方案。:)

var date = DateTime.Now; // It can be any day 
var lastBalances = (from a in context.AccountBalanceByDate 
     where a.Date < date 
     group a by new {a.AccountId} into g 
     select g.OrderByDescending(a => a.Date).FirstOrDefault() into r 
     select new 
     { 
      Id = r.Id, 
      AccountId = r.AccountId, 
      Date = r.Date, 
      Balance = r.Balance 
     }).ToList(); 

你想在LINQ,但个人而言,我可能会保持SQL的可维护性。

+0

我用您的解决方案更新了要点示例,请注意它仍然不正确。的确,我非常感谢你的帮助。 – LawfulHacker

+0

@SergioGarcia我不得不不同意你的先生,我从SQL中返回这个LINQ匹配ID。我只是硬编码的日期。我在VS2013中根据您的测试数据对此进行了测试。 –

+0

请检查要点。但我找到了一个解决方案。 – LawfulHacker