我该如何动态构建Entity Framework查询?

12

我对Entity Framework还比较新,关于数据过滤有一个问题。

我有两个不同的日志实体,它们分别是:DiskLogNetworkLog。这些实体都派生自Log实体。以下是我C#应用程序中的一些代码:

public class Log { ... }
public class DiskLog : Log { ... }
public class NetworkLog : Log { ... }

public enum LogType
{
    NotInitialized = 0,
    Disk,
    Network
}

public List<Log> GetWithFilter(
    Guid userKey, 
    int nSkip, 
    int nTake, 
    DateTime dateFrom = DateTime.MinValue, 
    DateTime dateTo = DateTime.MaxValue, 
    LogType logType = LogType.NotInitialized, 
    int computerId = 0)
{
    // need to know how to optimize ...

    return ...
}

当然,我已经有了工作的应用程序和数据库表。我想要做的是让GetWithFilter函数起作用。我有几种执行方式:
  1. if logType == LogType.Disk && computerId <= 0(这意味着在查询中不需要使用computerId参数,只选择DiskLog实体)

  2. if logType == LogType.Disk && computerId > 0(意味着必须使用computerId参数,只选择DiskLog实体)

  3. if logType == LogType.NotInitialized && computerId <= 0(不需要使用computerId和logType,只选择所有实体,DiskLog和NetworkLog)

  4. if logType == LogType.NotInitialized && computerId > 0(选择指定计算机的所有日志类型)

  5. if logType == LogType.Network && computerId <= 0(选择所有NetworkLog实体)

  6. if logType == LogType.Network && computerId > 0(选择指定计算机的所有NetworkLog实体)

正如您所看到的,有很多可用的选项。我需要编写6个类似于以下的查询:

1.

context.LogSet
    .OfType<DiskLog>
    .Where(x => x.Computer.User.UserKey == userKey)
    .Where(x => x.DateStamp >= dateFrom && x.DateStamp < dateTo)
    .OrderByDescending(x => x.Id)
    .Skip(nSkip)
    .Take(nTake)
    .ToList();

2.

context.LogSet
    .OfType<DiskLog>
    .Where(x => x.Computer.User.UserKey == userKey)
    .Where(x => x.DateStamp >= dateFrom && x.DateStamp < dateTo)
    .Where(x => x.Computer.Id == computerId)
    .OrderByDescending(x => x.Id)
    .Skip(nSkip)
    .Take(nTake)
    .ToList();

3.

context.LogSet
    .Where(x => x.Computer.User.UserKey == userKey)
    .Where(x => x.DateStamp >= dateFrom && x.DateStamp < dateTo)
    .OrderByDescending(x => x.Id)
    .Skip(nSkip)
    .Take(nTake)
    .ToList(); // simplest one!

4.

context.LogSet
    .Where(x => x.Computer.User.UserKey == userKey)
    .Where(x => x.DateStamp >= dateFrom && x.DateStamp < dateTo)
    .Where( x => x.Computer.Id == computerId)
    .OrderByDescending(x => x.Id)
    .Skip(nSkip)
    .Take(nTake)
    .ToList();

5.

context.LogSet
    .OfType<NetworkLog>
    .Where(x => x.Computer.User.UserKey == userKey)
    .Where(x => x.DateStamp >= dateFrom && x.DateStamp < dateTo)
    .OrderByDescending(x => x.Id)
    .Skip(nSkip)
    .Take(nTake)
    .ToList();

6.

context.LogSet
    .OfType<NetworkLog>
    .Where(x => x.Computer.User.UserKey == userKey)
    .Where(x => x.DateStamp >= dateFrom && x.DateStamp < dateTo)
    .Where( x => x.Computer.Id == computerId)
    .OrderByDescending(x => x.Id)
    .Skip(nSkip)
    .Take(nTake)
    .ToList();

所以问题是如何优化代码?有什么方法可以使其更好。
2个回答

14

您可以轻松使用查询合成。

您首先从查询开始。

IQueryable<Log> query = context.LogSet;

然后您可以组合子查询。

if (logType == LogType.Disk)
{
    query = query.OfType<DiskLog>(); // not sure if you need conversion here
} 
else if (logType == LogType.Network)
{
    query = query.OfType<NetworkLog>(); // not sure if you need conversion here
}

query = query.Where(x => x.Computer.User.UserKey == userKey);

if (computerId != 0)
   query = query.Where( x => x.Computer.Id == computerId);

// .. and so on

query = query.OrderByDescending(x => x.Id).Skip(nSkip).Take(nTake);

return query.ToList(); // do database call, materialize the data and return;

我建议在没有值的情况下使用可空值类型。


7
您可以使用 Func<T,bool> 来优化此操作。
IEnumerable<T> Select<T>(IEnumerable<T> source, Func<T, bool> userKeyFunc, Func<T, bool> dateFunc, int skip, int take)
{
    return source.OfType<T>().Where(userKeyFunc).Where(dateFunc).Skip(skip).Take(take);
}

那么使用:

var result = Select<NetworkLog>(context.LogSet,x => x.Computer.User.UserKey == userKey,
                                x => x.DateStamp >= dateFrom && x.DateStamp < dateTo, nSkip,nTake)

你可以为这些函数创建工厂。


5
应该使用Expression<Func ..>,这将在内存中过滤数据。 - Euphoric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接