Как я могу динамически создавать запросы Entity Framework?
Я новичок в Entity Framework, и у меня есть вопрос о фильтрации данных.
У меня есть два разных объекта журнала: DiskLog
и NetworkLog
. Эти сущности являются производными от объекта Log
. Вот код из моего приложения С#:
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. У меня есть несколько способов выполнения:
-
if logType == LogType.Disk && computerId <= 0
(это означает, что в запросе нет необходимости использовать параметр computerId, выберите только объекты DiskLog)
-
if logType == LogType.Disk && computerId > 0
(означает, что мне нужно использовать параметр computerId, выберите только объекты DiskLog)
-
if logType == LogType.NotInitialized && computerId <= 0
(нет необходимости использовать computerId и logType, просто выберите все сущности, DiskLog и NetworkLog)
-
if logType == LogType.NotInitialized && computerId > 0
(выберите все типы журналов для указанного компьютера)
-
if logType == LogType.Network && computerId <= 0
(выберите все объекты NetworkLog)
-
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();
Итак, вопрос в том, как я могу оптимизировать код? Где способ сделать это лучше.
Ответы
Ответ 1
Вы можете легко использовать компромисс запросов.
Сначала вы начинаете с запроса.
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;
И я бы рекомендовал использовать типы значений NULL для случаев, когда нет значения.
Ответ 2
Вы можете использовать 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)
И вы можете создать factory для этих функций