LinqConnect Documentation
Object Identity

When you are querying for a table row via an ORM, a new entity object that represents this row is created - that's simply what ORMs are for. But what if you fetch the same row via another query? Will you get the same object instance or a copy that is stored somewhere else in the memory? For LinqConnect, the answer is: yes, this would be the same object:

Object cache

To provide such handling of the object identity, DataContext creates an entity cache, where all entities created by the application or loaded from the database are placed. After another query gets the corresponding row from the database, this entity is got from the cache (instead of materializing a new object):

Materializing query results

The image shows how a result set returned by server is 'transformed' into a collection of entity objects. For each row from the result set, it is checked whether the corresponding entity is already in the cache. If it is, this entity is added to the resulting entity collection. If it is not, the entity is materialized from the row and added to both cache and result collection.

This approach has the following advantages:

The things one should keep in mind because of LinqConnect object caching:

Entity Cache Modes

As it was said, fetched entities are saved into a cache just after being materialized. Thus, the context actually has to hold references to these objects even if a user needs them no more. If the application architecture allows DataContext instances to exist a long time, storing references to all entities that were used once may result in excessive memory consumption.

To eliminate such a possibility, the entity cache may hold weak references to the entities instead of strong ones (and this is actually the default behaviour). In this case, unused entity objects will eventually be garbage-collected. If some future query returns the rows corresponding to such entities, new entity objects will be materialized from the result set (this also gives a collateral bonus of entities being up-to-date).

If, for some reason, you do want to keep strong references to the entities in the cache, you can change the EntityCachingMode property of the DataContext class. Let us consider the following sample:


C#csharpCopy Code
static double GetFirstProductPrice(CrmDemoDataContext db) {
 
    Product product = db.Products.Where(p => p.ProductID == 1).Single();
    return product.Price.Value;
}
 
static void Main(string[] args) {
    CrmDemoDataContext context = new CrmDemoDataContext();
 
    // Configure the caching behaviour:
    context.EntityCachingMode = EntityCachingMode.WeakReference;
 
    // Get the original value of an entity field.
    // A separate method is used to ensure that the Product object is not used in the current scope.
    double initialPrice = GetFirstProductPrice(context);
 
    // Force the garbage collector to finalize unused objects.
    GC.Collect();
 
    // Use another context instance to change the value in the database.
    CrmDemoDataContext checkContext = new CrmDemoDataContext();
    Product updatedProduct = checkContext.Products.Where(p => p.ProductID == 1).Single();
    updatedProduct.Price += 10;
    checkContext.SubmitChanges();
 
    // Query for the product used in the 'GetFirstProductPrice' method.
    // If strong references were used, the entity should still be available in the cache.
    // Otherwise, has to materialize the entity again.
    Product product = context.Products.Where(p => p.ProductID == 1).Single();
 
    Console.WriteLine(
        "Original value: {0}.\n Current database value: {1}.\n Value in the 'product' object: {2}.",
        initialPrice,
        updatedProduct.Price,
        product.Price);
}
Visual BasicCopy Code
Private Shared Function GetFirstProductPrice(db As CrmDemoDataContext) As Double
 
    Dim product As Product = db.Products.Where(Function(p) p.ProductID = 1).[Single]()
    Return product.Price.Value
End Function
 
Private Shared Sub Main(args As String())
    Dim context As New CrmDemoDataContext()
 
    ' Configure the caching behaviour:
    context.EntityCachingMode = EntityCachingMode.WeakReference
 
    ' Get the original value of an entity field.
    ' A separate method is used to ensure that the Product object is not used in the current scope.
    Dim initialPrice As Double = GetFirstProductPrice(context)
 
    ' Force the garbage collector to finalize unused objects.
    GC.Collect()
 
    ' Use another context instance to change the value in the database.
    Dim checkContext As New CrmDemoDataContext()
    Dim updatedProduct As Product = checkContext.Products.Where(Function(p) p.ProductID = 1).[Single]()
    updatedProduct.Price += 10
    checkContext.SubmitChanges()
 
    ' Query for the product used in the 'GetFirstProductPrice' method.
    ' If strong references were used, the entity should still be available in the cache.
    ' Otherwise, has to materialize the entity again.
    Dim product As Product = context.Products.Where(Function(p) p.ProductID = 1).[Single]()
 
    Console.WriteLine( _
        "Original value: {0}." & vbLf & " Current database value: {1}." _
        & vbLf & " Value in the 'product' object: {2}.", _
        initialPrice, _
        updatedProduct.Price, _
        product.Price)
End Sub

In the sample, we create a context and set its caching mode. Using weak references is the default behaviour, thus setting EntityCachingMode may actually be omitted. We then get the price of a single product via this context; this is done in a separate method to ensure that the Product entity falls out of the scope. Since the cache holds a weak reference only, the next line ensures that this Product is garbage-collected. After that, we update the corresponding row in the database with another DataContext instance, and query for the same product with the initial context object. At this moment, the LinqConnect runtime cannot find the proper Product object in the cache (because this object was GC-ed) and so has to materialize it from the result set. Thus, 'updatedProduct.UnitPrice' should be equal to 'product.UnitPrice'.

If we set the caching mode to keeping strong references:


C#csharpCopy Code
    // Configure the caching behaviour:
    context.EntityCachingMode = EntityCachingMode.StrongReference;
Visual BasicCopy Code
    ' Configure the caching behaviour:
    context.EntityCachingMode = EntityCachingMode.StrongReference

the runtime will find the Product instance created in the GetFirstProductPrice method in the cache. Thus, this object would be stale and 'product.UnitPrice' should be equal to the 'initalPrice' variable.

If an entity has no key, it cannot be cached and change tracking is not enabled for such entity. LinqConnect does not allow modifications of such entities. An exception is raised when such entity is created or deleted, and updates of such entity are ignored.