Eager loading with Include() - entity-framework

Imagine this case:
var locations = from Locations in this.LocationDataContext.Locations
.Include("ChildLocations")
where
(Locations.LocationType.ID == 3)
select
Locations;
This query will load all locations with type == 3 and all the related child locations, ok. But what i'm trying to figure out is how to filter the child locations that are being loaded. What if location have 3milion child locations?
Maybe something like this? (doesnt work because ChildLocations is a set of entities)
var locations = from Locations in this.LocationDataContext.Locations
.Include("ChildLocations")
where
(Locations.LocationType.ID == 3) &&
(Locations.ChildLocations.LocationType.ID == 2)
select
Locations;
Thank you.

The Entity Framework will never materialize a partially-complete instance. You cannot, in other words, materialize a Location with only some of its ChildLocations. This would be an "incomplete" object, and the Entity Framework does not allow this.
However, there are workarounds. If you only need the information from the ChildLocations and not from the Location itself, just select that:
from Locations in this.LocationDataContext.Locations
where Locations.LocationType.ID == 3
from ChildLocation in Locations
where ChildLocation.LocationType.ID == 2
select ChildLocation;
In this case, since we are only selecting the ChildLocations, it is OK to only select a few of them, since they can be materialized completely. It is only when materializing the Location that we need all of the children.
Another workaround is to materialize partial Location information into an anonymous type. This allows you to get information about both the Location and some of the ChildLocations without violating the rule that instances can only be materialized in their complete form. Since you're not actually materializing a real Location, there is no requirement to materialize the entire thing:
from Locations in this.LocationDataContext.Locations
where Locations.LocationType.ID == 3
select new
{
ID = Locations.ID,
LocationType= Locations.LocationType
ChildLocations = from ChildLocation in Locations
where ChildLocation.LocationType.ID == 2
select ChildLocation
}

Related

How to change the value of a tables attribute using LINQ/LAMBDA?

I have an order table and an orderStatus table, their relationship is as seen below:
I want to be able to change 'StatusID' to the value 2, of a specific order (i am able to get the specific order ID, and have loaded it into an integer variable) using a lambda expression within an action result - would there be any easy way of doing this?
So far i have tried:
//get specific order ID
int currentOrderId = newConfirmedOrderLine.OrderID;
//-----
Order statusChange = new Order();
statusChange.OrderStatus.StatusID = 2;
DBAccessor.SaveChanges();
I am new to linq and lambda, so any explanation with an answer would be greatly appreciated!
If DBAccessor is a DbContext then this could/should work. You need to load the Order entity that you want to change from the DBAccessor.Order DbSet, change it by setting a property, and then call SaveChanges.
var orderStatus = DBAccessor.OrderStatus.First(x => x.StatusID == 2);
var order = DBAccessor.Order.Find(currentOrderId);
order.OrderStatus = orderStatus;
DBAccessor.SaveChanges();

How can I create Hierarchical Container in Vaadin when I am having the duplicate ItemIds? If No, What is the alternate?

I want to create a tree of user with n level Hierarchy. I have a POJO object and within that I have id,parent_id.
The problem is user can belong in more than 1 group. So, when I am trying to do,
while (iterator.hasNext()) {
val user_pojo_obj = iterator.next()
val key = user_pojo_obj.id
val parent_key = user_pojo_obj.family_id
var child: Item = container.addItem(key)
child.getItemProperty("caption").asInstanceOf[Property[Any]].setValue(user_pojo_obj.name)
child.getItemProperty("POJOobj").asInstanceOf[Property[Any]].setValue(user_pojo_obj)
container.setParent(key, parent_key)
}
I got NullPointerException at the 2nd line, As per my knowledge it because of the addItem() duplication in container, which returns null.
Please suggest me the alternate if this can not be improve. (Using Scala)
Thanxx..
As far as I know, you can not have multiple parents or duplicate itemIds. The following pseudo-code is an alternate solution which builds the tree-like structure (node is your POJO):
counter = 0;
function process(nodes, parent) {
foreach (node in nodes) {
newId = counter++;
item = container.addItem(newId);
// set item caption etc.
if (parent not null)
container.setParent(newId, parent)
process(getNodesWithParent(node), newId);
}
}
process(getNodesWithParent(null), null);
The method getNodesWithParent needs to be defined by you. I guess you will take the iterator, iterate through your POJOs and return those with family_id equals the parameter's id. The overall performance depends on your implementation of getNodesWithParent, so if you have a large data set you should care to be efficient.

Core data multiple Entities

I have a problem regarding core data.
I have an app with multiple tabs. Every tab holds a list of different "objects".
In each tab i have an add button (+) that takes me to a screen where i can add an "object".
The problem :
When i click add an entity for insert is being created and added to the context.
So, i go on first tab, click add - an entity is in context.
I go on other tab, i click add - another entity is in context.
I finish adding information for one of them ( to be valid ) and hit save.
Now core data throws an exception saying that could not save the context because the other entity it's not valid.
One idea that came into my mind was :
Copy all objects that are invalid from context, save the context, add the copied ones back (and so on when needed).
But an entity could have other relations with other entities so Person could have address, role, Contact Details.. and Company could have Address, Employes .. etc.
If person is invalid and has relationship Address valid , above idea fails because will not save person but will save address , what it's not correctly.
Something with a temporarily context could work but i don't have an clear idea how to implement this.
Another idea is to take all relationships for an entity (eg Person) when i want to save it, and save only Person.
But i failed to implement an recursive function ( the problem lays in the fact that relationships are inverse and because of many many relationships on my coredata model (person has contact details and also company) ).
Here is the code :
- (void)relationshipInstancesForManagedObject:(NSManagedObject *)managedObject
andSkipValue:(id)skipValue
andSet:(NSMutableSet *)set
{
for (NSRelationshipDescription *relationship in managedObject.entity.properties)
{
if (![relationship isKindOfClass:[NSRelationshipDescription class]]) continue;
id value = [managedObject valueForKey:relationship.name];
if (value == skipValue || value == nil || value == self || [set containsObject:value] ) continue;
NSLog(#"%#",value);
if (relationship.isToMany)
{
if ([value containsObject:skipValue] || [value containsObject:managedObject] || [value count] == 0 ) {
continue;
}
for (NSManagedObject *entity in value)
{
[set addObject:entity];
[self relationshipInstancesForManagedObject:entity
andSkipValue:skipValue
andSet:set];
}
} else {
if (value != nil) {
[set addObject:value];
[self relationshipInstancesForManagedObject:value
andSkipValue:skipValue
andSet:set];
}
}
}
}
If you have any ideas, I would be grateful.
The simplest solution to your problem is to make sure that all objects are valid when you add them to the store. Collect the information you need to create a valid object first, and only then add a new object along with all the required information.
Also, consider carefully whether you need to require all the properties that you currently do. If you have two entities that each have a relationship to the other, do you need to require both relationships? Could you make one of the relationships optional? That can help you avoid a chicken and egg problem where it's impossible to add objects for either entity because none of the other type exist yet.

EF builds EntityCollection, but I (think I) want IQueryable

I have an entity A with a simple navigation property B. For any given instance of A, we expect several related thousand instances of B.
There is no case where I call something like:
foreach(var x in A.B) { ... }
Instead, I'm only interested in doing aggregate operations such as
var statY = A.B.Where(o => o.Property == "Y");
var statZ = A.B.Where(o => o.CreateDate > DateTime.Now.AddDays(-1));
As far as I can tell, EF instantiates thousands of references to B and does these operations in memory. This is because navigation properties use EntityCollection. Instead, I'd like it to perform these queries at the SQL level if possible.
My current hunch is that Navigation Properties may not be the right way to go. I'm not attached to EF, so I am open to other approaches. But I'd be very interested to know the right way to do this under EF if possible.
(I'm using EF4.)
CreateSourceQuery seems to do the trick.
So my examples would now be:
var statY = A.B.CreateSourceQuery().Where(o => o.Property == "Y");
var statZ = A.B.CreateSourceQuery().Where(o => o.CreateDate > DateTime.Now.AddDays(-1));
There's one thing you should know. Members that derives from IQueryable<> are executed on the server, not in memory. Members which are derived from IEnumerable<> is executed in memory.
for example
var someEntities = db.SomeEntities; <-- returns an IQueryable<> object. no data fetched. SomeEntities table may contain thousands of rows, but we are not fetching it yet, we are just building a query.
someEntities = someEntities.Where(s => s.Id > 100 && s.Id < 200); <-- creates expression tree with where statement. The query is not executed yet and data is not fetched on the client. We just tell EF to perform a where filter when query will execute. This statement too returns an IQueryable<> object.
var entities = someEntities.AsEnumerable(); <-- here we tell EF to execute query. now entities will be fetched and any additional linq query will be performed in memory.
you can also fetch the data using foreach, calling ToArray() or ToList<>.
Hope you understand what I mean, and sorry for my english :)

Entity Framework - Many to many question

I have a table called ASB and a table called PeopleInvolved. There is a junction table called PeopleInvolved_ASB which simply contains an ASBID and a PeopleInvolvedID column. The columns act as a composite primary key.
The designer does not show the junction table (as expected). I want to retrieve a list of PeopleInvolved based on an ASBID.
To retrieve the people, I'm doing this:
// This top line gets the ASB record from the Case
var asbRecord = (from c in dd.Case
where c.CaseID == caseID
select c.ASB).First();
var asbID = asbRecord.Select(asb => asb.ASBID).First();
var people = (from asb in dd.ASB
where asb.ASBID == asbID
select asb.PeopleInvolved);
Now, what I want to do is add each PeopleInvolved record to a simple list of type PeopleInvolved. I can't do this though. I keep getting:
Error 4 Cannot convert type 'System.Data.Objects.DataClasses.EntityCollection' to 'Dynamic.PeopleInvolved'
How can I get a simple list of PeopleInvolved into a generic list that I can pass back to my controller?
Thanks,
Based on the error you report I'm guessing PeopleInvolved is a collection. So try this:
var people = (from asb in dd.ASB
where asb.ASBID == asbID
from pi in asb.PeopleInvolved
select pi).ToList();
I think it's extremely confusing to have both a type named PeopleInvolved and a collection of that type with exactly the same name. Similarly, it seems you have a type called ASB and an Entity Set containing many instances of that type by the same name,
It's much clearer to make entity set names plural and type names singular.
If I've guessed the types wrong, please clarify the layout of ASB and Dynamic.PeopleInvolveed.
This is what you want to do?
List<PeopleInvolved> genericPeopleInvolvedList = (from asb in dd.ASB
where asb.ASBID == asbID
select asb.PeopleInvolved).ToList();
[Updated: answered bad before]
Just realised that asb.PeopleInvolved is collection not single entity (damn!). So,previous linq query is returning CollectionS of PeopleInvolved entities. Since you are selecting by ASPID there should be only one asb.ASBID that fullfill where clause asb.ASBID == asbID, and you can do as follows:
var listWithCollectionOfPeopleInvolved = (from asb in dd.ASB
where asb.ASBID == asbID
select asb.PeopleInvolved).ToList();
List<PeopleInvolved> peopleInvolved = listWithCollectionOfPeopleInvolved.First().ToList();
But it's much nicer if you do it using Include:
var asbInstance = (from asb in dd.ASB.Include("PeopleInvolved")
where asb.ASBID == asbID
select asb).FirstOrDefault();
foreach(PeopleInvolved pi in asbInstance.PeopleInvolved)
{
//do your stuff
}
With Include automatically load associated properties.
List<PeopleInvolved> = new List<PeopleInvolved>(people);