WPF - Multiple Async CslaDataProvider + ObjectFactory

WPF - Multiple Async CslaDataProvider + ObjectFactory

Old forum URL: forums.lhotka.net/forums/t/8001.aspx


Kevin Fairclough posted on Monday, November 16, 2009

Hi All,

This problem refs ObjectFactory (last post) and Multiple Async CslaDataProviders.

We are seeing very strange behaviour here where the ObjectFactory is involved.  We have two BO's in test (Item & ShelfLifeLookup) both are being retrieved async using separate CslaDataProviders.  I also re-created prob by debugging a test by firing off two simultaneous calls to the Get methods of each BO.

When we try and parse the factoryName in our GenericFactoryLoader and create the instance of the factory, it seems as if the parser contains different values (when it throws an Exception) to the factoryName parameter of the method, (it parses in the first line of this method), i.e. the other BO's parsed factoryName.

Synch calls work ok for these BO's.  Below is a Debug output from the Loader which show calls in call order before the Exception which is in GetFactory.
Also GetFactoryType is called excessively for some reason.

OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactoryType(Factory=INameValueListFactory;List=OurApp.Domain.ShelfLifeLookup,OurApp.Domain;Key=System.Guid;Value=System.String)
OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactoryType(Factory=OurApp.Data.Factories.ItemFactory,OurApp.Data;Item=OurApp.Domain.Item,OurApp.Domain)
OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactoryType(Factory=OurApp.Data.Factories.ItemFactory,OurApp.Data;Item=OurApp.Domain.Item,OurApp.Domain)
OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactoryType(Factory=OurApp.Data.Factories.ItemFactory,OurApp.Data;Item=OurApp.Domain.Item,OurApp.Domain)
OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactoryType(Factory=OurApp.Data.Factories.ItemFactory,OurApp.Data;Item=OurApp.Domain.Item,OurApp.Domain)
OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactoryType(Factory=INameValueListFactory;List=OurApp.Domain.ShelfLifeLookup,OurApp.Domain;Key=System.Guid;Value=System.String)
OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactory(Factory=OurApp.Data.Factories.ItemFactory,OurApp.Data;Item=OurApp.Domain.Item,OurApp.Domain)
OurApp.Data.Factories.Loader.GenericFactoryLoader - GetFactory(Factory=INameValueListFactory;List=OurApp.Domain.ShelfLifeLookup,OurApp.Domain;Key=System.Guid;Value=System.String)



Hope someone can shed some light.

TIA
Kevin



RockfordLhotka replied on Monday, November 16, 2009

The data portal creates exactly one factory loader instance, and reuses that instance for all calls. So if you create a custom factory loader you must make it totally threadsafe (no instance level fields without locking, etc).

The data portal does get the factory type several times, because it needs to look for the RunLocal and Transactional attributes, etc. Because the data portal uses a pipeline model, each step in the pipeline is independent of any other step, and so there's basically no global state.

In 4.0 I might rework the data portal to use more of a container model - still a basic pipeline, but with an overarching contextual model too.

Kevin Fairclough replied on Tuesday, November 17, 2009

Ok that explains it.  I should really make my GenericFactoryLoader a static class then for clarity but then I can't implement IObjectFactoryLoader.

Now the pain! Any good docs/links around for doing this?

Thanks Rocky




Kevin Fairclough replied on Tuesday, November 17, 2009

Hi Rocky

I have come up with this, seems to work but not sure about it (I'm still pretty wet behind the ears with C#)

public class GenericFactoryLoader : IObjectFactoryLoader
{
        private static readonly GenericFactoryLoader instance = new GenericFactoryLoader();      
      
        static GenericFactoryLoader()
        {
            StructureMap.ObjectFactory.Initialize(x => x.AddRegistry(new GenericFactoryRegistry()));          
        }

        public static GenericFactoryLoader Instance
        {
            get { return instance; }
        }

        #region IObjectFactoryLoader Members

        public object GetFactory(string factoryName)
        {
            return instance.GetFactorySafely(factoryName);           
        }

        public Type GetFactoryType(string factoryName)
        {
           return instance.GetFactoryTypeSafely(factoryName); 
        }
        #endregion                                          

    #region Privates
    Methods GetFactoryTypeSafely,GetFactorySafely that instantiate local parser. 
    Also use Activator.CreateInstance (ThreadSafe I think)
    MethodCaller.CallPropertySetter (Is this threadsafe?)   
    #endregion
}


So,  Is a call to MethodCaller.CallPropertySetter thread-safe?

Thanks
Kevin

RockfordLhotka replied on Saturday, November 21, 2009

CallPropertySetter() is absolutely not threadsafe.

.NET really has no threadsafe way to call a property setter. The only way to do such a thing would be to have a syncroot on your object, and to make sure all code everywhere uses that syncroot to lock the object before setting a property - a virtual impossibility in normal coding.

Kevin Fairclough replied on Monday, November 23, 2009

Thanks Rocky

I'll remove this call,  I was using this to set a specific repository type for my factory.
I guess this should be StructureMap's responsibility or the factory itself.

I wonder how dependency injection works when setting properties?

Kevin

RockfordLhotka replied on Monday, November 23, 2009

This is less a DI issue than a multi-threaded issue with singleton objects.

 

The factory loader is a singleton – it is a single instance used by multiple threads – and so it really shouldn’t have properties or fields – just two stateless methods.

 

I think you are trying to use the same instance of your object as well, which means it too must be written to be threadsafe – generally meaning no properties or fields, and only stateless methods. If you must have properties and fields, you must remember that they are shared across all threads – setting a property changes it for all threads. Therefore you must implement appropriate locking around all access to that object’s properties/fields.

 

A potentially better solution – certainly simpler – is to create a different object instance for each thread. Then the object can have properties/fields with no issue, because only one thread will interact with that particular object instance at a time.

Kevin Fairclough replied on Monday, November 23, 2009

My GenericFactoryLoader doesn't have any fields or props I removed them so that is ok. 

In the GetFactorySafely method I create an instance of the factory object that will be used to create the BO.  I was originally calling MethodCaller.CallPropertySetter(newFactoryObject,"Repository",newReposObject ) inside the stateless method, therefore I think my original code was ok, assuming I've understood.

Thanks
Kevin

Copyright (c) Marimer LLC