WPF - Multiple Async CslaDataProvider + ObjectFactoryWPF - 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