Entity Filter child without include - c#

i'm a C# developer and i have a trouble with Entity Framework 5.
I have mapped my database with Entity using the default code generation strategy. In particolar there are three classes: menus, submenus and submenuitems.
The relationships about three classes are:
one menu -> to many submenus
one submenu -> to many submenuitems.
All classes have a boolean attribute called "Active".
Now, i want to filter all the Menus with the SubMenus active, and the SubMenus with the SubMenuItems active.
To get this i've tried this:
var tmp = _model.Menus.Where(m => m.Active)
.Select =>
new
{
Menu = x,
SubMenu = x.SubMenus.Where(sb => sb.Active)
.Select(y =>
new
{
SubMenu = y,
SubMenuItem = y.SubMenuItems.Where(sbi => sbi.Active)
})
})
.Select(x => x.Menu).ToList();
But didn't work.
Someone can help me?
Thank you for your help!

Hi have you see this post? Entity Framework: Querying child entities. there are some difference from your code, maybe this helps you.
UPDATE: Projection is when the result of a query is output to a different type than the one queried, it can be to an anonymous type, but could also be to a concrete type. And so using Data transfer object can be usefull to pass data between processes you can read a full explaination here Data Transfer objects
public class MenuDto
{
public int MenuId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public List<MenuDto> SubMenus { get; set; }
}
_model.Menus.Where(m => m.Active)
.Select(p => new MenuDto
{
MenuId = p.idField,
Name = p.NameField,
Url = p.UrlField,
SubMenus = p.SubMenus.Where(sb => sb.Active)
.Select(y => new MenuDto
{
MenuId = y.idField,
Name = y.NameField,
Url = y.UrlField,
SubMenuItem = y.SubMenuItems.Where(sbi => sbi.Active)
.Select(z => new MenuDto
{
MenuId = z.idField,
Name = z.NameField,
Url = z.UrlField
})
})
}).ToList();
I hope this can solve your problem

can you try this:
List<SubMenuItems> tmp = _model.menus.Where(a => a.active)
.SelectMany(b => b.SubMenus.Where(a => a.active)).ToList()
.SelectMany(c => c.SubMenuItems.Where(a => a.active)).ToList();
I hope it's helping.

Related

loading related entities

We are using EF .NET core for our architecture and want to do a basic query. So all we are after is using both LINQ & EF with lazy loading switched off to select the parent, in this case stick item and some of the fields in the child objects. Then return them back into our strongly typed item.
Something like this.
var qry = _context.Set<stock>()
.Include(p => p.stockitem)
.Where(q => q.customer == accountNo)
.Select(r => new stock() {
customer = r.customer,
r.stockitem.select(s => new {id, s.id, s.name }})
.ToList();
So is it possible to do this? basically get hold of say just a couple of columns from our child object. Then have everything returned in the strongly typed object.
First create a model in which the selected data will be stored (This model is just an example):
public class MyNewCustomer
{
public string CustomerAccount { get; set; }
public Dictionary<int, string> Items { get; set; }
}
After that you can create a new object from your select:
var data = _context.Stocks
.Include(p => p.stockitem)
.Where(p => p.customer == accountNo)
.Select(p => new MyNewCustomer
{
CustomerAccount = p.customer,
Items = p.stockitem.ToDictionary(s => s.id, s => s.name)
}).ToList();
PS: I used Dictionary because you didn't provide the actual model. You can choose whatever kind of List<TObject> you want.

Cannot implicit convert type List to IEnumerable while grouping with Linq

New to MVC and Linq.
I'm was able to display all records just fine but now I was trying to get a count of records by name and just select 2 fields:Name and Count
I thought I should create a new ViewModel, fill the Name,Count and send it to the view.
public ActionResult Index()
{
var load =
db.loadingPPHs.Where(s => s.WORKDAY == db.loadingPPHs.Max(x => x.WORKDAY))
.GroupBy(fu => fu.TMNAME)
.Select(g => new {Name = g.Key, Count = g.Count()}).ToList();
var viewModel = new loadingViewModel
{
LoadingListCount = load
};
return View(viewModel);
}
The linq above works as expected.
ViewModel:
public class loadingViewModel
{
public IEnumerable<LoadingListCount> LoadingListCount { get; set; }
}
public class LoadingListCount
{
public string Name{ get; set; }
public int Count { get; set; }
}
However, I'm getting an error. Cannot implicitly convert type 'System.Collections.Generic.List<>' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)
I have trying converting the query to list and to IEnumerable but no luck. I've searched around other posts but I have not have luck with them.
You are getting the error because of two main things, first one is the Linq produces a collection of an anonymous type, and the attached .ToList() gives you the result as List of anonymous type objects. But the expected result would be of type IEnumerable<LoadingListCount> so here we need to do few changes, First of all, we have to create an object of type LoadingListCount now the Linq will give you the output as expected, But the attached .ToList() will convert them to List<LoadingListCount> to avoid that we have to remove them as well. Finally, the query will look like the following:
var load = db.loadingPPHs.Where(s => s.WORKDAY == db.loadingPPHs.Max(x => x.WORKDAY))
.GroupBy(fu => fu.TMNAME)
.Select(g => new LoadingListCount {Name = g.Key, Count = g.Count()})
.Select(g => new {Name = g.Key, Count = g.Count()})
produces objects of anonymous type, and puts them into a list. Since you need IEnumerable<LoadingListCount>, not IEnumerable<SomeAnonymousType> create instances of LoadingListCount instead by specifying the type in the invocation of new operator:
.Select(g => new LoadingListCount {Name = g.Key, Count = g.Count()})
Your query is creating a collection of anonymous objects - you need to project your query into your model
IEnumerable<LoadingListCount> load =
db.loadingPPHs.Where(s => s.WORKDAY == db.loadingPPHs.Max(x => x.WORKDAY))
.GroupBy(fu => fu.TMNAME)
.Select(g => new LoadingListCount { Name = g.Key, Count = g.Count() })
var viewModel = new loadingViewModel
{
LoadingListCount = load
};

c# MVC Pass Group Data to View

I have a view where I display a model, but at the model I want to also display a small table of data from another model that comes from the following linq in the controller
var StudentRatings = db.LearnerRatings
.Where(i => i.LessonId == id)
.GroupBy(i => i.Rating)
.Select(group => new { Rating = group.Key, TotalCount = group.Count() });
ViewBag.Ratings = StudentRatings;
I'm already returning another model from the controller, so have added this to the ViewBag.
I thought I would be able to iterate through these with a foreach
foreach (var ratings in ViewBag.Ratings){
#ratings.TotalCount
}
but get an error - Additional information: 'object' does not contain a definition for 'TotalCount'
I take it this has to be cast? If so what does it get casted to?
Is this the best approach for the above?
Anonymous types should not traverse method boundaries: that is, your View doesn't know what type ratings is.
Change your anonymous type to a named struct or class, then you can do it like so:
class Rating {
public Int32 Rating { get; set; }
public Int32 TotalCount { get; set; }
}
...
.Select(group => new Rating { Rating = group.Key, TotalCount = group.Count() });
...
foreach(Rating rating in ViewBad.Ratings) {
#rating.TotalCount
}

GROUP BY and HAVING clauses in nHibernate QueryOver

I'm trying to write this specific sql query in nHibernate QueryOver language, which I am not very familiar with:
SELECT MessageThreadId FROM MessageThreadAccesses
WHERE ProfileId IN (arr)
GROUP BY MessageThreadId
HAVING COUNT(MessageThreadId) = arr.Count
where arr is a array of integers(user Ids) I'm passing as argument and MessageThreadAccess entity looks like this:
public virtual MessageThread MessageThread { get; set; }
public virtual Profile Profile { get; set; }
....
After reading multiple stack overflow threads and experimenting I got this far with my query (trying to get MessageThread object - it should always be just one or none), but it still doesn't work and I'm not really sure what else to try. The query always seems to be returning the MessageThreadAccess object, but when reading it's MessageThread property it's always NULL.
var access = Session.QueryOver<MessageThreadAccess>()
.WhereRestrictionOn(x => x.Profile).IsIn(participants.ToArray())
.Select(Projections.ProjectionList()
.Add(Projections.Group<MessageThreadAccess>(x => x.MessageThread))
)
.Where(
Restrictions.Eq(Projections.Count<MessageThreadAccess>(x => x.MessageThread.Id), participants.Count)
)
.TransformUsing(Transformers.AliasToBean<MessageThreadAccess>())
.SingleOrDefault();
return Session.QueryOver<MessageThread>()
.Where(x => x.Id == access.MessageThread.Id)
.SingleOrDefault();
Can someone point me in the right direction, or explain what am I doing wrong?
Thanks in advance.
I guess you may try using a DTO for storing the result, instead of trying to fit the result in a MessageThreadAccess, when it is not one (no Profile).
Maybe you can try :
public class MessageThreadCountDTO
{
public MessageThread Thread { get; set; }
public int Nb { get; set; }
}
then
var profiles = new int[] { 1,2,3,4 };
MessageThreadCountDTO mtcDto = null;
var myResult =
_laSession.QueryOver<MessageThreadAccess>()
.WhereRestrictionOn(x => x.Profile.Id).IsIn(profiles)
.SelectList(list =>
list.SelectGroup(x => x.MessageThread).WithAlias(() => mtcDto.Thread).
SelectCount(x => x.MessageThread).WithAlias(() => mtcDto.Nb)
)
.Where(Restrictions.Eq(Projections.Count<MessageThreadAccess>(x => x.MessageThread), profiles.Count()))
.TransformUsing(Transformers.AliasToBean<MessageThreadCountDTO>())
.List<MessageThreadCountDTO>().FirstOrDefault();
would profiles be a Profile[], and not an int[], then the following line :
.WhereRestrictionOn(x => x.Profile.Id).IsIn(profiles)
should be :
.WhereRestrictionOn(x => x.Profile).IsIn(profiles)
Hope this will help

RavenDB workaround for nested LINQ expression

I have simple set of objects stored in RavenDB:
public class Question
{
public string Id { get; set; }
public DateTime CreatedOn { get; set; }
public ICollection<User> Supporters { get; set; }
public ICollection<Answer> Answers { get; set; }
}
public class Answer
{
public string Id { get; set; }
public bool IsOfficial { get; set; }
}
Now I want to query RavenDB to give me set of questions, ordered firstly by number of supporters, next by condition - if a question has any official answer, and in the and, by question creation date. So I've written a query:
var questions = DocumentSession.Query<Question>().AsQueryable();
questions = questions
.OrderByDescending(x => x.Supporters.Count)
.ThenByDescending(x => x.Answers.Any(a => a.IsOfficial)) //EDIT: source of exception
.ThenByDescending(x => x.CreatedOn)
.Take(15);
var result = questions.ToList();
which throws an exception:
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'
The query is logically correct and works, when I use linq-to-objects, and simply add .ToList() to first line:
var questions = DocumentSession.Query<Question>().Tolist().AsQueryable();
// next lines stay unchanged
I don't want to do it because of performance issues (this change forces all questions to be loaded from database into memory before filtering).
How to make this working without performance impact ? Maybe shell I define an index ? How it should looks like then ?
A custom index for your purposes is basically going to be a recreation of your class with extra fields in it (and some logic to support it). It seems like you don't want to have to add more fields to your current class, are you okay with adding more classes to your project?
Here's an example:
public class Question_WithAnyOfficial: AbstractIndexCreationTask<Question>
{
public class Question_WithAnyOfficial()
{
Map = questions => from question in questions
// New Anonymous Type
select new
{
Id = question.Id,
CreatedOn = question.CreatedOn,
Supporters = question.Supporters,
Answers = question.Answers,
AnyOfficial = question.Answers.Where(a => a.IsOfficial).Any()
};
}
}
Then you can query this:
var questions = DocumentSession.Query<Question_WithAnyOfficial>()
.OrderByDescending(x => x.Supporters.Count)
.ThenByDescending(x => x.AnyOfficial)
.ThenByDescending(x => x.CreatedOn)
.Take(15)
.ToList();
Don't forget that you'll have to register the index when your app starts.
Raven can't support calculations like that inside the LINQ query, so this should work (problem clause removed):
var questions = DocumentSession.Query<Question>()
.OrderByDescending(x => x.Supporters.Count)
//.ThenByDescending(x => x.Answers.Any(a => a.IsOfficial))
.ThenByDescending(x => x.CreatedOn)
.Take(15);
var result = questions.ToList();
If you want to include that logic, you need a field on your class called AreAllAnswersOfficial (or something similar). Then you can put that inside the clause:
var questions = DocumentSession.Query<Question>()
.OrderByDescending(x => x.Supporters.Count)
.ThenByDescending(x => x.AreAllAnswersOfficial)
.ThenByDescending(x => x.CreatedOn)
.Take(15);
var result = questions.ToList();
Based on Bear Alexander response, I've done this like that:
public class QuestionByAnyOfficial : AbstractIndexCreationTask<Question, QuestionByAnyOfficial.Result>
{
public class Result
{
public string Id;
public bool AnyOfficial;
public int SupportersCount;
public DateTime CreatedOn;
}
public QuestionByAnyOfficial()
{
Map = questions => from question in questions
select new
{
Id = question.Id,
AnyOfficial = question.Answers.Any(a => a.IsOfficial),
SupportersCount = question.Supporters.Count,
CreatedOn = question.CreatedOn
};
}
}
var questionIds = DocumentSession.Query<QuestionByAnyOfficial.Result, QuestionByAnyOfficial>()
.OrderByDescending(x => x.SupportersCount)
.ThenByDescending(x => x.AnyOfficial)
.ThenByDescending(x => x.CreatedOn)
.Take(NumberOfQuestions)
.Select(x => x.Id);
var questions = DocumentSession.Load<Question>(questionIds);
var result = questions.ToList();
It works and I believe it is more efficient than my original version. If it can be done in any more elegant way, I'd appreciate any ideas. Regards.

Categories