LINQ Include nearby collection (nearby table) and property - c#

I have a class Users with next format:
List<Subscriptions> Subscriptions {get;set;}
And class Subscriptions contains:
SubscriptionType type {get;set;}
I'd like to include all of these into User object, something like this:
var _referredUser = ctx.Users
.Include(x=>x.Subscriptions.Where(y=>y.Status==true))
.ToList()
.FirstOrDefault(y => y.Email == _all[i].Referred_email);
I can successfully include Subscriptions collection, but I'm not sure how can I go further beyond Subscription collection and include the property SubscriptionType into User object, if it's even possible?
My other question as well is whether I can only include those subscriptions that have status ==true, because user can have multiple records in subscriptions table and only one that is set to true?
I tried something like this, but it throws me an error:
.Include(x=>x.Subscriptions.Where(y=>y.Status==true))
The error is:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Can someone help me out with this LINQ ?

You can't filter in an Include, I recommend you do this:
var email=_all[i].Referred_email;
var _referredUser = ctx.Users.Include(x=>x.Subscriptions.Select(y=>y.SubscriptionType))
.FirstOrDefault(y => y.Email == email && y.Subscriptions.Any(y=>y.Status));
As the exception said, you must refer a navigation property in the Include

Something like this should work...
var email = _all[i].Referred_email;
var _referredUser = ctx.Users
.Include(x=>x.Subscriptions.Select(y=>y.SubscriptionType))
.Select(x => new { User = x, Subscriptions = x.Subscriptions.Where(subscription => subscription.Status)})
.Where(x => x.Subscriptions.Any())
.Select(x => x.User)
.FirstOrDefault(y => y.Email == email);
To include the child properties you can use Select in the include statement.
To only get the results where Subscriptions.Status is true you will need to do a separate query or do some Select() magic.

Related

Return Selected Properties of Single Object in EF Method Syntax

//This works, but seems incorrect to me
Object selection = db.ExampleTable
.Where(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow })
.SingleOrDefault();
//This seems correct, but does not work
Object selection = db.ExampleTable
.SingleOrDefault(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow });
db is our Entity Framework data context.
My goal is to select a single entry matching the provided id in ExampleTable. If an entry is not found, this is to return null. However, EF doesn't seem to let me select a single object and then only return specific properties. How do I accomplish this or is the first example I provided correct?
I did check this question:
select properties of entity ef linq:
Unfortunately you cannot conditionally load properties of related entity - you either load whole door entity, or don't include that entity.
But the answer just doesn't seem right, but obviously "seems" is a very weak statement.
Your first method is correct:
//This works, but seems incorrect to me
Object selection = db.ExampleTable
.Where(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow })
.SingleOrDefault();
Your second method gets you a single object, not an IQueryable<T> object that LINQ would work with. If you want to convert from one type of object to another, that isn't a LINQ thing. You can still, but it'll be more convoluted. Something like:
var selection =...;
var newselection=new { Id=selection.Id, PropIWantToShow=selection.PropIWantToShow };
but this is very bad because you DID retrieve the entire object from the DB, and then just threw away most of it. Your first method only returns 2 fields from the DB.
If you want your function to return null if condition doesn't match then use FirstorDefault() instead of SingleorDefalut(). So if you want to match an id and return an object then do it like this :
return db.ExampleTable.FirstorDefault(c=>c.Id == id);

LINQ Query comparing Id with an Id in a list inside SingleOrDefault query

I am trying to fetch an option using the SingleOrDefault Linq to SQL method.
var po = repository.Context.AsQueryable<Option>().SingleOrDefault(o => o.Option.Id == sp.Options // sp.Options is a collection);
The problem is that inside the SingleOrDefault method I am comparing p.Option.Id == a collection. What I want is to select the option from sp.Options that matches the o.Option.Id. How can I do that?
UPDATE:
One thing I should have mentioned that the sp.Options is a different class than the Option class. sp.Options is SPOptions class so I cannot pass it inside the contains method.
Take a look at Contains.
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If Options is not a collection of the class of Option.Id, you can use the Any method with your comparison logic in it as follow :
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Any(opts => opts.Something == o.Option.Id));
Search using Contains (sp.Options.Contains(o.Option.Id)) like:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If members of sp.Options are different from Id then you can do:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Any(r=> r.Id == o.Option.Id));
or
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Select(r=> r.Id).Contains(o.Option.Id));
Assuming Id is the field in sp.Options elements that you want to compare with.
Based on your question it seems you're expecting to have a single match between those two option sets, correct ?
If so, I'd suggest you to write it as:
var po = repository.Context.AsQueryable().Where(o => sp.Options.Any(item=>item.id == o.Option.Id)).SingleOrDefault();

asp.net MVC Where in List

I building my first application with c# and sp.net MVC 5, so far so good :)
Now I have a problem, we using 2 User Tables, first one contains the username, other the user data.
string user = User.Identity.Name;
var data = db.FE_Benutzer;
var collection = data.Where(o => o.Benutzername == user).Select(x => new
{
id = x.ID,
name = x.Name,
hauptbereiche = x.Hauptbereich.ToList()
});
var dataHauptbereich = db.Hauptbereich;
var collectionHauptbereich = dataHauptbereich.Where(o => collection.ElementAt(0).hauptbereiche.Contains(o)).Select(x => new
{
id = x.ID,
name = x.Name
});
return Json(collectionHauptbereich, JsonRequestBehavior.AllowGet);
I getting this error
LINQ to Entities does not recognize the method '<>f__AnonymousType63[System.Int32,System.String,System.Collections.Generic.List1[scorring.Models.Hauptbereich]] ElementAt[<>f__AnonymousType63](System.Linq.IQueryable1[<>f__AnonymousType63[System.Int32,System.String,System.Collections.Generic.List1[scorring.Models.Hauptbereich]]], Int32)' method, and this method cannot be translated into a store expression.
hauptbereiche = x.Hauptbereich.ToList()
contains a list of ids where the user have premission to.
When I fetching the data
dataHauptbereich.Where
I wont to include only the ids I have in the list
how is this possible?
Entity Framework doesn't know how to turn ElementAt into SQL. See this answer for more information: Getting the first result from a LINQ query - why does ElementAt<T>(0) fails when First<T>() succeeds?
Try
dataHauptbereich.Where(o => collection.ElementAt(0).hauptbereiche.Any(h => h.ID == o.ID))
Or
dataHauptbereich.Where(o => collection.Any(c => c.hauptbereiche.Any(h => h.ID == o.ID)))
I'm having a bit of a time deciphering exactly what you're trying to achieve with your code here, but it looks to me like your simply querying Hauptbereichs that belong to a particular user. Your first query selects an anonymous object composed of id, name and hauptbereiche, but of these you only ever use the hauptbereiche property. Then, in your second query, you merely selecting Hauptbereichs that match an item in this hauptbereiche property's collection. Actually, here, you're only comparing values from the first item in the original collection, which begs the question of why you're selecting anything other than the first item. That, and this second query is entirely redundant because if the items match that means you already had the items in the first place. You could get the same info directly from collection.ElementAt(0).hauptbereiche without issuing the second query.
So, here's a couple of simpler options:
If you're trying to get all the Hauptbereichs that belong to all the FE_Benutzers where Benutzername == user then just do:
var collectionHauptbereich = db.FE_Benutzer.Where(m => m.Benutzername == user)
.Include(m => m.Hauptbereich)
.SelectMany(m => m.Hauptbereich);
If you want just the first FE_Benutzer item's Hauptbereichs, then do:
var benutzer = db.FE_Benutzer.Where(m => m.Benutzername == user)
.Include(m => m.Hauptbereich)
.FirstOrDefault();
var collectionHauptbereich = benutzer != null
? benutzer.Hauptbereich.ToList()
: new List<Hauptbereich>();

to get specific item from list obltained through LINQ

I have following code,
var applications = from a in applications1.Entities.ToList()
select new
{
name = a.Attributes["sia_name"].ToString(),
applicationId = a.Attributes["sia_applicationid"].ToString(),
isDraftMode = a.Attributes.Contains("sia_applicationmode") ? a.FormattedValues["sia_applicationmode"].ToString().ToLower() == "draft" ? true : false : false
};
in this code I have applications which contains name ,applicationid,and isdraftmode,
now I want to fetch applicationid from var application so that I can pass it to session
how can it be achive
Session["ApplicationID"]=applications
do it like
Session["ApplicationID"]=applications.Select(m => m.applicationId).First();
it would give you first applicationId. If you want to select applicationId based on condition then you can do it like
Session["ApplicationID"]=applications.Select(m => m.applicationId).First(x => x.property == condition);
and for all the applicationid do it like
Session["ApplicationID"]=applications.Select(m => m.applicationId).ToList();
So you have a query which selects multiple properties in an anonymous type and you need to select one specific property from it to write it into session?
Session["ApplicationID"] = applications
.Select(x => x.applicationId)
.ToList(); // i would "materialize" it in a collection to avoid side effect due to lazy evaluation
or do you instead want to get a specific item from it, for example via sia_name?
var sia = applications.FirstOrDefault(x => x.sia_name == "InsertName");
if(sia != null)
Session["ApplicationID"] = sia.applicationId;

Entity Framework include Where

If I have a query that looks like this:
var forms = repo.GetForms().Where(f => f.SubForms.Any(sf => sf.Classes.Any(c => c.TermId == termId)));
From this you can see my schema is as follows:
SubForm has many Class which has many Term.
What I want:
All SubForms with their Classes In a particular Term.
What is happening now is that I get all the SubForm that has any Class in a particular Term. That means that SubForm comes back with ALL child Class and not just the ones related to the Term.
For eg. I have 2 terms, a subform with 2 classes in each term. This query brings back 4 classes instead of the 2 in that particular term.
Is there any Include('Expression') that I can use to say that I only want to include all classes based on a condition? Or is my query wrong?
Use this:
var subForms = repo.GetSubForms.Select(sf = new {
SubForm = sf,
Classes = sf.Classes.Where(c => c.TermId == termId)
}).ToList()
.Select(t => t.SubForm)
.ToList();
UPDATE: based on #Slauma's comment:
If you want to load SubForms that they have any Class that has a Term by termId, you can go from end to begin; like this:
var subForms = repo.Terms.Where(t => t.Id == termId).Select(t => new {
Term = t,
Class = t.Class,
SubForm = t.Class.SubForm
}).ToList()
.Select(t => t.SubForm).ToList();
OR in a easiest way, you can use Include on your Term, see:
var subForms = repo.Terms.Include("Class.SubForm").Where(t => t.Id == termId)
.Select(t => t.Class.SubForm).ToList();
NOTE: As I can understand from your question, you have a relationship like this:
SubForm has_many Class has_many Term
But, your provided code is showing a relationship like this one:
SubForm has_many Class
Term has_many Class
If you can, put your entities in question, or explain their relationship more please. Thank you.
An Include(Where Expression) does not exist. If you use eager loading with Include you will always load all the elements.
There is a way around this by using projections. The basic idea is you will select a new anonymous type with the property you want and another property with the filtered navigational items. EF will link those together and as a result you will fake a Include(Where ... )
Check this for an example.
You know sometimes I start getting lost in fancy LINQ extension methods and attempting to figure out how to eagerly load exactly what I want and resort to a very simple "join" concept.
var result =
(from f in SubForums
from c in Classes
from t in Term
where t.TermId = 1
select new { SubForum = f, Class = c, Term = t }).ToList();
This is a simple join that uses predefined navigation properties (hence you don't have to specify the join condition). You return an anonymous type with everything that you need. The beauty of this is that Entity Framework will do the auto-fixup for you, therefor you're free to return the SubForum only from your method if you wish, it will automatically contain the Class and subsequent Term references.
I don't know the exact names of the relations, but it should be something along the lines of
repo.Terms
.Include("Classes")
.Include("Classes.SubForms")
.SingleOrDefault(x => x.TermId = termId);
// or
repo.GetSubForms
.Include("Classes")
.Where(sf => sf.Classes.Where(c => c.TermId == termId));
This seems to be a common request, it was difficult to find a solution to it when I was looking earlier this year. I ended up using the solution included in the link below (I'm not sure this is the exact solution I found, but it's the same idea). Hope this helps!
Filter the "Includes" table on Entity Framework query
//Found this method to filter our child objects instead of using .include()
var Results = (from res in
(from u in DataContext.User
where u.Type.ToUpper() != "ADMIN"
&& u.StartDate <= DateTime.Now
&& (u.EndDate == null || u.EndDate >= DateTime.Now)
select new
{
User = u,
Access = u.Access.Where(a => a.StartDate <= DateTime.Now
&& (a.EndDate == null || a.EndDate >= DateTime.Now))
}
)
select res);
//The ToArray is neccesary otherwise the Access is not populated in the Users
ReturnValue = Results.ToArray().Select(x => x.User).ToList();

Categories

Resources