在属性中定义LAMBDA查询,以便可以重用它

时间:2018-06-09 13:22:44

标签: c# lambda entity-framework-6 ef-code-first

我正在尝试在我的代码第一个EF模型的属性中定义一个lambda查询,如下所示GetLatestTransaction

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual List<TransactionModel> Transactions { get; set; }

    public TransactionModel GetLatestTransaction { 
        get {
            return Transactions.OrderByDescending(x => x.Created).FirstOrDefault();
        }
    }
}

这样做的原因是我不想在很多地方重新输入这个查询,并且让它在一个地方减少错误的可能性。

我想在这样的查询中使用它:

var user = _DB.Users
.Select(u => new UserDetailsView()
{
    Id = u.Id,
    FirstName= u.FirstName,
    LastName= u.LastName,
    Balance = u.GetLatestTransaction.ValueResult
}).FirstOrDefault(x => x.Id == userId);

然而,这会导致此错误:

  

System.NotSupportedException:'LINQ to Entities中不支持指定的类型成员'GetLatestTransaction'。仅支持初始值设定项,实体成员和实体导航属性。'

是否有某种方法可以实现这一目标,而无需在用户上存储与最新交易的其他关系,并且每次有新交易时都必须更新它?

编辑:我还想像上面那样做,以避免对数据库进行另一次查询,我希望一次性完成以提高性能。

1 个答案:

答案 0 :(得分:2)

您的ApplicationUser类表示数据库中的表。它不代表表中数据的用法。

相当多的人认为将数据库结构与数据的使用分开是一种好习惯。这种分离通常使用存储库模式完成。存储库是数据库内部数据结构的抽象。它允许您向类添加功能,而无需在与数据库通信的控件类中要求此功能。

有很多关于存储库的文章。 This one帮助我理解了我应该在实体框架类中以及存储库中的哪些功能。

所以你需要一个代表数据库表中元素的类和一个代表applicationUsers只有LatestTransaction

的元素的类。

表示数据库表的类:

class ApplicationUser : IdentityUser
{
    public int Id {get; set;}
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public virtual List<TransactionModel> Transactions { get; set; }
}

ApplicationUser与最新的交易

class AppicationUserExt : <base class needed?>
{
    public int Id {get; set;}
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public TransactionModel LatestTransaction { get; set; }
}

获取扩展ApplicationUser的功能是ApplicationUser的扩展功能。输入:IQueryable<ApplicationUser输出:IQueryable<ApplicationUserExt>

static class MyDbContextExtensions
{
    // returns ne ApplicationUserExt for every ApplicationUser
    public IQueryable<ApplicationUserExt> ToExtendedUsers(this IQueryable<ApplicationUser> applicationUsers)
    {
        return applicationUsers
            .Select(user => new ApplicationUserExt()
            {
               Id = user.Id,
               FirstName = user.FirstName,
               LastName = user.LastName,
               LatestTransaction = user.Trnasactions
                   .OrderByDescenting(transaction => transaction.CreationDate)
                    .FirstOrDefault(),
            }
        }
    }
}

因此,只要您使用所需的ApplicationUsers进行查询,就可以使用ToExtendedUsers()来获取扩展的suers

using (var dbContext = new MyDbContext(...))
{
    // you wanted to have a query like:
    var result dbContext.ApplicationUsers
        .Where(user => user.FirstName = "John"
                    && user.LastName = "Doe");

    // you'll have to add ToExtendedUsers:
    var result = dbContext.ApplicationUsers
        .Where(user => user.FirstName = "John"
                    && user.LastName = "Doe");
        .ToExtendedUsers();
}

由于结果仍然是IQueryable,因此尚未进行任何查询。您仍然可以在查询完成之前添加LINQ语句:

var result2 = result
    .Where(user.LatestTransaction.Year == 2018)        
    .GroupBy(user => user.LatestTransaction.Date)
    .OrderBy(group => group.Key)
    .Take(10)
    .ToList();

你知道,只要它是ApplicationUser,你仍然可以做各种LINQ。只要您需要LatestTransaction,就可以将其转换为ApplicationUserExt并继续连接您的linq语句。