LINQ - 先前使用和证明的查询条件出现“无法翻译表达式”错误

5

我对LINQ相当新手,无法理解一些不一致的行为。非常感谢任何有知识的输入。我在SO和其他地方看到了类似的问题,但它们似乎没有帮助。

我有一个非常简单的设置-一个公司表和一个地址表。 每个公司可以拥有0个或多个地址,并且如果有> 0,则必须指定一个主地址。 我正在尝试处理没有地址的情况,使用外连接并相应地更改选择语句。

请注意,我目前将输出直接绑定到GridView,因此我希望在查询中保留所有处理。

以下可行

IQueryable query =
    from comp in context.Companies
    join addr in context.Addresses on comp.CompanyID equals addr.CompanyID into outer   // outer join companies to addresses table to include companies with no address
    from addr in outer.DefaultIfEmpty()
    where (addr.IsMain == null ? true : addr.IsMain) == true    // if a company has no address ensure it is not ruled out by the IsMain condition - default to true if null
    select new {
        comp.CompanyID,
        comp.Name,
        AddressID = (addr.AddressID == null ? -1 : addr.AddressID), // use -1 to represent a company that has no addresses
        MainAddress = String.Format("{0}, {1}, {2} {3} ({4})", addr.Address1, addr.City, addr.Region, addr.PostalCode, addr.Country)
    };

但是这会在GridView中显示一个空地址,即 ", , ()"

因此,我更新了MainAddress字段为

MainAddress = (addr.AddressID == null ? "" : String.Format("{0}, {1}, {2} {3} ({4})", addr.Address1, addr.City, addr.Region, addr.PostalCode, addr.Country))

现在我遇到了“无法翻译表达式”的错误,并且有很多自动生成的代码,对我来说意义不大。

我为MainAddress添加的条件与AddressID上的工作条件没有任何区别,所以有人可以告诉我这里发生了什么吗?

非常感谢任何帮助。


2
可能需要将“自动生成的代码”添加到问题中。 - Bert
1个回答

6
你遇到的错误提示说明LinqToSql无法将你的null检查和string.Format表达式转换为SQL。如果你查看第一个查询生成的SQL(使用LinqPad或SQL Profiler),你会看到类似以下内容的东西:
SELECT [t0].[CompanyID], [t0].[Name], 
    (CASE 
        WHEN [t1].[AddressID] IS NULL THEN @p0
        ELSE [t1].[AddressID]
     END) AS [AddressID], 
    [t1].[Address1] AS [value], 
    [t1].[City] AS [value2], 
    [t1].[Region] AS [value3], 
    [t1].[PostalCode] AS [value4], 
    [t1].[Country] AS [value5]
FROM [Company] AS [t0]
LEFT OUTER JOIN [Address] AS [t1] ON [t0].[CompanyID] = [t1].[CompanyID]
WHERE ([t1].[IsMain] IS NULL) OR ([t1].[IsMain] = 1)

对于你的AddressID字段,你可以看到它使用了一个CASE-WHEN来处理AddressID为空的情况。当你为MainAddress添加一个CASE-WHEN时,它试图对该字段执行同样的操作,但是没有SQL等效的string.Format可用于ELSE子句,因此会出错。
解决这个问题的简单方法是使用一个方法来格式化字符串。通过调用一个私有方法,LinqToSql不会尝试将string.Format翻译成SQL,而是返回填充Address对象所需的所有字段。然后该方法就可以处理格式化任务。
例如:
LINQ:
....
select new {
    comp.CompanyID,
    comp.Name,
    AddressID = (addr.AddressID == null ? -1 : addr.AddressID),
    MainAddress = FormatAddress(addr)
};

方法:

private static string FormatAddress(Address addr)
{
    return (addr == null ? "" : 
            string.Format("{0}, {1}, {2} {3} ({4})", 
                      addr.Address1, addr.City, 
                      addr.Region, addr.PostalCode, addr.Country));
}

太棒了,非常感谢你的帮助和解释。我没有意识到LINQ会尝试将哪些部分传递给数据库。我一直在不断学习这些疯狂的东西... - tomfumb

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