EF4 Include() with Projection - entity-framework

I am trying to do a simple query which involves both eager loading and projection and am having problems. I am using CodeFirst CTP5 but I beleive that this issue affects straight EF4 as well.
Here is my initial query:
public List<ArticleSummary> GetArticles()
{
var articlesQuery = _db.Articles.Include(article => article.Category).Select(article => new ArticleSummary
{
Article = article,
CommentsCount = article.Comments.Count
});
return articlesQuery.ToList();
}
This results in the category property of article being null. If I take out the projection, it works just fine. After reading this, it seems to suggest that I need to do the include after the projection, so I changed the query to:
public List<ArticleSummary> GetArticles()
{
var articlesQuery = _db.Articles.Select(article => new ArticleSummary
{
Article = article,
CommentsCount = article.Comments.Count
});
articlesQuery = articlesQuery.Include(x => x.Article.Category);
return articlesQuery.ToList();
}
This results in an exception (see below) which is similar to this SO post.
"Unable to cast the type
'System.Linq.IQueryable1' to type
'System.Data.Objects.ObjectQuery1'.
LINQ to Entities only supports casting
Entity Data Model primitive types."
So, how do I do it?

You could try:
public List<ArticleSummary> GetArticles()
{
var articlesQuery = _db.Articles.Select(article => new
{
Article = article,
Category = article.Category
CommentsCount = article.Comments.Count
}
).ToList().
Select(
x => new ArticleSummary()
{
Article = x.Article,
CommentsCount = x.CommentsCount
}
);
return articlesQuery.ToList();
}

Related

Using Dynamic LINQ with EF.Functions.Like

On the Dynamic LINQ website there's an example using the Like function.
I am unable to get it to work with ef core 3.1
[Test]
public void DynamicQuery()
{
using var context = new SamDBContext(Builder.Options);
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var lst = context.Contacts.Where(config, "DynamicFunctions.Like(FirstName, \"%Ann%\")".ToList();
lst.Should().HaveCountGreaterThan(1);
}
Example from the Dynamic LINQ website
var example1 = Cars.Where(c => EF.Functions.Like(c.Brand, "%t%"));
example1.Dump();
var config = new ParsingConfig { ResolveTypesBySimpleName = true };
var example2 = Cars.Where(config, "DynamicFunctions.Like(Brand, \"%t%\")");
example2.Dump();
Looks like my code. But I am getting the following error
System.Linq.Dynamic.Core.Exceptions.ParseException : No property or field 'DynamicFunctions' exists in type 'Contact'
you don't need the ResolveTypesBySimpleName, implement your wont type provider.
The piece below people to use PostgreSQL ILike with unnaccent
public class LinqCustomProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var result = base.GetCustomTypes();
result.Add(typeof(NpgsqlFullTextSearchDbFunctionsExtensions));
result.Add(typeof(NpgsqlDbFunctionsExtensions));
result.Add(typeof(DbFunctionsExtensions));
result.Add(typeof(DbFunctions));
result.Add(typeof(EF));
return result;
}
}
// ....
var expressionString = $"EF.Functions.ILike(EF.Functions.Unaccent(People.Name), \"%{value}%\")";
var config = new ParsingConfig()
{
DateTimeIsParsedAsUTC = true,
CustomTypeProvider = new LinqCustomProvider()
};
return query.Where(config, expressionString);
Hope this helps people, took me some time to get this sorted.

LINQ to Entity cannot use System.Object.GetValue

Below find a method that does not work. We fail on the line query.Select(...
Below that find a method with hard coded object property names which does work. But, this method is obviously not dynamic, nor flexible. There may be many properties of a Customer I may wish to search on.
The error string is at bottom. I get it that somehow the LINQ to Entity is unable to deal with conversion of GetValue to some sort of TSQL. Would anyone know of how I might code this up?
public List<Customer> GetForQuery(params Tuple<string, string>[] keyValuePairs) {
using (var db = new DBEntities()) {
var availableProperties = typeof(Customer).GetTypeInfo().DeclaredProperties.ToList();
var query = db.Customers.Select(c => c);
foreach (Tuple<string, string> pair in keyValuePairs) {
PropertyInfo pi = availableProperties.First(p => p.Name.Equals(pair.Item1));
if (pi == null)
continue;
query = query.Where(u => pi.GetValue(u, null).ToString().StartsWith(pair.Item2));
}
var results = query.Select(c => c).ToList();
return results;
}
}
How I might call the above:
CustomerController custController = new CustomerController();
List<Customer> results = custController.GetForQuery(Tuple.Create<string, string>("FName", "Bob" ));
The working fixed method:
public List<Customer> GetForQuery(string firstName = "", string lastName = "", string phoneNumber = "") {
using (var db = new DBEntities()) {
var query = db.Customers.Select(c => c);
if (firstName.HasContent())
query = query.Where(u => u.FName.StartsWith(firstName));
if (lastName.HasContent())
query = query.Where(u => u.LName.StartsWith(lastName));
if (phoneNumber.HasContent())
query = query.Where(u => u.EveningPhone.StartsWith(phoneNumber));
var results = query.Select(c => c).ToList();
return results;
}
}
ERROR:
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object, System.Object[])' method, and this method cannot be translated into a store expression.

MongoDB C# Combining Fields

The Plan:
So now what I basically want is to take my propertys out of the class, let the user pick some and then pull a List with ONLY those propertys out of MongoDB.
The Code:
here is where the method starts:
private void DoStuffExecute(object obj)
{
Class class= new Class();
ExtractClass(class);
if (propList != null)
{
var result = classService.DoStuff(propList);
}
}
in "ExtractClass()" the Propertys are being pulled out of the Class.
void ExtractClass(object obj)
{
foreach (var item in obj.GetType().GetProperties())
{
propList.Add(item.Name);
}
}
and finally in "classService.DoStuff()" i try to set the "fields".
public List<class> DoStuff(List<string> Props)
{
try
{
var filter = Builders<class>.Filter.Empty;
var fields = Builders<class>.Projection.Include(x => x.ID);
foreach (var item in Props)
{
string str = "x.";
str += item.ToString();
fields = Builders<class>.Projection.Include(x => str);
fields = Builders<class>.Projection.Include(x => item);
}
var result = MongoConnectionHandler.MongoCollection.Find(filter).Project<class>(fields).ToList();
return result;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
var result = new List<class>();
return result;
}
}
when i run the programm it gives me an "Unable to determine the serialization information for x=> value"... since im giving it a string.
The Question:
Does anyone have an Idea how to repair the code above or even make the plan work in another way?
thank you.
First of all: you are using such code lines as : var filter = Builders<class>.Filter.Empty; It is not possible, because class is a reserved keyword in c# (https://msdn.microsoft.com/en-us/library/x53a06bb.aspx) I assume, it's your Model, and i will speak about it as about Model class.
Include Filter needs Expression as a parameter, not a string, you should construct is as a expression. That's the second thing. Third, you should combine your includes as a chain, So your part of creating Include Filter from string List should look like:
var filter = Builders<Model>.Filter.Empty;
var fields = Builders<Model>.Projection.Include(x => x.Id);
foreach (var item in Props)
{
var par = Expression.Parameter(typeof(Model));
var prop = Expression.Property(par, item);
var cast = Expression.Convert(prop, typeof(object));
var lambda = Expression.Lambda(cast, par);
fields = fields.Include((Expression<Func<Model, object>>)lambda);
}
I have all expresiions separate for better understanding: first you create Parameter (x=>), than you add property (x=>x.Property1), than you should cast it to object, and after all create Lambda Expression from it.
And now the last part: You don't need all of it, Include function could get jsut a string as a parameter. So you could instead of all expression call write this:
fields = fields.Include(item);

how to get list of one entity set from another entity set in a many to many relationship

1I am working with controller code in MVC.
I have the follow code working where the are related entities priority list based on teams:
public JsonResult GetPrioritiesByTeam(int id)
{
List<Priority> priorities = new List<Priority>();
if (id > 0)
{
priorities = db.Priorities.Where(p => p.Team == id ).ToList();
}
else
{
priorities.Insert(0, new Priority { Id = 0, PriorityDescription = "--Select a Team first--" });
}
var result = (from r in priorities
select new
{
id = r.Id,
name = r.PriorityDescription
}).ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
The statement
priorities = db.Priorities.Where(p => p.Team == id
does get me the list of priorities based on the team passed in by the parameter id.
I am now try to achieve the same successful result with another entity that is related to team- an entity called 'members'. However the relationship is not a one to many. Its a many to many- so I have an intermediate entity call 'teammembers'.
The Team is still the entity driving the choice. When the team is chosen, I need it to give me all the team member entries in teammembers, and then I need similar to the priorities code above, give me a members list which is based on the teammembers identified based on the team selected.
I think based on the code provided the else clause would be similar coding along with the var result coding.
I just cant seem to get the coding right for the clause in the condition for id >0.
How do I write that clause for the many to many so as to get the list of members for the team through teammembers?
You can assume entity team is Id and TeamDescription, entity Member is Id and MemberName, and entity teammember is an ID,and teamId and MemberId -just a straightforward many to many.
Been trying with LINQ method syntax and LINQ query syntax but cant get it working.
Thanks for any help you can provide
New code would look like the following except i need working id>0 clause:
public JsonResult GetMembersByTeam(int id)
{
List<Member> members = new List<Member>();
if (id > 0)
{
**members = Members.Where(needed clause).ToList();**
}
else
{
members.Insert(0, new Member { Id = 0, MemberFullName = "--Select a Team first--" });
}
var result = (from m in members
select new
{
id = m.Id,
name = m.MemberFullName
}).ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
Entity Diagram
Current code with suggested changes
public JsonResult GetMembersByTeam(int id)
{
List<Member> members = new List<Member>();
if (id > 0)
{
//members = db.Teams.Where(p => p.Id == id)
// .SelectMany(e => e.TeamMembers)
// .Select(e => e.Member)
// .ToList();
members = db.TeamMembers.Where(p => p.Team == id)
.Select(e => e.Member)
.ToList();
}
else
{
members.Insert(0, new Member { Id = 0, MemberFullName = "--Select a Team first--" });
}
var result = (from m in members
select new
{
id = m.Id,
name = m.MemberFullName
}).ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
results in error :
Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List,ManageHR5.model.Member>'
try this:
members = db.TeamMembers.Where(p => p.Team == id )
.Select(e => e.Member1)
.ToList();

EF 4.1 DBContext - Apply filter loading related entities

I'm using EF 4.1 DBContext API, and I'm trying to load a parent entity named User and then explicitly load related Project entities using a .Where(x => x.IsEnabled) filter:
I found a suggested approach here under "Applying filters when explicitly loading related entities". But I cannot figure out why user.Projects is not being populated. Using SQL Profiler, I have verified I'm querying Projects and returning the data. But it's not being loaded into my user object.
Any thoughts?
Here is my code:
public User GetUser(string userName)
{
try
{
using (var context = new Entities())
{
var user = context.Users.FirstOrDefault(x => string.Compare(x.UserName, userName, true) == 0);
context.Entry(user).Collection(x => x.Projects).Query().Where(x => x.IsEnabled).Load();
//TODO: I expect user.Projects.Count() > 0.
}
}
catch (Exception ex)
{
LogWrapper.DumpException(ex);
}
return null;
}
User.projects = context.projects.select(x => x.user).where(x => x.isenabled);
Came across this problem recently, this is what I did. Hope you find it useful.
var query = context.User.Where(x => x.UserName == userName)
.Include(x => x.Projects.All(p => p.IsEnabled == true))
var data = query.ToList();