代码之家  ›  专栏  ›  技术社区  ›  Jean-Francois

为什么当我传递一个Lambda expressin Func给我一个错误

  •  1
  • Jean-Francois  · 技术社区  · 15 年前

    当我传递调用一个返回Func并传入Where参数的泛型方法时,这不起作用。(System.InvalidOperationException:内部.NET框架数据提供程序错误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 />");
                }
    

    我的联系人列表视图模型

    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>
    
    2 回复  |  直到 15 年前
        1
  •  2
  •   StriplingWarrior    15 年前

    正如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);
    }
    

    编辑

    啊,是的,问题是您的函数实际上不知道它在编译时使用的是什么类:它只知道它是一个类,并且它实现了ibasegenericxt。所以当你说 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实体将尝试获取查询表达式中的任何内容并将其转换为SQL语句,所以必须注意不要在查询过程中调用方法。您需要首先调用GetLmbLang方法,并将其值存储在一个变量中,以便在查询中使用。
    2. 正如您在注释中指出的,因为contactTypeText属性没有实现IQueryable,所以这变得特别棘手。据我所知,你有三个选择:

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

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

    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();
    
        2
  •  1
  •   naasking    15 年前

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