【问题标题】:using function in Linq在 Linq 中使用函数
【发布时间】:2011-08-24 22:06:43
【问题描述】:

我有这个问题:

var accounts =
    from account in context.Accounts
    from owner in account.AccountOwners
    join business in context.Businesses
        on account.CreditRegistryId
        equals business.RegistryId
    join astatus in context.AccountStatuses
        on account.AccountStatusId
        equals astatus.AccountStatusId     
    join LS in context.LegalStatuses
        on account.LegalStatusId 
        equals LS.LegalStatusId
    where !excludeTypes.Contains(account.AccountType)               
    select new AccountsReport
    {
        AccountTypeDescription = GetAccountTypeDescription(account.AccountType),  
        AccountNumber = 1, 
        AccountStatus = "aasd", 
        CreditorAddress = "address", 
        CreditorCity = "my city", 
        CreditorName = "creditor name", 
        CreditorState = "my state", 
        LegalStatus = "my status", 
        RegistryId = 121323
    };

这是错误的:

LINQ to Entities does not recognize the method 'System.String GetAccountTypeDescription(System.String)' method, and this method cannot be translated into a store expression. 

功能是:

public string GetAccountTypeDescription(string accountType)
{
    var result = context.AccountTypes.Where(x => x.AccountTypeCode == accountType).Select(x => x.Abbreviation).SingleOrDefault();

    if (string.IsNullOrEmpty(result))
    {
        result = accountType;
    }

    return result;
}

如果我不在 LINQ 查询中使用 GetAccountTypeDescription,它可以工作。

请提出解决方案

【问题讨论】:

  • 从您的编辑中,我不明白您为什么不简单地将其作为连接包含在较大的 linq 查询中。
  • @Steen:如何修改查询以将此函数用作连接?

标签: c# .net linq entity-framework asp.net-mvc-3


【解决方案1】:

编辑 - 添加左连接

var accounts =
    from account in context.Accounts
    from owner in account.AccountOwners
    join business in context.Businesses
        on account.CreditRegistryId
        equals business.RegistryId
    join astatus in context.AccountStatuses
        on account.AccountStatusId
        equals astatus.AccountStatusId     
    join LS in context.LegalStatuses
        on account.LegalStatusId 
        equals LS.LegalStatusId
    from accountType in context.AccountTypes
                               .Where(at => at.AcountTypeCode == account.AccountType)
                               .DefaultIfEmpty()
    where !excludeTypes.Contains(account.AccountType)               
    select new AccountsReport
    {
        AccountTypeDescription = accountType.Abbreviation == null ? account.AccountType : accountType.Abbreviation,  
        AccountNumber = 1, 
        AccountStatus = "aasd", 
        CreditorAddress = "address", 
        CreditorCity = "my city", 
        CreditorName = "creditor name", 
        CreditorState = "my state", 
        LegalStatus = "my status", 
        RegistryId = 121323
    };

【讨论】:

  • 其实我已经添加了简单的功能。我添加了原始功能,请检查编辑。
  • @DotnetSparrow - 更新答案以在 context.AccountTypes 上包含左连接
【解决方案2】:

您不能在 linq-to-entities 中使用任意函数。它被解释了很多次。例如here。如果您想在 Linq-to-entities 查询中使用自定义函数,您必须在数据库中将其定义为 SQL 用户定义函数 (UDF) 并进行映射。

首先创建函数

CREATE FUNCTION dbo.udf_GetAccountTypeDescription (@Param VARCHAR(50))
RETRUNS VARCHAR(100)
AS
BEGIN
    RETURN @Param + ' ' + 'Item type'
END

现在您必须更新模型并导入函数(它将在存储过程中列出)。将函数导入 SSDL(EDMX 中的存储描述)后,您可以映射函数:

public static class EdmFunctions
{
    // First parameter is namespace of SSDL (open EDMX as XML if you are not sure about namespace)
    [EdmFunction("TestModel.Store", "udf_GetAccountTypeDescription")]
    public static string GetAccountTypeDescription(string parameter)
    {
        throw new NotSupportedException("This function is only for L2E query.");
    }
}

这个函数只是表达式树中使用的占位符。当 store provider 将表达式树转换为 SQL 查询时,它将被映射的 SQL 函数替换。现在您可以在 Linq-to-entities 查询中使用它:

AccountTypeDescription = EdmFunctions.GetAccountTypeDescription("...")

【讨论】:

  • 我使用的是 Entity Framework 4.1 代码优先方法,而不是 Edmx
  • 在这种情况下忘记调用 Linq-to-entities 中的任何自定义函数并重写您的查询。
【解决方案3】:

我有点不明白。您为什么不向类/结构 AccountReport 提供一个属性,该属性为您提供 AccountTypeDescription... 这样您封装了逻辑,隐藏了实现细节,并且通常使代码更干净。由于帐户描述只是对提取数据的转换,这是最好的方法。 卢克

【讨论】:

  • @Luke:我在 AccountReport 中有 AccountTypeDescription 作为属性
  • 但它没有做正确的工作!将逻辑封装在属性中,不只是提供简单的数据 getter 和 setter。设置 AccountName 和属性 Account Description 将为您提供正确的格式。这样,当您更改帐户描述的格式时,您可以在一个地方进行更改,并且如果您有帐户层次结构,则可以对每个帐户进行不同的描述... formatAccountDescritpion 之类的功能在这里是高度冗余的,并且是代码异味我的看法
【解决方案4】:

您的 LINQ 查询不是在本地运行,而是转换为表达式(转换为 SQL)并执行您的数据库服务器。表达式解析器无法将您的函数转换为 SQL,因为该函数在服务器上不存在。您只能在要在数据库上运行的 LINQ 查询中使用 LINQ 和其他一些 .NET 函数。您可以轻松解决此问题

var accountDescription = GetAccountTypeDescription("sdfsdf");
var accounts =
            from account in context.Accounts
            from owner in account.AccountOwners
             join business in context.Businesses
             on account.CreditRegistryId
             equals business.RegistryId
             join astatus in context.AccountStatuses
             on account.AccountStatusId
             equals astatus.AccountStatusId     
             join LS in context.LegalStatuses
             on account.LegalStatusId 
             equals LS.LegalStatusId
             where !excludeTypes.Contains(account.AccountType)               
            select new AccountsReport { AccountTypeDescription= accountDescription,  AccountNumber = 1, AccountStatus = "aasd", CreditorAddress = "address", CreditorCity = "my city", CreditorName = "creditor name", CreditorState = "my state", LegalStatus = "my status", RegistryId = 121323 };

【讨论】:

  • 实际上 GetAccountTypeDescription 将为每个帐户行调用,例如 GetAccountTypeDescription(account.accountType) 我无法在查询之前定义它。有什么建议吗?
  • @DotnetSparrow,您可能需要编辑您的问题以包含该信息,因为您当前作为示例的代码不正确。 =)
  • 然后你必须将你的函数的逻辑内联到你的查询中。
猜你喜欢
  • 2012-03-09
  • 1970-01-01
  • 2016-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多