Adding entity to Collection removes it from another (Entity Framework) - entity-framework

guys, I've got very strange problem, that nobody from my company knows.
I have a Collection inside one object, which I created manualy:
Cost cost = new Cost
{
...
};
cost.Formulas.Add(new Formula()
{
CostID = cost.ID,
FieldName = "Distance",
Formula1 = "12+2",
});
Next, I got another object with the same type and ID from context:
Cost updatingCost = context.Costs.Include("Formulas").FirstOrDefault(c => c.ID == cost.ID);
And finally I do:
updatingCost.Formulas.Add(cost.Formulas.ToList()[0]);
And here is magic! After that line cost.Formulas doesn't contains that Formula any more!!
cost.Formulas.Count = 0
What does this method iCollection.Add() do? Why it removes an object from other collection? It blows my mind, please help to understand it!

In fact it is pretty simple. By adding the new formula to another Cost object, updatingCost, EF changes the foreign key value CostID to the value of updatingCost.ID.
This is because EF executes relationship fixup many times when entites are manipulated in code. This process makes sure that primitive ID values and object references and collection contents match.

Related

Entity Framework DeleteObject query a lot of items

I have a problem trying to delete an object with Entity Framework, I previously query the context to get a list of objects I need to delete, then one by one I call deleteobject
IQueryable result = context.CustomObjects.Where(t=>t.Property = something)
foreach (CustomObject customObj in result)
{
context.DeleteObject(customObj);
}
When I call DeleteObject EF executes a weird query, something like that:
exec sp_executesql N'SELECT
[Extent1].[Value1] AS [Value1],
[Extent1].[Value2] AS [Value2],
[Extent1].[Value3] AS [Value3],
FROM [CustomObject] AS [Extent1]
WHERE [Extent1].[ID] = #EntityKeyValue1',N'#EntityKeyValue1 int',#EntityKeyValue1=59
This query seems to search all the object with ID = something, but ID it is just part of the entity key that is indeed composed by 3 fields, so it attaches like n thousands items and make the process very slow, that is a behavior I can't understand, I always deleted object in this way and I have never had such a problem
can someone have an idea?
Thanks
You could be fetching mutiple objects as defined by your where query. Also for each object you fetch, you will be fetching all fields on the object CustomObject.
The query thats being executed is a select query so this confirms the above behaviour.
If you want better performance then I recommend that you do something like this:
var entityToDelete = new SomeEntity();
SomeEntity.PK = 12;
var context = new YourContextntities();
context.YourEntitites.Attach(entityToDelete);
context.YourEntitites.Remove(entityToDelete);
context.SaveChanges();

How to populate a property that is not directly stored in Database with CodeFirst

I have 2 Entities, each of which is managed by EF Code First, and each happily sitting in its own table. Entity_A has a property, called "SumTotal", which should be the sum of a specific column in Entity_B that matches a certain criteria.
SumTotal should not be persisted in the DB, but rather calculated each time an instance of Entity_A is retrieved.
I have looked at ComputedColumns, but it appears that the computedcolumn can only be defined relative to columns in the same table.
I also have a feeling that I need to set SumTotal to NotMapped (or something similar with AutoGenerated), but dont know how to get the actual value into SumTotal.
Hope this question makes sense, thanks in advance
You can project the results to an anonymous object and transform that it to your entity
var projection = db.EntityAs.Where(/* */)
.Select(a => new {A = a, Sum = a.Bs.Sum(b => b.Total)})
foreach(p in projection)
{
p.A.SumTotal = p.Sum;
}
var As = projection.Select(p => p.A);

Entity Framework: selecting from multiple tables

I have a statement:
var items = from e in db.Elements
join a in db.LookUp
on e.ID equals a.ElementID
where e.Something == something
select new Element
{
ID = e.ID,
LookUpID = a.ID
// some other data get populated here as well
};
As you can see, all I need is a collection of Element objects with data from both tables - Elements and LookUp. This works fine. But then I need to know the number of elements selected:
int count = items.Count();
... this call throws System.NotSupportedException:
"The entity or complex type 'Database.Element' cannot be constructed in a LINQ to Entities query."
How am I supposed to select values from multiple tables into one object in Entity Framework? Thanks for any help!
You are not allowed to create an Entity class in your projection, you have to either project to a new class or an anonymous type
select new
{
ID = e.ID,
LookUpID = a.ID
// some other data get populated here as well
};
Your code doesn't work at all. The part you think worked has never been executed. The first time you executed it was when you called Count.
As exception says you cannot construct mapped entity in projection. Projection can be made only to anonymous or non mapped types. Also it is not clear why you even need this. If your class is correctly mapped you should simply call:
var items = from e in db.Elements
where e.Something == something
select e;
If LookupID is mapped property of your Element class it will be filled. If it is not mapped property you will not be able to load it with single query to Element.

Why "EntityKey does not match the corresponding value in the EntityKey"?

First I'd like to show the corresponding code snippet. When it comes to objCtx.AttachTo() it throws me an error:
Error: "The object cannot be attached because the value of a property that is a part of the EntityKey does not match the corresponding value in the EntityKey."
// convert string fragIds to Guid fragIds
var fragIdsGuids = docGenResult.FragIds.Select(c => new Guid(c)).ToList();
//add each fragment to document))))
foreach (Guid fragIdsGuid in fragIdsGuids)
{
var fragment = new Fragment() { EntityKey = new EntityKey("DocTestObjectContext.Fragments", "ID", fragIdsGuid) };
objCtx.AttachTo("Fragments", fragment);
}
objCtx.SaveChanges();
I've checked everything and I'm not missing any primary key.
However I need some words to explain why I think I have to do it this way.
I'm using EF4 in a C# Environment.
I have a many to many relationship between two tables, Document and Fragments(Primary key "ID") (Documents can have many fragments and a fragment can be a part of many documents)
The Entity Model works fine for me.
However when I try to add a new document to the DB I already have the IDs of the related Fragments in my hand. For adding a new document to the DB I have to call each Fragmentobject and add it to the mapped reference in my document-object. This is a bottleneck because a document can have more than 1000 fragments. The Consequence is that I need 1sec per document. Not much, but I have to create more than 3000 documents and saving this second would result in more speed.
Hopefully you know what's wrong in here.
Thanks.
Thomas
1st edit:
here is the solution wich actually works. I would like to avoid to load all the fragments and instead just save the fragment GUID I already have in the mapping table.
// convert string fragIds to Guid fragIds
var fragIdsGuids = docGenResult.FragIds.Select(c => new Guid(c)).ToList();
// get responding entities from Fragment table
var fragmentList = objCtx.Fragments.Where(c => fragIdsGuids.Contains(c.ID)).ToList();
foreach (var fragment in fragmentList)
{
doc.Fragment.Add(fragment);
}
objCtx.SaveChanges();
2nd edit:
I have the feeling that it is not really clear what I try to do.
However I would like to link/reference existing fragments in a Fragment-table to a coressponding Document in a Document table. The Document I'd like to reference is a new one. The document to Fragment table has an many to many relationship. This relationship has a linking table on the database. In the model it is correctly modeled as a many to many relationship. That's fine.
So far so good. What works is what you can see under my first edit. I have to load all the necessary fragments for a document by their id
// get responding entities from Fragment table
var fragmentList = objCtx.Fragments.Where(c => fragIdsGuids.Contains(c.ID)).ToList();
After that I'm able to add them to my document entity:
foreach (var fragment in fragmentList)
{
doc.Fragment.Add(fragment);
}
But why the hell do I have to load the whole entity (fragments) only to link it to a new document. Why do not tell the EntityStateManager "Dude, here you have some Fragment IDs, link them!"?
Further I tried to follow the MSDN article mentioned by Adrian in the comments. This doesn't worked out for me.
I'll try this:
var fragment = new Fragment {ID = fragIdsGuid};
//fragment.EntityKey.Dump(); // -- this should be null
objCtx.AttachTo("Fragments", fragment);
//fragment.EntityKey.Dump(); // -- shows the EntityKey object, created after the object is attached
The Dump function is from LinqPad

EF4 inheritance and Stored procedures

I implemented inheritance with a discriminator field so all my records are in the same table. My basetype is Person (also the name of the table) and Driver and Passenger inherit from it. I receive instances of the correct type (Driver and Passenger) when I perform a query on the object context to Person. example:
var q = from d in ctx.Person
select d;
But I also create a function that calls a stored procedure and mapped the output of the function to the type Person. But now I get a list of Person and not Drivers or Passengers when I execute this method.
Anybody an idea how to solve this or is this a bug in EF4?
AFAIK, you can't use discriminator mapping (e.g TPH) when dealing with stored procedure mappings.
The stored procedure must be mapped to a complex type or custom entity (e.g POCO), the mapping cannot be conditional.
What you could do is map it to a regular POCO, but then project that result set into the relevant derived type (manual discrimination).
E.g:
public ICollection<Person> GetPeople()
{
var results = ExecuteFunction<Person>(); // result is ObjectResult<Person>
ICollection<Person> people = new List<Person>();
foreach (var result in results)
{
if (result.FieldWhichIsYourDiscriminator == discriminatorForDriver)
{
people.Add((Driver)result);
}
// other discriminators
}
}
If your always expecting a collection of one type (e.g only Drivers), then you wouldn't need the foreach loop, you could just add the range. The above is in case you are expecting a mixed bag of different people types.
Would be interested to see other answers, and if there is a better way though - but the above should work.