7

I want to isolate the "account" tables from "data" tables for reusing on another application.
I trying to use .NET Core 2.0 + Angular template, creating 2 connection strings, but when a create the another AbpDbContext, I couldn't set the connection strings for the context.
The example of using multiples DB contexts on their GitHub uses .NET framework, not .NET core, which is permitted to set the connection string on dbcontext ctor. How can I do this on .net core 2 template?

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
Kross
  • 303
  • 1
  • 4
  • 14

2 Answers2

22

Connect with multiple database in ASP.NET ZERO/ASP.NET BOILERPLATE.

Note - Use seperate DB Context to use multiple Databases.

Step 1. Create modal class in "MultipleDbContextEfCoreDemo.Core" Project for your tables.

[Table ("tblStudent")] //Is in First Database
public class Student : Entity<long> {
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    protected Student () { }
}

[Table ("tblCourses")] //Is in Second Database
public class Courses : Entity<long> {
    public int ID { get; set; }
    public string CourseName { get; set; }
    public string Standard { get; set; }

    protected Courses () { }
}

Step 2. In same project("MultipleDbContextEfCoreDemo.Core" Project) create/use "MultipleDbContextEfCoreDemoConsts.cs" file to add Database Connection names.

public class MultipleDbContextEfCoreDemoConsts
    {
        public const string LocalizationSourceName = "MultipleDbContextEfCoreDemo";

        public const string ConnectionStringName = "Default";

        public const string SecondDbConnectionStringName = "Second";
    }

Step 3. In "MultipleDbContextEfCoreDemo.EntityFrameworkCore" Project goto "EntityFrameworkCore" Folder and create individual "DBContext" and "DbContextConfigurer" file for each database connection to which you want to connect.

FirstDatabase Setting -

required files to connect to first db -

1. FirstDbContext.cs

public class FirstDbContext : AbpDbContext, IAbpPersistedGrantDbContext {
    /* Define an IDbSet for each entity of the application */
    public DbSet<PersistedGrantEntity> PersistedGrants { get; set; }
    public virtual DbSet<Student> Student { get; set; }

    public FirstDbContext (DbContextOptions<FirstDbContext> options) : base (options) {

    }

    protected override void OnModelCreating (ModelBuilder modelBuilder) { }
}

2. FirstDbContextConfigurer

public static class FirstDbContextConfigurer {
    public static void Configure (DbContextOptionsBuilder<FirstDbContext> builder, string connectionString) {
        builder.UseSqlServer (connectionString);
    }

    public static void Configure (DbContextOptionsBuilder<FirstDbContext> builder, DbConnection connection) {
        builder.UseSqlServer (connection);
    }
}

SecondDatabase Setting -

required files to connect to second db -

1. SecondDbContext.cs

public class SecondDbContext : AbpDbContext, IAbpPersistedGrantDbContext {
    /* Define an IDbSet for each entity of the application */
    public DbSet<PersistedGrantEntity> PersistedGrants { get; set; }
    public virtual DbSet<Student> Student { get; set; }

    public SecondDbContext (DbContextOptions<SecondDbContext> options) : base (options) {

    }

    protected override void OnModelCreating (ModelBuilder modelBuilder) { }
}

2. SecondDbContextConfigurer

public static class SecondDbContextConfigurer {
    public static void Configure (DbContextOptionsBuilder<SecondDbContext> builder, string connectionString) {
        builder.UseSqlServer (connectionString);
    }
    public static void Configure (DbContextOptionsBuilder<SecondDbContext> builder, DbConnection connection) {
        builder.UseSqlServer (connection);
    }
}

Step 4. Then in same project("MultipleDbContextEfCoreDemo.EntityFrameworkCore") add "MyConnectionStringResolver.cs"

public class MyConnectionStringResolver : DefaultConnectionStringResolver
        {
            public MyConnectionStringResolver(IAbpStartupConfiguration configuration) 
                : base(configuration)
            {
            }

            public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
            {
                if (args["DbContextConcreteType"] as Type == typeof(SecondDbContext))
                {
                    var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
                    return configuration.GetConnectionString(MultipleDbContextEfCoreDemoConsts.SecondDbConnectionStringName);
                }

                return base.GetNameOrConnectionString(args);
            }
        }

Step 5. Then in same project("MultipleDbContextEfCoreDemo.EntityFrameworkCore"), Update "MultipleDbContextEfCoreDemoEntityFrameworkCoreModule.cs" file to replace the "IConnectionStringResolver" with our custom implementation MyConnectionStringResolver.

[DependsOn(typeof(MultipleDbContextEfCoreDemoCoreModule), typeof(AbpEntityFrameworkCoreModule))]
    public class MultipleDbContextEfCoreDemoEntityFrameworkCoreModule : AbpModule
    {
        public override void PreInitialize()
        {
            Configuration.ReplaceService<IConnectionStringResolver, MyConnectionStringResolver>();

            // Configure first DbContext
            Configuration.Modules.AbpEfCore().AddDbContext<FirstDbContext>(options =>
            {
                if (options.ExistingConnection != null)
                {
                    FirstDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
                }
                else
                {
                    FirstDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
                }
            });

            // Configure second DbContext
            Configuration.Modules.AbpEfCore().AddDbContext<SecondDbContext>(options =>
            {
                if (options.ExistingConnection != null)
                {
                    SecondDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
                }
                else
                {
                    SecondDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
                }
            });
        }

        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(typeof(MultipleDbContextEfCoreDemoEntityFrameworkCoreModule).GetAssembly());
        }
    }

Step 6. Create the Service in "MultipleDbContextEfCoreDemo.Application" project with Dto, Interface and Service Class.

ITestAppService.cs-

public interface ITestAppService : IApplicationService
    {
        List<string> GetStudentAndCourses();

     }

TestAppService.cs

public class TestAppService : MultipleDbContextEfCoreDemoAppServiceBase, ITestAppService
    {
        private readonly IRepository<Student> _studentRepository; //in the first db
        private readonly IRepository<Courses> _coursesRepository; //in the second db

        public TestAppService(
            IRepository<Student> studentRepository,
            IRepository<Courses> coursesRepository
        )
        {
            _studentRepository = studentRepository;
            _coursesRepository = coursesRepository;
        }

        //a sample method uses both databases concurrently
        public List<string> GetStudentAndCourses()
        {
            List<string> names = new List<string>();

            var studentNames = _studentRepository.GetAllList().Select(p => "Student: " + p.FirstName).ToList();
            names.AddRange(peopleNames);

            var courseNames = _coursesRepository.GetAllList().Select(p => "Course: " + p.CourseName).ToList();
            names.AddRange(courseNames);

            return names;
        }
    }

Step 7. Add Database connectionStrings to your MultipleDbContextEfCoreDemo.Web/MultipleDbContextEfCoreDemo.Web.Host project's "appsettings.json" file.

{
  "ConnectionStrings": {
    "Default":
      "Server=XXX.XXX.XX.XX;Database=firstDB;Integrated Security=False;TrustServerCertificate=True;User ID=XX;Password=XXX;",
    "Second":
      "Server=XXX.XXX.XX.XX;Database=secondDB;Integrated Security=False;TrustServerCertificate=True;User ID=XX;Password=XXX;"
  }
  }

Step 8. Use Service in your angular/MVC project.

Ravindra Vairagi
  • 1,055
  • 15
  • 22
  • YES! I like your answer. I'll test it. Thank you! – Kross Apr 02 '18 at 13:34
  • 1
    I used this code and although it works great with my local appsettings file, I'm struggling with user secrets or Azure AppService settings. I think it might be missing the environment and userSecrets: true in AppConfigurations.Get(...). You could inject the IHostingEnvironment to access that information at runtime. Something like: var configuration = AppConfigurations.Get(_env.ContentRootPath, _env.EnvironmentName, _env.IsDevelopment()); – Federico Rodriguez Aug 01 '19 at 15:53
  • 1
    If you have issues with Step 5, please ensure that you are referencing Abp.Configuration.Startup – André Haupt Sep 25 '19 at 20:46
  • 1
    This is a related link: https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/MultipleDbContextEfCoreDemo – Sniipe Jan 26 '21 at 16:36
2

I ran into the same problem as @Sniipe with regards to navigational properties.

The problem was that I was inheriting from

 AbpZeroDbContext<Tenant, Role, User, PortalDbContext>

in my second DBContext instead of just

AbpDbContext
Dharman
  • 30,962
  • 25
  • 85
  • 135
Eamon
  • 51
  • 1
  • 4