实体框架转换错误

时间:2011-02-25 17:00:42

标签: c# entity-framework entity-framework-4 iqueryable

以下作品完美无缺:

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   实体仅支持转换实体   数据模型基元类型。

有人可以了解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;
    }

修改

我真的希望Akash的回答是正确的,但如果是的话,我希望这里的中间查询会爆炸,但实际上这三个都很好。

我开始怀疑我在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根本不想解析查询中的整个查询;你必须将你的子步骤分解为局部变量。

除非有人有其他东西可以补充,否则今晚我会给Akash奖励。

        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();  

3 个答案:

答案 0 :(得分:1)

错误消息指出它只支持基元类型。

在可行的代码中,您已指定它为IQueryable<long>

我的猜测是,不起作用的代码使用IQueryable<decimal>因此导致错误。

您正在返回标识列。 Identity列可以有多种类型。 Decimal是可以处理所有可能的标识类型的数据类型。

Why does select SCOPE_IDENTITY() return a decimal instead of an integer?

在有效的代码中,编译器会得到一个使用long的提示。

答案 1 :(得分:1)

尝试编写第一个查询,而不是

IQueryable<long> propertyIdQuery = PropertyQuery.Select(p => p.PropertyId);

var propertyIdQuery = PropertyQuery.Select(p => p.PropertyId);

是否会抛出错误?这是对我的查询中唯一明显的区别。

答案 2 :(得分:1)

f => PropertyQuery.Select(p => p.PropertyId).Contains(f.PropertyId)

上面是一个linq表达式,f =&gt;之后的所有内容是一个表达式树。 Composite Linq只能基于表达式扩展查询,但不能扩展委托。

您的语句集在逻辑上都是正确的,但从编译器的角度来看它们是不同的。如果您注意到展开(在哪里或选择)将仅适用于相同类型的模板参数。其他地方你的IQueryable int不会起作用,因为你的linq期望T的IQueryable。

其次,当你在T上执行Select并返回T的IQueryable时,运行时无法知道早期的T是什么类型。

简而言之,保存中间步骤作为局部变量会破坏表达式树。我建议你看看Reflector,看看实际生成的源代码。

您的整个lambda表达式实际上是使用表达式节点构建的,并且构建了整个树并将其返回到Where方法。在第一个示例中的其他位置,表达式树是不同的,它涉及在表达式中执行其他内容。