Linq to Sql: Can the DataContext instance return collections that include pending changes? - linq-to-sql

Consider the following code block:
using (PlayersDataContext context = new PlayersDataContext())
{
Console.WriteLine(context.Players.Count()); // will output 'x'
context.Players.InsertOnSubmit(new Player {FirstName = "Vince", LastName = "Young"});
Console.WriteLine(context.Players.Count()); // will also output 'x'; but I'd like to output 'x' + 1
}
Given that I haven't called
context.SubmitChanges();
the application will output the same player count both before and after the InsertOnSubmit statement.
My two questions:
Can the DataContext instance return collections that include pending changes?
Or must I reconcile the DataContext instance with context.GetChangeSet()?

Sure, use:
context.GetChangeSet()
and for more granularity, there are members for Inserts, Updates, and Deletes.
EDIT: I understand your new question now. Yes, if you wanted to include changes in the collection, you would have to somehow combine the collections returned by GetChangeSet() and your existing collections.

Related

Best way to perform insert/delete batch spring-jdbc/mysql

First, I will try to describe what I am willing to do and then, I will ask my questions.
I need to do the following:
List all rows corresponding to some conditions
Do some tests (e.g: check if it wasn't already inserted), if test passes then insert row into another database
Delete row (whether it passed tests or not)
The following is my implementation
List<MyObject> toAdd = new ArrayList<MyObject>();
for(MyObject obj:list){
if(!notYetInserted(obj){
toAdd.add(obj);
}
}
myObjectDAO.insertList(toAdd);
myObjectDAO.deleteList(list);
The service method is marked transactional.
In my DAO methods for deleteList and insertList are pretty similar so I will just put here method for insert.
public void insertList(final List<MyObject> list){
String sql = "INSERT INTO table_test " +
"(col_id, col2, col3,col4) VALUES (?, ?, ?,?)";
List<Object[]> paramList = new ArrayList<Object[]>();
for (MyObject myObject : list) {
paramList.add(new Object[] {myObject.getColId(),
myObject.getCol2(), myObject .getCol3(), myObject.getCol4()}
);
}
simpleJdbcTemplate.batchUpdate(sql, paramList);
}
I am not sure about the best way to perform such operations, I read here that calling for update inside a loop may slow down the system (especially in my case, I will have about 100K insert/delete at a time). I wonder if these additional loops inside DAO won't slow down my system even more and what would happen if problem happened repeatedly while processing that batch (I thought also about moving test from service to DAO to have only one loop and an additional test, I don't really know if it's a good idea). So, I would like your advices. Thanks a lot.
PS: if you need more details feel free to ask!
This is not necessarily a bad approach, but you are right, it might be really slow. If I were to do a process like this that inserted or deleted this many rows I would probably do it all in a stored procedure. My code would just execute the proc and the proc would handle the list and the enumeration through it (as well as the inserts and deletes).

Confusion with Entity Framework context

I'm a bit confused in regards to how EF's dbContext works.
If I do something like _context.Persons.Add(_person) (assuming person is a valid entity), if I then (before calling _context.SaveChanges()) query Persons, will the person I just added be included in the results?
For example:
Person _person = new Person() {Firstname = "Bill", Lastname = "Snerdly"};
_context.Persons.Add(_person);
var _personList = _context.Persons.Where(p => p.Lastname.StartsWith("Sne"));
Whenever I try this, it seems as though the context loses track of the fact that I've added this new person to the context.
What confuses me is that if I edit an existing person and attach the person and set the state to modified, querying the context seems to keep track of the changes that were made and returns them in the results. For example:
//Assuming that Person 5 exists with the name William Snerdly
Person _person = new Person() {Id = 5, Firstname = "Bill", Lastname = "Snerdly"};
_context.Persons.Attach(_person);
_context.Entry(_person).State = System.Data.EntityState.Modified;
var _personList = _context.Persons.Where(p => p.Lastname.StartsWith("Sne"));
In this case, it seems like the person with the id of 5 will show up in the list with the name Bill instead of William. IOW, the context queried the data but retained the changes while in the first scenario, the context queried the data but ignored any added items. It just seems a bit inconsistant.
Am I understanding this correctly or am I missing something?
Thanks for your help with this.
No, as it does not yet exist in the database. It will, however, be accessible through the ObjectStateManager of the ObjectContext, or alternatively, if you're using the DbContext/DbSet wrappers, through the .Local property of the DbSet.
In the case of the edit, you're seeing the ORM's first level cache at work. The query is executed against the database (and so compares against the values in there - your example would get even weirder if you modified the Lastname in the context, but still get the result from the query looking for the unmodified Lastname), but when its results are processed, first the ID of the returned entity is checked, and since the entity with that ID is already present in the context, you get that instance back. This is the default "AppendOnly" mode of operation.
I don't know what you want to do, but I had to understand all that when I wanted to validate my changes according to rules that needed to use the values of both loaded and unread entities. I ended up starting a transaction, saving the changes with the "None" options, doing my validation queries againt the database (which then contained the "merged" view of the data), and the rolling back the transaction if the data was invalid, or accepting the changes and committing the transaction otherwise.

Linq-to-SQL EntitySet Is Not IQueryable -- Any Workarounds?

When you query an EntitySet property on a model object in Linq-to-SQL, it returns all rows from the entityset and does any further querying client-side.
This is confirmed in a few places online and I've observed the behavior myself. The EntitySet does not implement IQueryable.
What I've had to do is convert code like:
var myChild = ... ;
// Where clause performed client-side.
var query = myChild.Parents().Where(...) ;
to:
var myChild = ... ;
// Where clause performed in DB and only minimal set of rows returned.
var query = MyDataContext.Parents().Where(p => p.Child() == myChild) ;
Does anyone know a better solution?
A secondary question: is this fixed in the Entity Framework?
An EntitySet is just a collection of entities. It implements IEnumerable, not IQueryable. The Active Record pattern specifies that entities be directly responsible for their own persistence. OR mapper entities don't have any direct knowledge of the persistence layer. OR Mappers place this responsibility, along with Unit Of Work, and Identity Map responsibilities into the Data Context. So if you need to query the data source, you gotta use the context (or a Table object). To change this would bend the patterns in use.
I had a similar problem: How can I make this SelectMany use a join. After messing with LINQPad for a good amount of time I found a decent workaround. The key is to push the EntitySet you are looking at inside a SelectMany, Select, Where, etc. Once it's inside that it becomes an Expression and then the provider can turn it into a proper query.
Using your example try this:
var query = from c in Children
where c == myChild
from p in c.Parents
where p.Age > 35
select p;
I'm not able to 100% verify this query as I don't know the rest of your model. But the first two lines of the query cause the rest of it to become an Expression that the provider turns into a join. This does work with my own example that is on the question linked to above.

How to update in Linq to SqL?

every example I seen shows how to do a update query in linq to sql by doing this.
// grab entity you want to update
entity.UserId = "123"; // update the fields you want to update.
entity.Name = "bob";
Dbcontext.SubmitChanges();
I am wondering can you juse pass in a new object and have it figure it out?
Like could I do this?
Enity myEntity = new Entity();
myEntity.UserId = "123";
myEntity.Name = bob:
// grab entity record
// shove record ito the found record
// it figured out what to update and what no to update
Depending on what exactly you want to do you either need the InsertOnSubmit method, or the Attach method of the respective table (i.e. dbContext.Entities). InsertOnSubmit is used to add a record, while Attach can be used if you want to affect an UPDATE without having to first SELECT the record (you already know the primary key value)
In the case you have the dbContext available and ready, just add InsertOnSubmit:
Entity myEntity = new Entity();
myEntity.UserId = "123";
myEntity.Name = bob:
Dbcontext.InsertOnSubmit(myEntity);
Dbcontext.SubmitChanges();
As the name of the method implies, this will insert your new entity into the database on calling SubmitChanges.
Marc
If you want to do this for performance reasons then you shouldn't worry about it. Linq to Sql will cache objects locally so that just grabbing an entity by ID to modify some fields is very cheap.
It's possible to attach and persist it to the database, however you may want to set a field to check for concurrency (ie LastModified).
If you are going to use the Attach method on the data context, you need to set the primary/composite keys before you attach the entity (so you don't trigger INotifyPropertyChanging, INotifyPropertyChanged events).

LINQ-to-SQL and SQL Compact - database file sharing problem

I'm learing LINQ-to-SQL right now and i have wrote a simple application that define SQL data:
[Table( Name = "items" )]
public class Item
{
[ Column( IsPrimaryKey = true, IsDbGenerated = true ) ]
public int Id;
[ Column ]
public string Name;
}
I have launched 2 copy of application connected to the same .sdf file and tested if all database modifications in one application affects another application. But strange thing arise. If i use InsertOnSubmit() and DeleteOnSubmit() in one application, added/removed items are instantly visible in other application via 'select' LINQ queue. But if i try to modify 'Name' field in one application, it is NOT visible in other applicaton until it reconnects the database :(. The test code i use:
var Items = from c in db.Items
where Id == c.Id
select c;
foreach( var Item in Items )
{
Item.Name = "new name";
break;
}
db.SubmitChanges();
Can anyone suggest what i'm doing wrong and why InsertOnSubmit()/DeleteOnSubmit works and SubmitChanges() don't?
This is related to how Linq-to-Sql manages Object Identity. If you query for the same entity more than once, you will always receive the same object representing the row in the database.
When you insert or delete entities, you are able to see the changes from another client because Linq-to-Sql will need to create/delete the objects associated with those entities and, if there are no conflicts, this presents no problem.
Updating has a different behavior that's explained in the Object Identity article.
LINQ to SQL uses this approach to
manage the integrity of local objects
in order to support optimistic
updates. Because the only changes that
occur after the object is at first
created are those made by the
application, the intent of the
application is clear.
If you need the most updated data from the database, use the DataContext.Refresh with RefreshMode.OverwriteCurrentValues.