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"&开发者_运维知识库gt;
<% 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>
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:
- 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.
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:
- Create your entire select statement as an expression tree. This is very annoying and error-prone.
- 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.
- 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();
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.
精彩评论