Entity equality across different Linq-to-SQL contexts - c#

I am attempting to add some multi-threading to a WPF application I have created in order to create a more responsive interface, but as Linq-to-SQL data contexts are not thread safe, I am forced to use one per thread.
My problem is that the same entity pulled from two different contexts, are apparently not equal. Take the following code sample, where I have a simple database with employee records:
var context1 = new DataModelDataContext();
var context2 = new DataModelDataContext();
var emp1 = context1.Employees.Single(x => x.ID == 1);
var emp2 = context2.Employees.Single(x => x.ID == 1);
Console.WriteLine(string.Format("Employees equal: {0}", emp1 == emp2));
Console.ReadKey();
When run, this returns:
Employees equal: False
In my mind I would expect these to objects to be equal, as they would be if I pulled them from the same context. I can overcome this by checking emp1.ID == emp2.ID instead, but this is problematic when trying to use WPF bindings such as SelectedItem.
Is there any way around this? This behaviour appears to be the same in Entity Framework as well.

Two independently instantiated objects may represent the same item in the data store, but they will never evaluate as equal in any language. You will have to write code that compares the members of each object to determine if their data is equal. This may or may not be as simple as comparing the primary key.

You can always override Equals and GetHasCode to ensure that objects are "equal" even if they are not same instance (which is default equality rule used for reference types).

As #cdonner stated, when you load an object from the data store - two different instances of the object will be created with identical data. This will mean that object1 != object2.
One way of overcoming this is to set up a cache like dictionary in your repository. Example: Dictionary<Type, object>. Where object is the type of your indentifier (int in this case).
So rather than querying the data store by using inline code like context1.Employees.Single(x => x.ID == 1); you could set it up so that you call it like Repositories.Employees.WithID(1);
What this would then do is check the local repository cache for an Employee object with ID == 1, if this is available return it instead of querying the data store.
From then on, your references will always be the same.
This may need some refining for when you want to update and\or refresh the object in memory from the data store so that you don't persist stale data, and so that when you refresh the data from the data store, you update your cache.

Related

How to reload database with Entity Framework in C#

I am trying to do some checks on my database with an automated process. On a schedule the process goes out to a service and checks all the entries in the database against a list.
I want to re-insert the records that may have been deleted and update ones that are out of date
foreach (Category x in CustomeClass)
{
Category exists = Context.SSActivewear_Category
.Where(b => b.CategoryID == x.CategoryID)
.FirstOrDefault();
if (exists == null)
Context.Add(x);
else
Context.Update(x);
}
Not sure but I keep getting messages about tracking an instance with the same key etc. Can someone point me to a best practice on something like this
Danka!
This type of error is common when re-using the same instance of EF dbContext, especially when trying to load the same entity more then once from database using the same context.
If this is the case, then simply recreate the context (either new it up or use context factory) and then try update or modify your database data.
Creating new context is cheap, so no worries there.
After updating, save changes and dispose of the context (or use it in using statement to begin with).
If you are modifying the same entity multiple times using the same context, then do not load it from database multiple times.
In Your particular code example I would check if there is no duplication of Category objects in CustomeClass collection.
Is there duplication of CategoryID?
Is CategoryID for sure auto-generated when saving data (entity configuration)?
Is code not trying to update multiple entities with same id?
etc.
Entity framework works with references. Two instances of a class with the same data amount to two different references and only one reference can be associated with a DbContext, otherwise you get errors like that.
As per your example below:
foreach (Category x in CustomeClass)
{
Category exists = Context.SSActivewear_Category
.Where(b => b.CategoryID == x.CategoryID)
.FirstOrDefault();
if (exists == null)
Context.Add(x);
else
Context.Update(x);
}
Category is assumed to be your Entity, so "CustomeClass" would be a collection of instances that are not associated with your Context. These are Detached instances.
When your "exists" comes back as #null, this will appear to work as the Category "x" gets added and tracked by the Context. However, when "exists" comes back as not #null, you now have two instances for the same entity. "exists" is tracked by the DbContext, while "x" is not. You cannot use Update() with "x", you must copy the values across.
The simplest way to do this would be Automapper where you can create a map from Category to Category, then use Map to copy all values from "x" over into "exists":
var config = new MapperConfiguration(cfg => cfg.CreateMap<Category, Category>());
var mapper = config.CreateMapper();
mapper.Map(x, exists);
This is purely an example, you'll probably want to configure and inject a mapper that handles your entity copying. You can configure the CreateMap to exclude columns that shouldn't ever change. (Using ForMember, etc.)
Alternatively you can copy the values across manually:
// ...
else
{
exists.Name = x.Name,
exists.SomeValue = x.SomeValue,
// ...
}
In general you should avoid using the Update method in EF as this will result in a statement that overwrites all columns in a table, rather than updating just the column(s) that changes. (If no columns changed then no UPDATE SQL will actually get run)
On another side note, when getting "exists", you should use SingleOrDefault() not FirstOrDefault() as you expect 0 or 1 row back. First* methods should be used in cases where you expect there can be multiple matches but only want the first match, and should always be used with an OrderBy*() method to ensure the results are predictable.
You can use Update by performing an Exists check query that doesn't load a tracked entity. Examples would be:
Category exists = Context.SSActivewear_Category
.AsNoTracking()
.Where(b => b.CategoryID == x.CategoryID)
.SingleOrDefault();
or better:
bool exists = Context.SSActivewear_Category
.Where(b => b.CategoryID == x.CategoryID)
.Any();
Then you could use Context.Update(x). AsNoTracking() tells EF to load an instance but not track it. This would really be a waste in this case as it's a round trip to the DB to return everything in the Category only to check if something was returned or not. The Any() call would be a round trip but just does an EXISTS db query to return true or false.
However, these are not fool-proof as there is no guarantee that the Context instance isn't already tracking an instance for that Category from some other operation. Something as trivial as having a Category appear in CustomeClass twice for any reason would be enough to trip the above examples up as once you call Context.Update(x) that instance is now tracked, so the loop iteration for the second instance would fail.

EF Core Entity Equality

Sorry if this is a dumb question, most of my experience with ORMs has not been EF, and looking this up online gets me a lot of bad hits. It's almost like "reference" means different things to different people...
If I write code like so:
using (var db = new DbContext())
{
var entity1 = await db.Foos.FirstOrDefaultAsync(x => x.Id == 1);
var entity2 = await db.Foos.FirstOrDefaultAsync(x => x.Id == 1);
return entity1.Equals(entity2);
}
This returns true. Since my entity is a reference type, the Equals under the hood should be an Object.ReferenceEquals() call.
What I want to know is, is this reliable, i.e. will any entity represented by a particular database record in a context always be referentially equal, or can it "drop out" of the cache, get reloaded on demand and have a new reference like what happens in some less sophisticated ORMs? If an entity is loaded as part of a collection on another entity, is it still the same object? Are there rules/ settings that govern this behavior?
As #IvanStoev pointed out in the comments, the referential consistency is by design and a core part of EF, so the same object in the database should always reference the same object in the database context... at least in the scope of the specific database context you're in. YMMV if you are dealing with multiple database contexts.

Duplicating a model that has a hierarchy

My model looks something like this:
Company
-Locations
Locations
-Stores
Stores
-Products
So I want to make a copy of a Company, and all of its associations should also be copied and saved to the database.
How can I do this if I have the Company loaded in memory?
Company company = DbContext.Companies.Find(123);
If it is tricky, I can loop through each association and then call create a new object. The Id's will be different but everything else should be the same.
I am using EF 6.
Cloning object graphs with EF is a piece of cake:
var company = DbContext.Companies.AsNoTracking()
.Include(c => c.Locations
.Select(l => l.Stores
.Select(s => s.Products)))
.Where(c => c.Id == 123)
.FirstOrDefault();
DbContext.Companies.Add(company);
DbContext.SaveChanges();
A few things to note here.
AsNoTracking() is vital, because the objects you add to the context shouldn't be tracked already.
Now if you Add() the company, all entities in its object graph will be marked as Added as well.
I assume that the database generates new primary key values (identity columns). If so, EF will ignore the current values from the existing objects in the database. If not, you'll have to traverse the object graph and assign new values yourself.
One caveat: this only works well if the associations are 1:0..n. If there is a n:m association, identical entities may get inserted multiple times. If, for example, Store-Product is n:m and product A occurs at store 1 and store 2, product A will be inserted twice. If you want to prevent this, you should fetch the objects by one context, with tracking (i.e. without AsNoTracking), and Add() them in a new context. By enabling tracking, EF keeps track of identical entities and won't duplicate them. In this case, proxy creation should be disabled, otherwise the entities keep a reference to the context they came from.
More details here: Merge identical databases into one
I would add a method to each model that needs to be cloneable this way, I'd recommend an interface for it also.
It could be done something like this:
//Company.cs
Company DeepClone()
{
Company clone = new Company();
clone.Name = this.name;
//...more properties (be careful when copying reference types)
clone.Locations = new List<Location>(this.Locations.Select(l => l.DeepClone()));
return clone;
}
You should repeat this basic pattern for every class and "child" class that needs to be copiable. This way each object is aware of how to create a deep clone of its self, and passes responsibility for child objects off to the child class, neatly encapsulating everything.
It could be used this way:
Company copyOfCompany123 = DbContext.Companies.Find(123).DeepClone;
My apologies if there are any errors in the above code; I don't have Visual Studio available at the moment to verify everything, I'm working from memory.
One other really simple and code efficient way to deeply clone an object using serialization can be found in this post How do you do a deep copy an object in .Net (C# specifically)?
public static T DeepClone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T) formatter.Deserialize(ms);
}
}
Just be aware that this can have some pretty serious resource and performance issues depending on your object structure. Every class that you want to use it on must also be marked with the [Serializable] attribute.

Storing run-time data in objects from ObjectSet<T>

I'm very new to this Entity Framework Object Services Overview (Entity Framework), so forgive me if I use the wrong terminology here.
I'm using the EDMX file to connect to an SQLite database. What I'm trying to do is use the ObjectSet<T> normally, to access a collection of objects from a table in the database. However, I want to additionally store some run-time-only data in the objects in that set. In my case, I have a set of devices stored in the database, but upon startup, I want to mark them as "Connected" or "Disconnected", and keep track of this state throughout execution.
Since the (row) types generated by the EDMX are partial I've added another partial definition, and added my public bool Connected property there. This seems to work, I can set it, and future queries provide objects with the same value that I previously set. The problem is, I don't know a) how it is working, or b) whether I can trust it. These doubts come from the fact that these aren't really collections of objects we're dealing with, right?
Hopefully that made sense, else I can provide more detail.
What you're doing is completely safe.
ObjectSet is still a collection of objects. With a lot magic added underneath.
I am not an expert on the internals but here is how I think it works:
The Entity Framework has a StateTracker hat keeps track of all the entities you're working with.
Every class in your EDMX model is required to have a key. EF is using that key internally so that it loads that specific object only once into memory.
var foo = db.Foos.Single(x => x.Id == 1); // foo with Id 1 is unique (in memory)
var foo2 = db.Foos.Single(x => x.Id == 1); // same instance of foo, but with updated values
var foo3 = db.Foos.Single(x => x.Id == 2) // a new unique instance (Id = 2)
bool sameObject = Object.Equals(foo, foo2); // will return true;
At every select the following happens:
Is an instance of class Foo already tracked/does it already exist?
Yes -> update the properties of the existing instance from the database.
No -> create new instance of class Foo (take values from database)
Of course it can only ever update mapped properties. So the ones you defined in the partial class won't be overwritten.
In case you're going to use code first. There is also the [NotMapped] attribute, that makes sure that the property won't be included in the table if you generate a new database from your code first models.
I hope I could clarify some things for you.

load an object twice and change one of them, the other one doesnt changed

Father father = BL.GetFatherById(1);
Product product = BL.GetByID(123);
(father.Products[553] == product)
product.delete = true;
father.Products[553].delete == false !!??
why is that ?
aren't they connected ?? its the same object.
As you can read in section 10.3 of the NHibernate reference manual database identity and CLR object identity are equivalent per session.
Therefore Object.ReferenceEquals(foo, bar) will yield true if and only if foo and bar are attached to the same session and map to the same database row. Be careful when using == for comparing object identity - the operator may have been overloaded (but you should usually know that).
In consequence you should always get the same object no matter what query you use for the object as long as you stay within the same session. Are you using multiple sessions? Maybe a Unit of Work pattern and you are comparing objects returned from to different units of work?
First, let me tell you that what you are doing is HORRIBLE. What does this actually mean
father.Products[553] == product;
Unless you have coded a custom collection, which I doubt you did there is no way that would work.
Are you removing product that is
already at index 553 from collection?
No
Are you breaking the relationship
between father and product on index
553? No
Are you establishing relatinship to a
father with a new product? No
Are you establishing relationship
from new product to father? No
So
Expose products a IEnumerable rather than list
Add Add/Remove methods that will handle relationship syncronozation.
Take a look here how to do it (disregard the actual question)
How to map it? HasOne x References

Categories

Resources