The EntitySpaces Community

Share and learn about the EntitySpaces Architecture.
Welcome to The EntitySpaces Community Sign in | Join | Help
in
Home Forums Photos

Identity Mapping

Last post 08-01-2008, 9:32 AM by Mike.Griffin. 8 replies.
Sort Posts: Previous Next
  •  07-29-2008, 10:02 PM 10422

    Identity Mapping

    So I came across a interesting issue today.  I was wondering if the entityspaces utilizes something like the "Identity Map" pattern?  I realize you can add a timestamp and get a concurrency error, but is it possible to have 2 entitys that load the same KEY and have changes reference each other.  If it isnt built into entityspaces, does anyone know of a good way to manually implement it?

     For example (forgive the minor syntax errors, just trying to get my point accross) :

     Employee empA = new Employee();

     Employee empB = new Employee(); 

    empA.LoadByPrimaryKey(1);

    empB.LoadByPRimaryKey(1);

    empA.FirstName = "John"; // << I change the first name of an employee to be John

    Response.Write(empA.FirstName); // outputs 'John' of course

     Response.Write(empAB.FirstName); // in my tests this would still be 'Darren'... but it would make sense if it automatically was changed to 'john'

     

     

    Filed under:
  •  07-30-2008, 12:27 AM 10425 in reply to 10422

    Re: Identity Mapping

    Hi

    I don't think that type of functionality would suit most people to be honest - if I have more than one "copy" of an entity loaded from the DB, and I make changes to one of them, I don't want those changes propogated across the others until I hit "Save" - especially if I want to, for example, reload one to help handle a concurrency error (so one should show the users altered copy, the other should show the DB copy).

    If you want to manually implement something like that then why not have both of your entities point to the same object (or just use one entity Smile)?

    Cheers

    Martin

  •  07-30-2008, 6:20 PM 10450 in reply to 10425

    Re: Identity Mapping

    Actually I can think of one instance where you would....  Lets say you have an like 20 employees in a collection, while your looping through them you want to get a related position entity. Why would you hit the database 20 times to get the position (I guess you could use a view in some cases - but not all) instead of having a identity mapping pattern in the template that would simply only grab entitys that are havent been pulled during that particular response?  It would be a lot more efficient.
  •  07-31-2008, 12:39 AM 10452 in reply to 10450

    Re: Identity Mapping

    Hi

    To be honest I'd probably set up a cached collection for that type of situation - when the datagridview tries to retrieve a value for something already in the collection it would use the cache, if the info hasn't been retrieved yet you would retrieve it and add it to the cache.

    Cheers

    Martin

  •  07-31-2008, 6:43 AM 10453 in reply to 10452

    Re: Identity Mapping

    This is something we have considered but there would be a significant performance hit and there are no destructors in .NET (I know there are other ways around this but it makes garbage collection much slower). Also, imagine a high traffic web site with millions of hits a day (we have customers than run that much traffic) and all this "Uniquing" going on. When you do a query and load a collection we would need to index into some hash table based on its id, and it could be a composite primary key, so we have to form a key by concatenating them and doing a lookup. Suppose then we don't find this "guy" in the cache, now we have to add it which means we must do a lock, which then means we are blocking on other threads (web requests) and so on. Then of course, for every type of object you have a cache, which means lots more memory.

    This doesn't mean that we will never support it, we have spoken about it several times, however, it's not trivial and comes with some real performance costs.


    EntitySpaces | Twitter | BLOG | Please honor our Software License
  •  07-31-2008, 9:55 PM 10475 in reply to 10453

    Re: Identity Mapping

    Thanks for the reply Mike; I know your really busy but its awsome that you take the time to respond. (with an answer that actually really does makes sense).

    I understand your point, and I can see why on a web application it really doesnt make sense to do.  My suggestion is that if you ever do implement the above, it would be cool to just have a property on the entity itself and that way you could turn caching on for some and off for others.

    Anyways, thanks again.

  •  08-01-2008, 5:47 AM 10496 in reply to 10475

    Re: Identity Mapping

    It's definitely in our list for consideration. We would like to do this so that in our hierarchical model a record (or entity) only appears once in the model know matter how many times and from what angle you approach it.
    EntitySpaces | Twitter | BLOG | Please honor our Software License
  •  08-01-2008, 8:56 AM 10513 in reply to 10496

    Re: Identity Mapping

    Mike,

    Couldn't it be implemented on a 'per-request' method?  Here is what a co-worker wrote me on the matter (I think it makes sense as well): 

    To have per-request (i.e. safe per-thread) data under ASP.NET, you can use HttpContext.Current.  That object has an Items collection - essentially a per-request dictionary that you can store values in.  You just need to come up with a string-based key for each.

    Current is an object of type HttpContext.  The doc says that it "encapsulates all HTTP-specific information about an individual HTTP request".  From what I've read (and done occasionally in the past), it's the safe way to do per-thread "global" data, since ordinary .NET thread-local-storage doesn't work properly in an ASP.NET environment.

    The one catch is this: if you're testing your code outside of running it on a web server (e.g. unit tests), then HttpContext.Current is null!  To avoid exceptions being thrown, you have to do something like this:

       using System.Web;

        // ...

        class SomeObject
        {
            // ...

            [ThreadStatic]
            private static SomeObject instance;     // used when HttpContext.Current == null
                                                    // otherwise, the instance is stored in
                                                    // HttpContext.Current.Items

            private static string index = "SomeObject";

            protected SomeObject()
            {
                // ...
            }

            public static SomeObject Instance
            {
                get
                {
                    SomeObject instance;

                    if (HttpContext.Current == null)
                    {
                        instance = SomeObject.instance;

                        if (instance == null)
                            SomeObject.instance = instance = new SomeObject();
                    }
                    else
                    {
                        instance = (SomeObject)HttpContext.Current.Items[index];

                        if (instance == null)
                            HttpContext.Current.Items[index] = instance = new SomeObject();
                    }

                    return instance;
                }
            }

            // ...
        }

     

    Like I said, the EntitySpaces developer is assuming that the identity map is application-wide.  I think that would be a mistake, due to all the locking issues required (as he rightly points out).  If the identity map is per-request, then his main concern is not actually a problem.

    The performance overhead of accessing the hash table is probably small, compared to the cost of database access.  In some cases (e.g. redundantly loading an individual object), it actually *saves* you a trip to the DB (it doesn't save a trip when loading a collection, though, but it could still save memory because redundant objects won't be instantiated).

    If the performance hit of indexing based on composite primary keys is a problem, then the answer is simple: don't use composite primary keys!  I can't think of a good reason to use composite primary keys for tables whose rows correspond to domain entities anyway.  You'll always want them to have int keys.  You mainly see composite keys in association tables (i.e. many-to-many mappings), but rows in association tables don't correspond to domain entities.

    The final concern is that the identity maps take up a lot of memory due to all the objects they store.  If the maps are per-request, I think this is a total non-issue.
    Sure, objects take up memory and must eventually be garbage-collected, but this is true of EntitySpaces objects now anyway.  They don't need to be stored post-request.
    I suppose that, if you really wanted to, you could use the ASP.NET cache to (possibly) cache commonly accessed objects across requests (then you'd need some locking to synchronize concurrent accesses), but that's a side issue.

    Last: the developers could add support for identity maps in the same way as they've added hierarchical models: as a feature you can turn on or off at code generation time.
    That way, projects that don't need it don't use it.

    It's true that many projects probably don't need identity maps.  But, for those projects that do make use of large, complex domain models, this could be a big weakness of on the part of entityspaces.  How could they possibly build a network of inter-referenced objects, where there can be more than one reference to the same object? 

     

  •  08-01-2008, 9:32 AM 10519 in reply to 10513

    Re: Identity Mapping

    We will definitely be taking another look at this for ES2009. Good points, probably per thread and global are the two options you'd need, however, we'd probably use TLS (Thread Local Storage) rather than http context as we use the same assmeblies for web and windows forms.
    EntitySpaces | Twitter | BLOG | Please honor our Software License
View as RSS news feed in XML