Увы, вы забыли показать нам класс DataPoint
, поэтому я могу только дать рекомендации, которые помогут вам решить вашу проблему.
Из вашего кода я вижу, что каждый DataPoint
имеет несколько однозначныхмногие отношения (или многие-ко-многим).
Поскольку вы используете существительное во множественном числе для свойства ConstraintFilters
, кажется, что каждый DataPoint
имеет ноль или более ConstraintFilters
.Эти ConstraintFilters
хранятся в отдельной таблице:
class DataPoint
{
public int Id {get; set;} // primary key
...
public virtual ICollection<ConstraintFilter> ConstraintFilters {get; set;}
}
В рамках сущности не виртуальные свойства представляют столбцы в таблице базы данных;виртуальные свойства представляют отношения между таблицами (один ко многим, многие ко многим и т. д.)
Однако, исходя из вашего заголовка и вашей ошибки, кажется, что ConstraintFilters
равен единице (сложный) объект, которые являются столбцами в вашей таблице DataPoints
.
class DataPoint
{
public int Id {get; set;} // primary key
...
public ConstraintFilter ConstraintFilters {get; set;}
}
Если это так: причина в вашем .Include(ent=>ent.ConstraintFilters)
.Не используйте это.Просто получите доступ к свойствам, которые вы планируете использовать в своем запросе, и значения будут извлечены.
Совет: придерживайтесь соглашений о коде в рамках структуры сущностей , это повышает удобочитаемость вашего кода.В вашем случае: если у вашего DataPoint
есть один (сложный тип) ConstraintFilter
, не используйте множественное число ConstraintFilters
.
Проблемы в вашем запросе
Из вашей коллекции DataPoints
вы, кажется, хотите выбрать определенное DataPoints
и запросить некоторые (или все) его свойства.
В вашем запросе я вижу несколько проблем.
Вы получаете больше данных, чем на самом делеиспользуйте
Одна из более медленных частей запроса к базе данных - это передача выбранных данных из вашей системы управления базами данных в локальный процесс.Следовательно, имеет смысл ограничить объем передаваемых данных.
Каждый DataPoint
имеет первичный ключ, вероятно, в свойстве Id
.Каждый DataPoint
имеет ноль или более DataPointEnumerations
.Каждый DataPointEnumeration
принадлежит ровно одному DataPoint
(один ко многим).Для этого DataPointEnumeration
имеет внешний ключ DataPointId
, который имеет то же значение, что и DataPoint.Id
, к которому он принадлежит.
Если вы запросите DataPoint
с его 1000 DataPointEnumerations
you 'Вы узнаете, что каждый DataPointId
каждого DataPointEnumeration
этого DataPoint
имеет одно и то же значение, а именно значение DataPoint.Id
: что за передача этого же значения снова и снова.
При запросе данных не используйте Include
.Вместо этого используйте Select
и Select
только те свойства, которые вы действительно планируете использовать.Используйте Include
только в том случае, если вы планируете изменить и сохранить извлеченные включенные данные.
После ToList () вы продолжите объединение операторов LINQ
До тех пор, пока вы сохраняете свои данные IQueryable
, запрос не выполняется.Объединение операторов LINQ изменит только IQueryable.Expression
.
. ToList
выполнит запрос и передаст данные вашему локальному процессу.После этого вы снова делаете его IQueryable
.
Представьте, что происходит в следующем коде:
IQueryable<DataPoint> dataPointQuery = YourProcedure() // the procedure that return your query
DataPoint firsDataPoint = dataPointQuery.FirstOrDefault();
Сначала ваш ToList
выбирает все DataPoints
, которые соответствуют вашему Where
в локальную память, тогда вы берете только первый и выбрасываете все остальные извлеченные DataPoints
: что за трата вычислительной мощности.
Сохраняйте ваши IQueryable
и IQueryable
как можно дольше,что позволяет пользователям объединять операторы LINQ без фактического выполнения IQueryable
.
IQueryable и IEnumerable
Единственная причина для выполнения операторов linq после передачи данных в локальную память -потому что вам нужно вызывать локальные функции, как вы делаете в ConstraintFilters = getConditions()
Этот оператор не может быть переведен в SQL.Это, вероятно, причина, по которой вы добавили ToList()
в свой запрос.
Если вы начнете перечислять AsEnumerable
, либо неявно используя ToList()
, FirstOrDefault()
, Any (), foreach
, либонеявно используя GetEnumerator
и MoveNext
, AsEnumerable
будет извлекать только страницу запроса, а не полные данные.
IQueryable<...> sourceData = ...
var fetchdItem = sourceData
.AsEnumerable()
.Where(item => this.Localfunction(item)) // for this we need AsEnumerable
.FirstOrDefault();
FirstOrDefault
будет внутренне GetEnumerator
и MoveNext
.AsEnumerable будет получать одну «страницу» исходных данных, которая не является полной коллекцией.Поэтому, если вы используете только FirstOrDefault()
, выбирается более одного исходного элемента, но не все, что немного более эффективно.Только если вы перечисляете больше элементов, чем помещается на одной странице, следующая страница запрашивается из базы данных.
Объединение советов
- Нет
Include
, но Select
дляизвлекать только использованные данные - Нет
ToList
, но использовать Enumerable
для извлечения данных на страницу вместо всех данных - Возвращать
IEnumerable
, в конце концов: данные находятся в локальной памяти,IEnumerable
может сделать больше, чем IQueryable
.
IEnumerable<DataPoint> FetchDataPoints(...)
{
return myDbContext.DataPoints
// select a subset of dataPoints:
// improve readability, use proper identifiers: not "ent", but "dataPoint"
.Where(dataPoint => ...)
// select only the properties you plan to use in this use-case scenario
.Select(dataPoint => new
{
Id = dataPoint.Id,
Description = dataPoint.Description,
EffectiveDate = dataPoint.EffectiveDate,
...
DataPointStates = dataPoint.DataPointStates
// only Select the dataPointStates I plan to use
.Where(dataPointState => ...))
// from every dataPointSate select only the properties I plan to use
.Select(dataPointState => new
{
...
// not needed: dataPointState.DataPointId, you know the value
})
.ToList(),
// query only the DataPointExpressions you plan to use,
DataPointExpressions = dataPoint.DataPointExpressions
.Where(dataPointExpression => ...)
.Select(dataPointExpression => new
{
// again select only the properties you plan to use
})
.ToList(),
})
В следующих инструкциях вы будете использовать локальные функции, такие как getConditions()
, поэтому выбранные данные должны бытьперенесено в локальную память на страницу:
Продолжите оператор LINQ:
.AsEnumerable()
Следующее необходимо, только если вам нужно вернуть извлеченные данные в типизированном объекте.Если вы используете данные только в этом блоке кода, нет необходимости преобразовывать их в новую точку данных:
// continue the linq statement: put the transferred data in a type.
.Select(fetchedData => new DataPoint
{
Description = fetchedData.Description,
EffectiveDate = fetchedData..EffectiveDate,
DataPointStates = fetchedData.DataPointStates,
...
ConstraintFilters = getConditions(),
});
// note: the result is an IEnumerable! no need to convert it to IQueryable
// because an IEnumerable can do much more than an IQueryable
}