Следующее работает отлично:
IQueryable<Property> PropertyQuery = PropertyDAO.SearchWithAdditionalParameters(/* elided */);
IQueryable<long> propertyIdQuery = PropertyQuery.Select(p => p.PropertyId);
var relevantFMVs = PropertyDAO.db.FMVHistories.Where(f => propertyIdQuery.Contains(f.PropertyId)).ToList();
Но взрывается следующее:
IQueryable<Property> PropertyQuery = PropertyDAO.SearchWithAdditionalParameters(/* elided */);
var relevantFMVs = PropertyDAO.db.FMVHistories.Where(f => PropertyQuery.Select(p => p.PropertyId).Contains(f.PropertyId)).ToList();
(обратите внимание, что вместо создания propertyIdQuery отдельно, я просто подставил сам запрос на место переменной)
Исключение составляет
Невозможно привести тип "System.Linq.IQueryable
1' to type 'System.Linq.IQueryable
1". LINQ to Entities поддерживает только приведение типов примитивов модели данных сущности.
Может ли кто-нибудь пролить свет на то, что делает EF (4) под прикрытием, чтобы заставить работать только первый запрос, даже если они якобы эквивалентны?
Я знаю, что IQueryable<T>
и деревья выражений делают много вещей под прикрытием, но как так получается, что сохранение промежуточного шага в локальной переменной может повлиять на результат?
ИЗМЕНИТЬ
По запросу, вот полный вызываемый метод и методы, которые вызывает этот метод:
public IQueryable<Property> BasicSearchFromConstraints(PropertyInvoiceConstraints constraints) {
return ExecuteSearchFromConstraints((dynamic)constraints.PropertyInst, constraints.CompanyNumber, constraints.TaxSubType, constraints.PhaseID, constraints.State, constraints.County, constraints.City, constraints.Jurisdiction);
}
private IQueryable<T> ExecuteSearchFromConstraints<T>(T property, int CompanyNumber, byte SubType, byte PhaseID, string State, string County, string City, string Jurisdiction) where T : Property {
IQueryable<T> result = base.db.Properties.OfType<T>();
if (SubType > 0)
result = result.Where(p => p.TaxSubTypeId == SubType);
if (CompanyNumber > 0)
result = result.Where(p => p.CompanyNum == CompanyNumber);
if (!String.IsNullOrEmpty(State))
result = result.Where(p => p.State == State);
if (!String.IsNullOrEmpty(County))
result = result.Where(p => p.County == County);
if (!String.IsNullOrEmpty(City))
result = result.Where(p => p.City == City);
if (!String.IsNullOrEmpty(Jurisdiction))
result = result.Where(p => p.Jurisdiction == Jurisdiction);
if (PhaseID > 0)
result = result.Where(p => p.PhaseId == PhaseID);
return result;
}
public virtual IQueryable<Property> SearchWithAdditionalParameters(DataLayer.DAO.PropertyInvoiceConstraints constraints, string propertyNumber = "", string altDesc = "", string countyAcctNumber = "", string City = "", string Jurisdiction = "", string secondaryStateID = "", string LegalDesc = "", string status = "", int? TaxYear = null) {
IQueryable<Property> result = BasicSearchFromConstraints(constraints);
if (!String.IsNullOrEmpty(status))
result = result.Where(p => p.Status == status);
if (!String.IsNullOrEmpty(propertyNumber))
result = result.Where(p => p.PropertyNum.Contains(propertyNumber));
if (!String.IsNullOrEmpty(altDesc))
result = result.Where(p => p.AltDescription.Contains(altDesc));
if (!String.IsNullOrEmpty(countyAcctNumber))
result = result.Where(p => p.CountyAccountNum.Contains(countyAcctNumber));
if (!String.IsNullOrEmpty(City))
result = result.Where(p => p.City.Contains(City));
if (!String.IsNullOrEmpty(Jurisdiction))
result = result.Where(p => p.Jurisdiction.Contains(Jurisdiction));
if (TaxYear.HasValue)
result = result.Where(p => p.FMVHistories.Any(f => f.TaxYear == TaxYear));
if (constraints.FMVPhaseID > 0)
result = result.Where(p => p.FMVHistories.Any(f => f.PhaseId == constraints.FMVPhaseID));
if (!String.IsNullOrEmpty(secondaryStateID))
if (constraints.PropertyInst is WellDetail)
result = result.OfType<WellDetail>().Where(w => w.SecondaryStateId == secondaryStateID);
else
throw new ApplicationException("Invalid use -> Secondary State ID can only be set when searching for Well property types");
if (!String.IsNullOrEmpty(LegalDesc))
if (constraints.PropertyInst is RealEstateDetail)
result = result.OfType<RealEstateDetail>().Where(r => r.LegalDescr.Contains(LegalDesc));
else if (constraints.PropertyInst is RealEstateServicingDetail)
result = result.OfType<RealEstateServicingDetail>().Where(r => r.LegalDescr.Contains(LegalDesc));
else throw new ApplicationException("Invalid use -> Legal Description can only be set when searching for either real estate or real estate servicing property types");
return result;
}
ИЗМЕНИТЬ
Я действительно хотел, чтобы ответ Акаша был правильным, но если бы это было так, я ожидал бы, что средний запрос здесь взорвется, но на самом деле все три работают нормально.
Я начинаю подозревать, что структура наследования типа Property
(из исходного примера) может иметь какое-то отношение к этому.
DummyBookModelEntities db = new DummyBookModelEntities();
IQueryable<int> BookIds = db.Books.Where(b => b.id < 4).Select(b => b.id);
IQueryable<Book> BooksFromIdQuery = db.Books.Where(b => b.id < 4);
try {
var l1 = db.Books.Where(b => BookIds.Contains(b.id)).ToList();
Console.WriteLine("ID Query With ID Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query Failed:");
Console.WriteLine(E.ToString());
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Select(b_inner => b_inner.id).Contains(b.id)).ToList();
Console.WriteLine("ID Query With Whole Book Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query With Whole Book Local Var Failed:");
Console.WriteLine(E.ToString());
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Contains(b)).ToList();
Console.WriteLine("Whole Book sub query without select worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("Whole Book sub query without select:");
Console.WriteLine(E.ToString());
Console.WriteLine();
}
ИЗМЕНИТЬ
Я добавил наследование, и теперь два нижних запроса не работают. Похоже, что всякий раз, когда у вас есть OfType()
в запросе, EF просто не хочет анализировать все запросы внутри запросов; вам нужно разбить свои подэтапы на локальные переменные.
Сегодня вечером я награду Акаша наградой, если у кого-то еще не будет чего добавить.
DummyBookModelEntities db = new DummyBookModelEntities();
IQueryable<int> BookIds = db.Books.OfType<SciFiBook>().Where(b => b.id < 4).Select(b => b.id);
IQueryable<Book> BooksFromIdQuery = db.Books.OfType<SciFiBook>().Where(b => b.id < 4);
try {
var l1 = db.Books.Where(b => BookIds.Contains(b.id)).ToList();
Console.WriteLine("ID Query With ID Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query Failed:");
Console.WriteLine(E.Message);
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Select(b_inner => b_inner.id).Contains(b.id)).ToList();
Console.WriteLine("ID Query With Whole Book Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query With Whole Book Local Var Failed:");
Console.WriteLine(E.Message);
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Contains(b)).ToList();
Console.WriteLine("Whole Book sub query without select worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("Whole Book sub query without select:");
Console.WriteLine(E.Message);
Console.WriteLine();
}
Console.WriteLine();