Почему, когда я передаю лямбда-выражение в Func, это выдает ошибку - PullRequest
1 голос
/ 13 ноября 2010

Когда я передаю вызов Generic Method, который возвращает 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 />");
                }

Этот код 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; }
    }

Мой список

 ..... 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 ]

2 голосов
/ 13 ноября 2010

Как следует из наглядности, вам нужно использовать выражение 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);
}

Edit

Ах, да, хорошопроблема в том, что ваша функция на самом деле не знает, с каким классом она работает во время компиляции: она знает только, что это класс, и реализует 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. Создайте весь оператор выбора в виде дерева выражений.Это очень раздражает и подвержено ошибкам.
    2. Используйте 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();
1 голос
/ 13 ноября 2010

Последний код работает, потому что компилятор C # преобразует его в дерево выражений, т.е. System.Linq.Expression, тогда как ваш исходный код был скомпилирован как Func. Linq to SQL, как в настоящее время разработано, не может обрабатывать Func, только деревья выражений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...