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();
Related
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.
In the following example, we insert an entity called taskinstance to our context. we have a foreign key FK_Contract that we set at 2.
entity.FK_Contract = 2;
context.TaskInstances.AddObject(entity);
The query generated by entity framework is a simple insert. (everything is fine)
However, the following query works differently.
int contractId = context.Contracts.Where((T) => T.Name == contractName).Single().Id;
entity.FK_Contract = contractId;
context.TaskInstances.AddObject(entity);
In the trace created by entity framework we see without surprise the query selecting the Id according a contractName but we also see an extra request looking like:
select id,... from [TaskInstances] WHERE [Extent1].[FK_Task] = #contractId
This extra query leads to many problems, especially when we work with a foreign table with millions of record. The network goes down!
Therefore we 'd like to figure out the purpose of this extra query and the way to make it disappear.
It looks like the extra query is populating a collection of tasks on the returned Contract object. Try projecting just the column you want:
int contractId = context.Contracts
.Where(T => T.Name == contractName)
.Select(T => T.Id)
.Single();
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.
Say I have two entities with about 20 properties per entity and a Many-to-Many relationship like so:
User (Id int,Name string, .......)
Issue (Id int,Name string, .......)
IssueAssignment (UserId,RoleId)
I want to create a new Issue and assign it to a number of existing Users. If I have code like so:
foreach(var userId in existingUserIds)
{
int id = userId
var user = _db.Users.First(r => r.Id == id);
issue.AssignedUsers.add(user);
}
_db.Users.AddObject(user);
_db.SaveChanges();
I noticed it seems terrribly inefficient when I run it against my SQL Database. If I look at
the SQL Profiler it's doing the following:
SELECT TOP(1) * FROM User WHERE UserId = userId
SELECT * FROM IssueAssignment ON User.Id = userId
INSERT INTO User ....
INSERT INTO IssueAssignment
My questions are:
(a) why do (1) and (2) have to happen at all?
(b) Both (1) and (2) bring back all fields do I need to do a object projection to limit the
fields, seems like unnecessary work too.
Thanks for the help
I have some possible clues for you:
This is how EF behaves. _db.Users is actaully a query and calling First on the query means executing the query in database.
I guess you are using EFv4 with T4 template and lazy loading is turned on. T4 templates create 'clever' objects which are able to fixup their navigation properties so once you add a User to an Issue it internally triggers fixup and tries to add the Issue to the User as well. This in turns triggers lazy loading of all issues related to the user.
So the trick is using dummy objects instead of real user. You know the id and you only want to create realtion between new issue and existing user. Try this (works with EFv4+ and POCOs):
foreach(var userId in existingUserIds)
{
var user = new User { Id = userId };
var _db.Users.Attach(user); // User with this Id mustn't be already loaded
issue.AssignedUsers.Add(user);
}
context.Issues.AddObject(issue);
context.SaveChanges();
I have a table AccountSecurity which is a many-to-many table that relates Account entities and Securities. When I write the query below it returns all Securities that satisfy the where clause. However each Security instance in the list no longer has the reference to the AccountSecurity it came from. So when I do list[0].AccountSecurity it is empty. Is there anyway to include that information? I know I can rewrite the query to return AccountSecurities instead and use .Include("Security") on that, but I wonder if it can be done another way.
var list = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec.Security).ToList();
UPDATE
Of course if I do two queries the graph gets populated properly, there has to be a way to do this in one shot.
var securities = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec.Security).ToList();
//this query populates the AccountSecurities references within Security instances returned by query above
var xref = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec).ToList();
var list = (from sec in base.context.Securities
.Include("AccountSecurity")
where sec.AccountSecurities.Any(as => as.AccountId == accountId)
select sec).ToList();
Try this:
var list = (from acctSec in base.context.AccountSecurities.Include("Security")
where acctSec.AccountId == accountId
select acctSec).ToList();
Then simply use the Security property as needed, and since it's read at the same time AccountSecurities is (single SQL with join), it will be very efficient.