2012-08-29

ECO Persistence Mapper per connection

The Enterprise Core Objects Persistence Mapper is a singleton which is used by all EcoSpace instances.  It’s purpose is to load mapping information from the DB when your app starts and to cache it, improving performance.

I needed a connection per client, all running within a single application.  This was a problem because once the PMP is created its connection string is tied to a single database.  So I had to come up with a new PersistenceMapperDynamicSharer component.  It is used on the EcoSpace to specify the PMapper type; additionally you can specify a connection string to use.

It works by dynamically creating a descendant class of your PersistenceMapperProvider at runtime, one for each connection string.


public class PersistenceMapperDynamicSharer : PersistenceMapperSharer
{
static ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
static Dictionary
<string, PersistenceMapperProvider> MapperProviders = new Dictionary<string, PersistenceMapperProvider>();

[Browsable(false)]
public string ConnectionString { get; set; }

public override IPersistenceMapper GetPersistenceMapper(ITypeSystemService typeSystemService)
{
return GetPersistenceMapperProvider().GetPersistenceMapper(typeSystemService);
}

public override void ReturnPersistenceMapper(IPersistenceMapper persistenceMapper)
{
GetPersistenceMapperProvider().ReturnPersistenceMapper(persistenceMapper);
}

PersistenceMapperProvider GetPersistenceMapperProvider()
{
//No connection string = default mapper provider
if (string.IsNullOrEmpty(ConnectionString))
return PersistenceMapperProvider.GetInstance(MapperProviderType);

PersistenceMapperProvider result;
Locker.EnterUpgradeableReadLock();
try
{
if (MapperProviders.TryGetValue(ConnectionString, out result))
return result;
Locker.EnterWriteLock();
try
{
var mapperType = CreateNewPersistenceMapperProviderType();
result = (PersistenceMapperProvider)Activator.CreateInstance(mapperType);
((IDynamicallySharedPersistenceMapper)result).SetConnectionString(ConnectionString);
MapperProviders[ConnectionString] = result;
}
finally
{
Locker.ExitWriteLock();
}
}
finally
{
Locker.ExitUpgradeableReadLock();
}
return result;
}

Type CreateNewPersistenceMapperProviderType()
{
string guid = Guid.NewGuid().ToString();
var assemblyName = new AssemblyName(guid);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
System.Reflection.Emit.AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(guid);
var typeBuilder = moduleBuilder.DefineType(
guid,
TypeAttributes.Class | TypeAttributes.Public,
MapperProviderType);
return typeBuilder.CreateType();
}
}


On my EcoSpace I have a method called ActivateWithConnectionString which I can use whenever I need to activate the EcoSpace connecting to a connection string OTHER than the default…



public void ActivateWithConnectionString(string connectionString)
{
persistenceMapperDynamicSharer1.ConnectionString = connectionString;
Active = true;
}


And on my PersistenceMapperProvider I need to implement IDynamicallySharedPersistenceMapper like so…



void IDynamicallySharedPersistenceMapper.SetConnectionString(string connectionString)
{
sqlConnection1.ConnectionString = connectionString;
}


Now I can benefit from the performance gains from the singleton-pattern implemented by PersistenceMapperProvider and connect to different databases.

No comments: