Multiple Entity Framework Contexts(Multiple database types) on the same time.

Jul 16, 2014 at 5:42 PM
Edited Jul 16, 2014 at 5:43 PM
Hello, im trying to create a project with the Repository Framework.
I will use the Mysql and SQLCE on different contexts at the same time(local database and external database).

What im trying to do:
this.Container.RegisterType<IDataContextAsync, SistemaContext>(new ContainerControlledLifetimeManager());
this.Container.RegisterType<IDataContextAsync, SistemaContext2>(new ContainerControlledLifetimeManager());
this.Container.RegisterType<IUnitOfWorkAsync, UnitOfWork>(new ContainerControlledLifetimeManager());
But when i use the UoW to return me a instance its only return from the SistemaContext2.
public CadastroCliente(IClienteService clienteService, IUnitOfWorkAsync unitOfWork)
        {
            _clienteService = clienteService;
            _unitOfWork = unitOfWork;
            InitializeComponent();
        }
...
_unitOfWork.BeginTransaction();//_unitOfWork is from Context2.
How i get the Contex1 UoW?

Im a noob on prism and repository world... any help can be useful.

Image
Jul 17, 2014 at 4:53 PM
Hi,

Did you get a resolution to this issue, I having the same problem, short of giving them different registration names and then changing UnitOfWork to accommodate this. IoC will always register the last one and overwrite the previous if you don't give a name.

I'm still thinking through the best approach. I want an easy way to have > 1 Context and for that context to be able to cope with the same entity names.

Anyone got any ideas?

Regards

Toby.
Jul 17, 2014 at 5:21 PM
No... i didnt get the resolution... i hope somebody help us or i will not be able to use the repositorie framework.
Jul 18, 2014 at 12:12 PM
Hi,

I got this working with Unity there was a fair amount of messing about and I'll try a quick explanation as I'm short of time. It's all down to how you register the types and instances. I haven't fully battle / performance tested this yet but the approach does work. See below and hope it helps.

Regards

Toby
private void InitializeContainer()
    {
     this.Container.RegisterInstance(new DB1Context("DB1Context"))
                   .RegisterInstance(new DB2Context("DB2Context"))
                    
                   .RegisterType<IDataContextAsync>("DB1Context", new PerResolveLifetimeManager(), new InjectionFactory(c => Container.Resolve<DB1Context>()))
                   .RegisterType<IDataContextAsync>("DB2Context", new PerResolveLifetimeManager(), new InjectionFactory(c => Container.Resolve<DB2Context>()))
                    
                   .RegisterType<IUnitOfWorkAsync, UnitOfWork>("DB1Context", new PerResolveLifetimeManager(), new InjectionFactory(c => new UnitOfWork(Container.Resolve<IDataContextAsync>("DB1Context"))))
                   .RegisterType<IUnitOfWorkAsync, UnitOfWork>("DB2Context", new PerResolveLifetimeManager(), new InjectionFactory(c => new UnitOfWork(Container.Resolve<IDataContextAsync>("DB2Context"))))
                   
                   .RegisterType<IRepositoryAsync<DB1ContextEntity>, Repository<DB1ContextEntity>>(new InjectionFactory(c => new Repository<DB1ContextEntity>(Container.Resolve<IDataContextAsync>("DB1Context"), Container.Resolve<IUnitOfWorkAsync>("DB1Context"))))
                   .RegisterType<IRepositoryAsync<DB2ContextEntity>, Repository<DB2ContextEntity>>(new InjectionFactory(c => new Repository<DB2ContextEntity>(Container.Resolve<IDataContextAsync>("DB2ontext"), Container.Resolve<IUnitOfWorkAsync>("DB2Context"))))
                   .RegisterType<IMyServiceDB1, MyServiceDB1>()
                   .RegisterType<IMyServiceDB2, MyServiceDB2>();

    }
    
public class MyService : Service<DB1ContextRepository>, IMyService
{
    public MyServiceDB1(
        IRepositoryAsync<DB1ContextRepository> dB1ContextRepository,
        [Dependency("DB1Context")] IUnitOfWorkAsync unitOfWork)
    {
        if (dB1ContextRepository == null) { throw new ArgumentNullException("dB1ContextRepository"); }
        if (unitOfWork == null) { throw new ArgumentNullException("unitOfWork"); }
        
        this.dB1ContextRepository = dB1ContextRepository;
        this.unitOfWork = unitOfWork;

    }
    public MyServiceDB2(
        IRepositoryAsync<DB2ContextRepository> dB2ContextRepository,
        [Dependency("DB2Context")] IUnitOfWorkAsync unitOfWork)
    {
        if (dB2ContextRepository == null) { throw new ArgumentNullException("dB2ContextRepository"); }
        if (unitOfWork == null) { throw new ArgumentNullException("unitOfWork"); }
        
        this.dB2ContextRepository = dB2ContextRepository;
        this.unitOfWork = unitOfWork;
    }   
        
}       
Marked as answer by Daniloloko on 7/25/2014 at 10:44 AM
Jul 18, 2014 at 8:41 PM
Thats great! thanks Toby!

But i need some back from the Cordinator or a developer of the Generic unit of work and repository framework.

Because i received none messages back from them on what they say about it, and i think can update the framework to it be more easy to everybody.

Im waiting a back message...

Danilo Breda.
Coordinator
Jul 20, 2014 at 7:12 AM
Edited Jul 20, 2014 at 7:13 AM
Apologize for joining the thread late, have you read this http://blog.longle.net/2013/07/30/bounded-dbcontext-with-generic-unit-of-work-generic-repositories-entity-framework-6-unity-3-0-in-mvc-4? This should get you guys started with the Bounded DataContext pattern.
Marked as answer by lelong37 on 7/20/2014 at 12:13 AM
Developer
Aug 6, 2014 at 8:00 AM
Edited Aug 6, 2014 at 8:02 AM
I'm also late to the party, for which I apologise.

@Daniloloko, if I understand correctly you want to:
  1. Use Unity to do dependency injection
  2. Have two different data contexts, each of which implement IDataContextAsync
  3. Decide within any given method, or even per line of code, which data context to use
These assumptions are important. Your solution for (2) and (3) depends on your DI framework of choice, in this case Unity. The principal is independent of your choice of DI framework: you have two concrete types that need to resolve to the same interface, so that the DI framework can inject the right type for that interface in the right place. However, how you do this depends entirely on your DI framework.

You should essentially be looking at some kind of factory pattern to help you get the correct concrete implementation when you need it, without tightly coupling yourself to the actual types DB1Context and DB2Context inside your class.

I can't point you at an obvious piece of documentation for Unity as this is a more advanced use of DI, but named registrations are a good start. See MSDN: Resolving an Object by Type and Registration Name.
Developer
Aug 6, 2014 at 8:18 AM
Edited Aug 6, 2014 at 8:18 AM
@agilefutures. Your solution is along the right lines, but I have a couple of comments.

You do this:
this.Container
    .RegisterInstance(new DB1Context("DB1Context"))
    .RegisterInstance(new DB2Context("DB2Context"))

    .RegisterType<IDataContextAsync>("DB1Context", 
        new PerResolveLifetimeManager(), new InjectionFactory(
            c => Container.Resolve<DB1Context>()))
    .RegisterType<IDataContextAsync>("DB2Context", 
        new PerResolveLifetimeManager(), new InjectionFactory(
            c => Container.Resolve<DB2Context>()))
As I mentioned above, my Unity is rusty, but
  1. It looks like you register a singleton for each concrete context. Have a look at my gist of a reduced test case to see what I mean, and my comments below for why this isn't a good idea.
  2. Inside an InjectionFactory, I think you should be using c.Resolve, not Container.Resolve. See my gist revision to see the change I think you need to make.
Be aware that a singleton context means you are going to run into potential issues in a multi-threaded/multi-user environment, such as a web site. It is probably ok in a console application, but for any multi-threaded application you will have issues under even small loads.

From MSDN: Working with Db Context > Lifetime
Here are some general guidelines when deciding on the lifetime of the context:
  • When working with long-running context consider the following:
    • As you load more objects and their references into memory, the memory consumption of the context may increase rapidly. This may cause performance issues.
    • Remember to dispose of the context when it is no longer required.
    • If an exception causes the context to be in an unrecoverable state, the whole application may terminate.
    • The chances of running into concurrency-related issues increase as the gap between the time when the data is queried and updated grows.
  • When working with Web applications, use a context instance per request.
  • When working with Windows Presentation Foundation (WPF) or Windows Forms, use a context instance per form. This lets you use change-tracking functionality that context provides.
Additionally, I personally don't recommend singleton contexts as:
  • you are not separating your "units of work" in a multi-threaded application (I mean that both in our UnitOfWork sense, and also in the pattern sense).
  • the EF DbContext is not thread-safe (see the MSDN docs)
Aug 6, 2014 at 8:26 PM
Your absolutely right, I have included a singleton Lifetime Management in my example, however depending on the usage scenario i.e. ASP.NET / MVC then you could use Unity.Mvc.PerRequestLifetimeManager.

Good point about the using the expression c to resolve the context rather than the container instance.

Thanks

Toby.
Aug 6, 2014 at 8:32 PM
Ok... my code now is like this:
Thats all ok?
Im very bad on unity.
            this.Container.RegisterType<IDataContextAsync, SistemaContext>("Teste1", new ContainerControlledLifetimeManager());
            this.Container.RegisterType<IDataContextAsync, SistemaContext2>("Teste2", new ContainerControlledLifetimeManager());

            this.Container.RegisterType<IUnitOfWorkAsync, UnitOfWork>("Teste2UoW", new InjectionConstructor(new ResolvedParameter<IDataContextAsync>("Teste2")));
            this.Container.RegisterType<IUnitOfWorkAsync, UnitOfWork>("Teste1UoW", new InjectionConstructor(new ResolvedParameter<IDataContextAsync>("Teste1")));

            this.Container.RegisterType<IRepositoryAsync<Cliente>, Repository<Cliente>>(new InjectionFactory(c => new Repository<Cliente>(c.Resolve<IDataContextAsync>("Teste1"), c.Resolve<IUnitOfWorkAsync>("Teste1UoW"))));
            this.Container.RegisterType<IRepositoryAsync<Cliente>, Repository<Cliente>>(new InjectionFactory(c => new Repository<Cliente>(c.Resolve<IDataContextAsync>("Teste2"), c.Resolve<IUnitOfWorkAsync>("Teste2UoW"))));
            
            this.Container.RegisterType<IClienteService, ClienteService>();
Aug 6, 2014 at 8:49 PM
Edited Aug 6, 2014 at 8:51 PM
Hi,

Depending on your front end ContainerControlledLifetimeManager is still creates a singleton instance so just take AGBrown's advice above on Lifetime management.

When declaring your Service make sure that you add an Injection Dependency attribute to the constructor arguments so that the correct unit of work is passed in.

i.e.
public class MyService : Service<DB1ContextRepository>, IMyService
{
    public MyServiceDB1(
        IRepositoryAsync<DB1ContextRepository> dB1ContextRepository,
        [Dependency("DB1Context")] IUnitOfWorkAsync unitOfWork)
    {
        if (dB1ContextRepository == null) { throw new ArgumentNullException("dB1ContextRepository"); }
        if (unitOfWork == null) { throw new ArgumentNullException("unitOfWork"); }
        
        this.dB1ContextRepository = dB1ContextRepository;
        this.unitOfWork = unitOfWork;
    }
Regards

Toby.
Developer
Aug 6, 2014 at 9:28 PM
You could also make your life a little simpler and actually use different interfaces for each unit of work. Don't be afraid to extend the framework.

To me the below code would be very expressive; it would convey your intent perfectly. But even better: your DI setup code for production and testing will become a lot less confusing. It also matches what you are actually doing: you are using the IDataContextAsync contract but you are applying to two separate underlying data stores - same contract, different roles.

I didn't go so far as to extend the IRepositoryProvider as well (which you would need to do) or in fact test this, but I think it should work ...
public interface IDataContextTeste1 : IDataContextAsync { }
public class DataContextTeste1 : DataContext, IDataContextTeste1
{
    public DataContextTeste1() : base("connStringTeste1") { }
}

public interface IDataContextTeste2 : IDataContextAsync { }
public class DataContextTeste2 : DataContext, IDataContextTeste2
{
    public DataContextTeste2() : base("connStringTeste2") { }
}

public interface IUnitOfWorkTeste1 : IUnitOfWorkAsync { }
public class UnitOfWorkTeste1 : UnitOfWork, IUnitOfWorkTeste1
{
    public UnitOfWorkTeste1(IDataContextTeste1 dataContext, IRepositoryProvider repositoryProvider) 
        : base(dataContext, repositoryProvider) { }
}

public interface IUnitOfWorkTeste2 : IUnitOfWorkAsync { }
public class UnitOfWorkTeste2 : UnitOfWork, IUnitOfWorkTeste1
{
    public UnitOfWorkTeste2(IDataContextTeste2 dataContext, IRepositoryProvider repositoryProvider)
        : base(dataContext, repositoryProvider) { }
}
Aug 7, 2014 at 9:04 AM
Hi!

I had to use multiple contexts in a Windows Forms solution 4 months ago. I don't know if this would be helpful. At least, I think it's related to @Daniloloko's issue.
I used Ninject and this was my approach using attributes:
public class ProgramModule : NinjectModule
{
    public ProgramModule() { }

    public override void Load()
    {
        // Unit of Work & Generic Repository Patterns.
        Bind<IUnitOfWork>().To<UnitOfWork>().WhenTargetHas<InDirectorioEntitiesScope>().InSingletonScope();
        Bind<IUnitOfWork>().To<UnitOfWork>().WhenTargetHas<InGdEntitiesScope>().InSingletonScope();
        Bind<IUnitOfWork>().To<UnitOfWork>().WhenTargetHas<InVariosEntitiesScope>().InSingletonScope();

        // DataContexts: When any ancestor in the inheritance chain has been labeled with any of these attributes.
        Bind<IDataContext>().To<DirectorioEntities>()
            .WhenAnyAncestorMatches(Predicates.TargetHas<InDirectorioEntitiesScope>).InSingletonScope();
        Bind<IDataContext>().To<GdEntities>()
            .WhenAnyAncestorMatches(Predicates.TargetHas<InGdEntitiesScope>).InSingletonScope();
        Bind<IDataContext>().To<VariosEntities>()
            .WhenAnyAncestorMatches(Predicates.TargetHas<InVariosEntitiesScope>).InSingletonScope();
    }
}
These are the attributes...
namespace ExportGdInvoicesToSap.DependencyInjection
{
    public class InDirectorioEntitiesScope : Attribute { }
    public class InGdEntitiesScope : Attribute { }
    public class InVariosEntitiesScope : Attribute { }
}
These are the predicates...
public static class Predicates
{
    public static bool TargetHas<T>(IContext context)
    {
        return TargetHas<T>(context, false);
    }

    public static bool TargetHas<T>(IContext context, bool inherit)
    {
        var target = context.Request.Target;
        return target != null && target.IsDefined(typeof(T), inherit);
    }
}
Program...
static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // Configure Ninject Kernel
        CompositionRoot.Wire(new ProgramModule());
        IProgramKernel program = CompositionRoot.Resolve<ProgramKernel>();

        // Run App.
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm(program));
    }
}
Example of service...
public class PersonasService : Service, IPersonasService
{
    public PersonasService([InDirectorioEntitiesScope]IUnitOfWork unitOfWork)
        : base(unitOfWork)
    {
    }

    public Persona GetPersona(string iniciales) 
    {
        return UnitOfWork.Repository<Persona>().Query()
            .Filter(p => p.iniciales == iniciales).Get().FirstOrDefault();
    } 
}
I wish to take this opportunity to congratulate the team on the quality of the framework and their knowledge. You're awesome, guys!
Aug 7, 2014 at 5:27 PM
Thanks all for the support... it's helped me a lot... someday the discussions can help somebody that had the same problem as i.

Danilo Breda
Sep 12, 2014 at 5:30 AM
Hi Danilo,

I am having the same issue, I have a prototype with Northwind and Adventure works together.

Here is my UnityConfig.
        container.RegisterType<IRepositoryProvider, RepositoryProvider>(
            new PerRequestLifetimeManager(),
            new InjectionConstructor(new object[] { new RepositoryFactories() })
        );

        container.RegisterType<IUnitOfWorkAsync, UnitOfWork>(new PerRequestLifetimeManager());

        // Northwind
        container.RegisterInstance(new NorthwindContext());

        container.RegisterType<IDataContextAsync, NorthwindContext>(new PerRequestLifetimeManager());

        container.RegisterType<IRepositoryAsync<Customer>, Repository<Customer>>();
        container.RegisterType<IRepositoryAsync<Product>, Repository<Product>>();
        container.RegisterType<IRepositoryAsync<Order>, Repository<Order>>();

        container.RegisterType<ICustomerService, CustomerService>();
        container.RegisterType<IProductService, ProductService>();
        container.RegisterType<IOrderService, OrderService>();

        // Adventure Works
        container.RegisterInstance(new AdventureWorksContext());

        container.RegisterType<IDataContextAsync, AdventureWorksContext>(new PerRequestLifetimeManager());

        container.RegisterType<IRepositoryAsync<Address>, Repository<Address>>();
        container.RegisterType<IRepositoryAsync<Person>, Repository<Person>>();

        container.RegisterType<IPersonService, PersonService>();
        container.RegisterType<IAddressService, AddressService>(); 
Can you please post your UnityConfig, so we can see how you resolve your issue.

Thanks!!!
May 1, 2015 at 4:02 PM
Danilo, me explica como vc conseguiu configurar o my sql em cima deste projeto.
O meu gera erro toda vez q vou criar o banco, sendo q a versão em Sql Server funciona perfeitamente. Peço sua ajuda, já q aparentemente vc conseguiu fazer funcionar.
May 6, 2015 at 5:44 PM
Edited May 6, 2015 at 5:46 PM
Thats my SistemaContext:
namespace Sistema.DataAccess
{
    public class SistemaContext : Sistema.Common.Repository.DataContext
    {
        static SistemaContext()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<SistemaContext, Sistema.DataAccess.Migrations.Configuration>()); //migrations here
        }
        public SistemaContext()
            : base(ConnectionStringFactory.GetDbConnection()) //take the connection here
        {
            
        }

        public DbSet<Cliente> Cliente { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.Configurations.Add(new Sistema.DataAccess.Mapping.ClienteConfig());
        }
    }
}
My ConnectionStringFactory returns the connection and set the providers depending on the flag.
namespace Sistema.DataAccess
{
    public class ConnectionStringFactory
    {
        public static DBType DbTypeNow { get; set; }
        public enum DBType
        {
            MYSQL,
            SQLCE
        }

        public static void LoadConfigDBType()
        {
            AppConfigSettingsManager connectionmanager = new AppConfigSettingsManager();
            DbTypeNow = (DBType)Enum.Parse(typeof(DBType), connectionmanager.GetAppConfigSettings("Tipo"), false);//converte o string para enum representado
        }

        public static string GetConnectionString()
        {
            AppConfigSettingsManager connectionmanager = new AppConfigSettingsManager();
            if (DbTypeNow == DBType.MYSQL)
            {
                return connectionmanager.GetAppConfigSettings("ConnMYSQL");
            }
            else if (DbTypeNow == DBType.SQLCE)
            {
                return connectionmanager.GetAppConfigSettings("ConnSQLCE");
            }
            throw new Exception("ConnectionStringFactory ERROR - Tipo de banco de dados invalido");
        }

        public static DbConnection GetDbConnection()
        {
            if (DbTypeNow == DBType.MYSQL)
            {
                var factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient");
                var connection = factory.CreateConnection();
                if (connection != null)
                {
                    connection.ConnectionString = GetConnectionString();
                    return connection;
                }
            }
            else if (DbTypeNow == DBType.SQLCE)
            {
                var factory = DbProviderFactories.GetFactory("System.Data.SqlServerCe.4.0");
                var connection = factory.CreateConnection();
                if (connection != null)
                {
                    connection.ConnectionString = GetConnectionString();
                    return connection;
                }
            }
            throw new Exception("ConnectionStringFactory ERROR - Tipo de banco de dados invalido");
        }
    }
}
My configuration for Migrations:
namespace Sistema.DataAccess.Migrations
{
    public sealed class Configuration : DbMigrationsConfiguration<Sistema.DataAccess.SistemaContext>
    {
        public Configuration()
        {
            DbInterception.Add(new NLogCommandInterceptor());// guardar logs

            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true; 
            
            if (ConnectionStringFactory.DbTypeNow == ConnectionStringFactory.DBType.MYSQL)
            {
                SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); //Mysql da erro se nao colocar isso.(Pelo que vi da para colocar no App.config tambem.)
                SetHistoryContextFactory("MySql.Data.MySqlClient", (conn, schema) => new MySQLHistoryContext(conn, schema));
            }
        }

        protected override void Seed(Sistema.DataAccess.SistemaContext context)
        {
            //  This method will be called after migrating to the latest version.

            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
}
I have this too but i dont know why... but its for remake the column size of migrations table.
namespace Sistema.DataAccess
{
    public class MySQLHistoryContext : System.Data.Entity.Migrations.History.HistoryContext
    {
        public MySQLHistoryContext(System.Data.Common.DbConnection connection, string defaultSchema)
            : base(connection, defaultSchema)
        {

        }

        protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<System.Data.Entity.Migrations.History.HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
            modelBuilder.Entity<System.Data.Entity.Migrations.History.HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired();
        }
    }
}
Aug 20, 2015 at 8:33 AM
Edited Aug 20, 2015 at 8:48 AM
AGBrown's solution (Aug 6, 2014) of extending the framework to create context specific interfaces worked very well for me. I am using the Autofac DI with three different data contexts. For each data context all I had to do was to create the following inherited interfaces and concrete implementations:

Interfaces:
IxxxDataContextAsync { }
IxxxRepositoryAsync { }
IxxxUnitOfWorkAsync { }

Implementations:
xxxDataContext : DataContext, IxxxDataContextAsync { }
xxxRepository<TEntity> : Repository<TEntity>, IxxxRepositoryAsync<TEntity> where TEntity : class, IObjectState { }
xxxUnitOfWork : UnitOfWork, IxxxUnitOfWorkAsync { }

Whilst I have not yet fully tested all scenarios, there does not appear to be a need to extended the IService and Service implementations.

In the Autofac container, all I needed to do was to wire up the context specific interfaces. As AGBrown stated, this approach seems far more explicit in intent than creating complex, DI specific solutions.
Sep 29, 2015 at 2:06 AM
Edited Sep 29, 2015 at 2:08 AM
Thanks AGBrown and Neilski. Your approach helped a lot in resolving the problem.