为什么当我传递 Lambda Expressin Func 时会出现错误

发布于 2024-10-02 09:06:12 字数 3628 浏览 3 评论 0原文

当我通过调用返回 A Func 的通用方法并传入Where 的参数时,这是行不通的。 (System.InvalidOperationException:内部 .NET Framework 数据提供程序错误 1025。) 错误是当我想要获取角色信息时。
对于角色,我需要执行Where子句表达式EX:(p => p.LangID == 1)

此代码不起作用

在存储库中

public Func<T, bool> GetLmbLang<T>() where T:class,IBaseGenericTxt
    {
        int lang = -1;
        lang = Convert.ToInt32(HttpContext.Current.Session["Language"]);
        return (p => p.LangID == lang);
    }

在控制器中

                var ViewModel = _db.Contacts.Where(a=> a.IsActive == true).Select(a => new ContactListViewModel { 
                    ContactID = a.ContactID,
                    ContactName = a.ContactName,
                    Role = a.ContactType.ContactTypeTexts.Where(repGeneric.GetLmbLang<ContactTypeText>()).Select(af => af.Txt).FirstOrDefault(),
                    CompanyType = a.Supplier.SupplierName,
                    Addr = a.Address ,
                    Email = a.ContactEmail,
                    Phone = a.ContactPhone
                }).ToList();
                for (int i = 0; i < ViewModel.Count(); i++)
                {
                    Response.Write(ViewModel.ElementAt(i).ContactID + "<br />");
                }

此代码有效

 int lang = -1;
            lang = Convert.ToInt32(Session["Language"]);
            var ViewModel = _db.Contacts.Where(a=> a.IsActive == true).Select(a => new ContactListViewModel { 
                ContactID = a.ContactID,
                ContactName = a.ContactName,
                Role = a.ContactType.ContactTypeTexts.Where(p => p.LangID == lang).Select(af => af.Txt).FirstOrDefault(),
                CompanyType = a.Supplier.SupplierName,
                Addr = a.Address ,
                Email = a.ContactEmail,
                Phone = a.ContactPhone
            }).ToList();
            for (int i = 0; i < ViewModel.Count(); i++)
            {
                Response.Write(ViewModel.ElementAt(i).ContactID + "<br />");
            }

我的ContactListViewModel

public class ContactListViewModel
    {
        public int ContactID { get; set; }
        public string ContactName { get; set; }
        public string Role { get; set; }
        public string Company { get; set; }
        public string CompanyType { get; set; }
        public Address Addr { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
    }

我的列表视图

 ..... Inherits="System.Web.Mvc.ViewPage<List<mvcinfosite.ViewModels.ContactListViewModel>>" %>
 <table class="genTable">


    <% for (int i = 0;i < Model.Count; i++) { %>
        <tr>
            <td>
                <%: Html.ActionLink(item.ContactName, "Edit", new { id=item.ContactID }) %>
            </td>

            <td>
                <%: item.Role  %>
            </td>

            <td>
                <%: item.Company %>
            </td>

            <td>
                <%: item.CompanyType  %>
            </td>

            <td>
                <%: GlobalHelper.GetAddress(item.Addr) %>
            </td>
            <td>
                <%: item.Email %>
            </td>
            <td>
                <%: item.Phone %>
            </td>
        </tr>

    <% } %>

    </table>

When I pass call a Generic Method that return A Func and pass in parameter of the Where, that's dosen't work. (System.InvalidOperationException: Internal .NET Framework Data Provider error 1025.)
The error is when I want to get the Role information.
For the Role, I need to perform a Where Clause Expression EX: (p => p.LangID == 1)

This code dosen't Work

In the repository

public Func<T, bool> GetLmbLang<T>() where T:class,IBaseGenericTxt
    {
        int lang = -1;
        lang = Convert.ToInt32(HttpContext.Current.Session["Language"]);
        return (p => p.LangID == lang);
    }

In the controller

                var ViewModel = _db.Contacts.Where(a=> a.IsActive == true).Select(a => new ContactListViewModel { 
                    ContactID = a.ContactID,
                    ContactName = a.ContactName,
                    Role = a.ContactType.ContactTypeTexts.Where(repGeneric.GetLmbLang<ContactTypeText>()).Select(af => af.Txt).FirstOrDefault(),
                    CompanyType = a.Supplier.SupplierName,
                    Addr = a.Address ,
                    Email = a.ContactEmail,
                    Phone = a.ContactPhone
                }).ToList();
                for (int i = 0; i < ViewModel.Count(); i++)
                {
                    Response.Write(ViewModel.ElementAt(i).ContactID + "<br />");
                }

This code WORK

 int lang = -1;
            lang = Convert.ToInt32(Session["Language"]);
            var ViewModel = _db.Contacts.Where(a=> a.IsActive == true).Select(a => new ContactListViewModel { 
                ContactID = a.ContactID,
                ContactName = a.ContactName,
                Role = a.ContactType.ContactTypeTexts.Where(p => p.LangID == lang).Select(af => af.Txt).FirstOrDefault(),
                CompanyType = a.Supplier.SupplierName,
                Addr = a.Address ,
                Email = a.ContactEmail,
                Phone = a.ContactPhone
            }).ToList();
            for (int i = 0; i < ViewModel.Count(); i++)
            {
                Response.Write(ViewModel.ElementAt(i).ContactID + "<br />");
            }

My ContactListViewModel

public class ContactListViewModel
    {
        public int ContactID { get; set; }
        public string ContactName { get; set; }
        public string Role { get; set; }
        public string Company { get; set; }
        public string CompanyType { get; set; }
        public Address Addr { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
    }

My List View

 ..... Inherits="System.Web.Mvc.ViewPage<List<mvcinfosite.ViewModels.ContactListViewModel>>" %>
 <table class="genTable">


    <% for (int i = 0;i < Model.Count; i++) { %>
        <tr>
            <td>
                <%: Html.ActionLink(item.ContactName, "Edit", new { id=item.ContactID }) %>
            </td>

            <td>
                <%: item.Role  %>
            </td>

            <td>
                <%: item.Company %>
            </td>

            <td>
                <%: item.CompanyType  %>
            </td>

            <td>
                <%: GlobalHelper.GetAddress(item.Addr) %>
            </td>
            <td>
                <%: item.Email %>
            </td>
            <td>
                <%: item.Phone %>
            </td>
        </tr>

    <% } %>

    </table>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

月光色 2024-10-09 09:06:12

正如 naasking 指出的那样,您需要使用 Func 的表达式而不是直接的 Func:

public Expression<Func<T, bool>> GetLmbLang<T>() where T:class,IBaseGenericTxt
{
    int lang = -1;
    lang = Convert.ToInt32(HttpContext.Current.Session["Language"]);
    return (p => p.LangID == lang);
}

编辑

啊,是的,问题是您的函数实际上并不知道它正在使用哪个类编译时:它只知道它是一个类,并且它实现了 IBaseGenericTxt。因此,当您说 p.LangId 时,表达式的该部分正在调用 IBaseGenericTxt.LangId,而不是 ContactTypeText.LangId。

您需要构建自己的表达式树才能使其正常工作。像这样的东西:

var paramExpr = Expression.Parameter(typeof(T), "p");
return Expression.Lambda<Func<T, bool>>(
    Expression.Equal(
        Expression.Property(paramExpr, "LangId"),
        Expression.Constant(lang)),
    paramExpr);

编辑 2

两件事:

  1. 因为 LINQ to Entities 会尝试获取查询表达式中的任何内容并将其转换为 SQL 语句,所以您必须小心不要在中间调用方法您的查询。您需要首先调用 GetLmbLang 方法并将其值存储在变量中以在查询中使用。
  2. 正如您在评论中指出的那样,因为 ContactTypeTexts 属性没有实现 IQueryable,所以这变得特别棘手。据我所知,您有三个选择:

    1. 将整个 select 语句创建为表达式树。这非常烦人并且容易出错。
    2. 使用 Joe Albari 的 LinqKit 来“编译”和“扩展”您的查询。 LinqKit 将遍历表达式树并构建一个新树,其中您的查询表达式将转换为其等效的 Func。
    3. 返回到您的数据上下文,而不是使用 ContactTypeTexts 属性。

就我个人而言,我可能会选择最后一个选项,如下所示:

var lambdaLang = repGeneric.GetLmbLang<ContactTypeText>();
var ViewModel = _db.Contacts
    .Where(a=> a.IsActive == true)
    .Select(a => new ContactListViewModel { 
    ContactID = a.ContactID,
    ContactName = a.ContactName,
    Role = _db.ContactTypeTexts
        .Where(ct => ct.ContactType.Contacts.Any(
            c => c.ContactId == a.ContactId)
        .Where(lambdaLang)
        .Select(af => af.Txt).FirstOrDefault(),
    CompanyType = a.Supplier.SupplierName,
    Addr = a.Address ,
    Email = a.ContactEmail,
    Phone = a.ContactPhone
}).ToList();

As naasking points out, you need to use an Expression of a Func instead of a straight Func:

public Expression<Func<T, bool>> GetLmbLang<T>() where T:class,IBaseGenericTxt
{
    int lang = -1;
    lang = Convert.ToInt32(HttpContext.Current.Session["Language"]);
    return (p => p.LangID == lang);
}

Edit

Ah, yes, well the problem is that your function doesn't actually know what class it's working with at compile time: it only knows that it's a class, and it implements IBaseGenericTxt. So when you say p.LangId, that part of the expression is calling IBaseGenericTxt.LangId, and not ContactTypeText.LangId.

You'll need to build your own expression tree in order to get this to work right. Something like this:

var paramExpr = Expression.Parameter(typeof(T), "p");
return Expression.Lambda<Func<T, bool>>(
    Expression.Equal(
        Expression.Property(paramExpr, "LangId"),
        Expression.Constant(lang)),
    paramExpr);

Edit 2

Two things:

  1. Because LINQ to Entities will try to take anything in a query expression and convert it to a SQL statement, you have to be careful not to go calling methods in the middle of your query. You'll want to call the GetLmbLang method first and store its value in a variable to use in the query.
  2. As you point out in your comment, because the ContactTypeTexts property does not implement IQueryable, this gets particularly tricky. You have three options as far as I can tell:

    1. Create your entire select statement as an expression tree. This is very annoying and error-prone.
    2. Use Joe Albari's LinqKit to "Compile" and "Expand" your query. LinqKit will traverse the expression tree and build a new tree wherein your query Expression is converted to its equivalent Func.
    3. Go back to your data context rather than using the ContactTypeTexts property.

Personally, I would probably go with the last option, like this:

var lambdaLang = repGeneric.GetLmbLang<ContactTypeText>();
var ViewModel = _db.Contacts
    .Where(a=> a.IsActive == true)
    .Select(a => new ContactListViewModel { 
    ContactID = a.ContactID,
    ContactName = a.ContactName,
    Role = _db.ContactTypeTexts
        .Where(ct => ct.ContactType.Contacts.Any(
            c => c.ContactId == a.ContactId)
        .Where(lambdaLang)
        .Select(af => af.Txt).FirstOrDefault(),
    CompanyType = a.Supplier.SupplierName,
    Addr = a.Address ,
    Email = a.ContactEmail,
    Phone = a.ContactPhone
}).ToList();
无人问我粥可暖 2024-10-09 09:06:12

后一个代码之所以有效,是因为 C# 编译器将其转换为表达式树,即。 System.Linq.Expression,而您的原始代码被编译为 Func。当前设计的 Linq to SQL 无法处理 Func,只能处理表达式树。

The latter code works because the C# compiler converts it into an expression tree, ie. System.Linq.Expression, whereas your original code was compiled as a Func. Linq to SQL as currently designed cannot process the Func, only expression trees.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文