Update in LINQ with one trip to database? - c#

I'm having a hard time wrapping my head around when LINQ accesses the database, and how it knows when the optimal time would be. Mainly, this UPDATE statement seems to make two trips to the database. But it shouldn't -- there seems to be no "update" equivalent in LINQ short of querying for a row, updating it, and then saving that change. In basic SQL, I should be able to UPDATE WHERE in one fell swoop.
var query = from u in db.users where u.id == 1 select u;
foreach (user u in query)
{
u.name = "JOE";
}
db.Save();
I get the magic of Save. It allows me to make multiple changes before committing them. But there still seems to be the initial query for the rows.
Thoughts? Ideas?
EDIT
Now tested, I can confirm that it does in fact go to the database twice. There must be a better way!

If id is the primary key, you can use:
var u = new user { id = 1 };
db.users.Attach(u);
u.name = "JOE";
db.SubmitChanges();

Take a look at one of custom Linq extensions, like this one http://www.aneyfamily.com/terryandann/post/2008/04/Batch-Updates-and-Deletes-with-LINQ-to-SQL.aspx. You have DeleteBatch and UpdateBatch methods there. In particular, the UpdateBatch takes the condition and the updating expression and executes as one SQL query.

Related

What is the best way to write queries in Entity Framework

What is best way to write query performance wise. e.g. i can write query to get all employes on specific last name as follow
ObjectQuery<Employee> queryEmp =
context.CreateQuery<Employee>(
"Select value e from Employees AS e Where e.LastName = #lastName",
new ObjectParameter("lastName", typeof(String)) { Value = "Farooqi" });
ObjectResult<Employee> results = query.Execute(MergeOption.AppendOnly);
OR
var v = from e in context.Employees where e.LastName == "Farooqi" select e;
OR
context.Employees.Where(e => e.LastName == "Farooqi");
OR
context.Employees.Where("LastName = #lastName", new ObjectParameter("lastName", typeof(String)) { Value = "Farooqi" });
My question is this that which method for querying is best.
What i am confused with is that i think (but not sure) that using last three methods other than the first one will fetch all records of employee from database then by enumerating all records those will be returened which fulfill the specified where condition, so i think last three ways will take more time than the first one so i used only first one.
But what is the best one in actual i dont know.
Do all method only fetch records form database which fulfill the were condition or fetch all records from db and then enumrated to return filtered records at applicaion end ?
There is little difference; the 2nd/3rd have the advantage of enforcing type correctness at compile-time, since the expression-tree won't compile if LastName doesn't exist or is (say) an int - avoids typos and the issue of "magic strings".
Re your supposition about bringing back everything; no. The 2nd/3rd compile to expression trees, which can be de-constructed and used to create the correct SQL (performing the filter at the server). Very clever, but often misunderstood. The key point is that this is Queryable.Where (taking an Expression<Func<T, bool>>), not Enumerable.Where (taking a Func<T, bool>). Feel free to perform a trace.
Likewise, there is little difference between the 1st/4th - both will filter at the server.

Entity Framework - How many trips to the database on DeleteObject

Is this entity framework call actually making two trips to the database?
var result = from p in entities.people where p.id == 6 select p;
entities.DeleteObject(result);
It strikes me that maybe DeleteObject would force the first trip to get results and THEN, having the object to work with, would execute the delete function.
If that is the case, how do I avoid the second trip? How does one do a remove-by-query in entity framework with a single database trip?
Thanks!
EDIT
My original example was misleading, because it was a query by primary key. I guess the real question is whether there is a way to have a single-trip function that can delete items from an IQueryable. For example:
var result = from p in entities.people where p.cityid == 6 select p;
foreach (var r in result)
{
entities.DeleteObject(r);
}
(Notice that the query is of a foreign key, so there may be multiple results).
You can do it like this:
entities.ExecuteStoreCommand("DELETE FROM people WHERE people.cityid={0}", 6);
this is one trip to the database for sure, and effective as it can be.
EDIT:
Also, take a look here, they suggest the same solution. And to answer the question, this is the only way to delete entities, not referenced by primary key, from a database using entity framework, without fetching these entities (and without writing some helper extension methods like suggested in this answer).
Direct delete:
var people = new people() { id = 6 };
entities.people.Attach(people);
entities.people.Remove(people);
entities.SaveChanges();
If you want to see it for yourself, fire up a profiler.
EDIT:
This will allow you to use Linq but it won't be one trip.
var peopleToDelete = entities.people.Where(p => p.id == 6);
foreach (var people in peopleToDelete )
entities.people.DeleteObject(people );
entities.SaveChanges();
There's no easy way to do that out of the box in EF, a big annoyance indeed (as long as one does not want to resort to using direct SQL, which personally I don't). One of the other posters links to an answer that in turn links to this article, which describes a way to make your own function for this, using ToTraceString.

Update only a single item in linq

Currently I update a single item in a database as follows:
var master = (from tmi in db._Masters where tmi.Id == Id select tmi).FirstOrDefault();
master.logPosition++;
db.SubmitChanges();
This strikes me as inefficient, as (I think) I am pulling out a full DB row to just update a single value. Is there a more efficient query I can use?
You can select only the field you want to modify by adjusting your original linq select:
var master = (from tmi in db._Masters
where tmi.Id == Id
select new { tmi.logPosition }).FirstOrDefault();
master.logPosition++;
db.SubmitChanges();
EDIT: By selecting the specific data into associated properties, the property logPosition should lose its read-only status and be fully updatable.
var master = (from tmi in db._Masters
where tmi.Id == Id
select new {
ID = tmi.ID,
logPosition = tmi.logPosition }).FirstOrDefault();
master.logPosition++;
db.SubmitChanges();
I assume this is LINQ to SQL; please correct if wrong.
You could make a stored procedure and pull that stored procedure into your DataContext and then invoke the stored procedure instead of using the above query.
Alternatively, you could hand write an update query and use DataContext.ExecuteQuery to execute the query.
However, none of this really matters until you profile and find out whether or not this is truly a bottleneck. If it is not, I would stick with the simplest that works which is what you already have.
#linq
I can't get the accepted answer by Joel to work.
The select new { } creates an anonymous type which is not updatable (as you stated in your comment, see also this question) and is also not part of the DataContext.
I described a couple of possible solutions
Stored Procedure
View
ExecuteCommand
dbml mappings
on my blog.

Linq is returning too many results when joined

In my schema I have two database tables. relationships and relationship_memberships. I am attempting to retrieve all the entries from the relationship table that have a specific member in it, thus having to join it with the relationship_memberships table. I have the following method in my business object:
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var results = from r in _context.Repository<DBMappings.relationships>()
join m in _context.Repository<DBMappings.relationship_memberships>()
on r.rel_id equals m.rel_id
where m.obj_id == objId
select r;
return results.ToList<DBMappings.relationships>();
}
_Context is my generic repository using code based on the code outlined here.
The problem is I have 3 records in the relationships table, and 3 records in the memberships table, each membership tied to a different relationship. 2 membership records have an obj_id value of 2 and the other is 3. I am trying to retrieve a list of all relationships related to object #2.
When this linq runs, _context.Repository<DBMappings.relationships>() returns the correct 3 records and _context.Repository<DBMappings.relationship_memberships>() returns 3 records. However, when the results.ToList() executes, the resulting list has 2 issues:
1) The resulting list contains 6 records, all of type DBMappings.relationships(). Upon further inspection there are 2 for each real relationship record, both are an exact copy of each other.
2) All relationships are returned, even if m.obj_id == 3, even though objId variable is correctly passed in as 2.
Can anyone see what's going on because I've spent 2 days looking at this code and I am unable to understand what is wrong. I have joins in other linq queries that seem to be working great, and my unit tests show that they are still working, so I must be doing something wrong with this. It seems like I need an extra pair of eyes on this one :)
Edit: Ok so it seems like the whole issue was the way I designed my unit test, since the unit test didn't actually assign ID values to the records since it wasn't hitting sql (for unit testing).
Marking the answer below as the answer though as I like the way he joins it all together better.
Just try like this
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var results = (from m in _context.Repository<DBMappings.relationship_memberships>()
where m.rel_id==objID
select m.relationships).ToList();
return results.ToList<DBMappings.relationships>();
}
How about to set _context.Log = Console.Out just to see the generated SQL query? Share the output with us (maybe use some streamwriter instead of console.out so that you can copy that easily and without mistakes).
Pz, the TaskConnect developer
I might have this backwards, but I don't think you need a join here. If you've setup your foreign keys correctly, this should work, right?
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var mems = _context.Repository<DBMappings.relationship_memberships>();
var results = mems.Where(m => m.obj_id == objId).Select(m => m.relationships);
return results.ToList<DBMappings.relationships>();
}
Here's the alternative (if I've reversed the mapping in my brain):
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var mems = _context.Repository<DBMappings.relationship_memberships>();
var results = mems.Where(m => m.obj_id == objId).SelectMany(m => m.relationships);
return results.ToList<DBMappings.relationships>();
}
Let me know if I'm way off with this, and I can take another stab at it.

Select clause containing non-EF method calls

I'm having trouble building an Entity Framework LINQ query whose select clause contains method calls to non-EF objects.
The code below is part of an app used to transform data from one DBMS into a different schema on another DBMS. In the code below, Role is my custom class unrelated to the DBMS, and the other classes are all generated by Entity Framework from my DB schema:
// set up ObjectContext's for Old and new DB schemas
var New = new NewModel.NewEntities();
var Old = new OldModel.OldEntities();
// cache all Role names and IDs in the new-schema roles table into a dictionary
var newRoles = New.roles.ToDictionary(row => row.rolename, row => row.roleid);
// create a list or Role objects where Name is name in the old DB, while
// ID is the ID corresponding to that name in the new DB
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles.ToList();
But calling ToList gives me this NotSupportedException:
LINQ to Entities does not recognize
the method 'Int32
get_Item(System.String)' method, and
this method cannot be translated into
a store expression
Sounds like LINQ-to-Entities is barfing on my call to pull the value out of the dictionary given the name as a key. I admittedly don't understand enough about EF to know why this is a problem.
I'm using devart's dotConnect for PostgreSQL entity framework provider, although I assume at this point that this is not a DBMS-specific issue.
I know I can make it work by splitting up my query into two queries, like this:
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var roles2 = from r in roles.AsEnumerable()
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles2.ToList();
But I was wondering if there was a more elegant and/or more efficient way to solve this problem, ideally without splitting it in two queries.
Anyway, my question is two parts:
First, can I transform this LINQ query into something that Entity Framework will accept, ideally without splitting into two pieces?
Second, I'd also love to understand a little about EF so I can understand why EF can't layer my custom .NET code on top of the DB access. My DBMS has no idea how to call a method on a Dictionary class, but why can't EF simply make those Dictionary method calls after it's already pulled data from the DB? Sure, if I wanted to compose multiple EF queries together and put custom .NET code in the middle, I'd expect that to fail, but in this case the .NET code is only at the end, so why is this a problem for EF? I assume the answer is something like "that feature didn't make it into EF 1.0" but I am looking for a bit more explanation about why this is hard enough to justify leaving it out of EF 1.0.
The problem is that in using Linq's delayed execution, you really have to decide where you want the processing and what data you want to traverse the pipe to your client application. In the first instance, Linq resolves the expression and pulls all of the role data as a precursor to
New.roles.ToDictionary(row => row.rolename, row => row.roleid);
At that point, the data moves from the DB into the client and is transformed into your dictionary. So far, so good.
The problem is that your second Linq expression is asking Linq to do the transform on the second DB using the dictionary on the DB to do so. In other words, it is trying to figure out a way to pass the entire dictionary structure to the DB so that it can select the correct ID value as part of the delayed execution of the query. I suspect that it would resolve just fine if you altered the second half to
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r.RoleName;
var list = roles.ToDictionary(roleName => roleName, newRoles[roleName]);
That way, it resolves your select on the DB (selecting just the rolename) as a precursor to processing the ToDictionary call (which it should do on the client as you'd expect). This is essentially exactly what you are doing in your second example because AsEnumerable is pulling the data to the client before using it in the ToList call. You could as easily change it to something like
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var list = roles.AsEnumerable().Select(r => new Role { Name = r.RoleName, ID = newRoles[r.RoleName] });
and it'd work out the same. The call to AsEnumerable() resolves the query, pulling the data to the client for use in the Select that follows it.
Note that I haven't tested this, but as far as I understand Entity Framework, that's my best explanation for what's going on under the hood.
Jacob is totally right.
You can not transform the desired query without splitting it in two parts, because Entity Framework is unable to translate the get_Item call into the SQL query.
The only way is to write the LINQ to Entities query and then write a LINQ to Objects query to its result, just as Jacob advised.
The problem is Entity-Framework-specific one, it does not arise from our implementation of the Entity Framework support.

Categories