1

The structure of projects looks like this:

  • Cars (ASP.NET Core MVC. Here we have a connection string)

  • Cars.Persistence (ASP.NET Core Class library. Here we have Repository, Database First)

I've created a Model by the following command from this msdn docs:

Scaffold-DbContext "Server=PC\SQL2014XP;Database=Cars;Trusted_Connection=True;" 
    Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

So far so good. However, now carsContext has hard coded connection string in Cars.Persistence - ASP.NET Core Class library:

public partial class carsContext: DbContext
{
    public carsContext()
    {
    }

    public carsContext(DbContextOptions<carsContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Cars> Cars { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer("Server=PC\SQL2014XP...");// hard coded 
                                                                  // connection string
        }
    }
}

At first, I thought to create own appsettings.json in my class library Cars.Persistence. However, according to this post it is not advisable to have appsettings.json file in Class Library..

I've read this approach, however, the hard coded string will appear again, if I will run this command again:

Scaffold-DbContext "Server=PC\SQL2014XP;Database=Cars;Trusted_Connection=True;" 
        Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

So my question is how can I use connection string(located in Cars project) in my class library Cars.Persistence?

UPDATE:

I've commented out the following code:

public partial class eshopContext : DbContext
{

     public eshopContext(DbContextOptions<eshopContext> options): base(options)
     {} 

     // public eshopContext(){}        

      /*protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     {
         if (!optionsBuilder.IsConfigured)
         {
             #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
             optionsBuilder.UseSqlServer("Server=...");
         }

      }*/

 }
Tony Ngo
  • 19,166
  • 4
  • 38
  • 60
StepUp
  • 36,391
  • 15
  • 88
  • 148
  • 1
    I did something similar with Dapper and the IDbConnection class. Since you're using Core, you can take advantage of the IOptions interface wrapper class and set the connection string at startup near the composition root. It's been awhile since I've used Entity Framework so my example may not help, but you can check it out on Github: https://github.com/B-Richie/Dapper_DAL – user10728126 Aug 28 '19 at 19:59
  • You're making it a billion times hard than it actually is! – user10728126 Aug 30 '19 at 13:22
  • @user10728126 could you say how can I do it simpler? – StepUp Aug 30 '19 at 13:29
  • you have the example above or go to the other thread: https://stackoverflow.com/questions/51304432/how-to-read-connection-string-inside-net-standard-class-library-project-from-as/57727560#57727560 – user10728126 Aug 30 '19 at 13:37
  • Just use Configuration to map your appsettings.json section containing your connectionstring and set the value at startup near the composition root using IOptions. – user10728126 Aug 30 '19 at 13:39

4 Answers4

4

You can take the advantage of .Net Core Dependency Injection and out of box features. Your connection string will remain in web project but you can use DB context without declaring any connection string in Class Library Project. I am sharing code sample from my existing project.

Set Connection String

You have referenced connection string in your start up and added to services. You don't need to define the connection string again and use the db context using Built in DI. The code could look like this !

Start up class

Set up your SQL config. Look closely at MigrationsAssembly this is where you would reference your class library project.

public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration)
{

    // Add DbContext using SQL Server Provider
    services.AddDbContext<PaymentDbContext>(options =>
        options.UseSqlServer(configuration.GetConnectionString("myconnectionstring"), x => x.MigrationsAssembly("Payment.Persistence")));

    return services;
}

Context Class

This class is in your class library project.

public class PaymentDbContext : DbContext
    {
        public PaymentDbContext(DbContextOptions<PaymentDbContext> options)
            : base(options)
        {

        }

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


    }    

Use DI to access Context

    private readonly PaymentDbContext _context;


     public PaymentsRepository(PaymentDbContext dbContext)
     {
     _context = dbContext;
    }
Imran Arshad
  • 3,794
  • 2
  • 22
  • 27
  • Thanks for your reply! Do I need three projects - Net Core MVC, MigrationAssembly(getting connString here from Net Core MVC and set connString to RepositoryAssembly?) and RepositoryAssembly(Database First here)? – StepUp Aug 29 '19 at 06:19
  • you only need 2 projects. .net core mvc and class library project. this is i am calling as assembly that contains migrations and database context (db first). your startup class has migrarion assembly method pointing this class library – Imran Arshad Aug 29 '19 at 08:30
  • Thank you! So should `.NET Core MVC` have a dependency to `class library`? Could you write your project dependencies? – StepUp Aug 29 '19 at 08:35
  • have a look at this example. this is my code on github https://github.com/ImranMA/MicroCouriers/tree/master/src/Services/Payment – Imran Arshad Aug 29 '19 at 10:53
  • look for payment api and persistence . ignore rest – Imran Arshad Aug 29 '19 at 10:53
  • Many thanks for your reply! Please, tell me is it okay that I've commented out the constructor and `OnConfiguring` code generated by Entity Framework? I mean I touched a code which is not written by me. I really appreciate yours opinion. I use Database First. I've added the commented code to my question. Thanks in advance. – StepUp Aug 29 '19 at 20:25
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/198687/discussion-between-imran-arshad-and-stepup). – Imran Arshad Aug 29 '19 at 23:23
1

You could use an Environment Variable in your Cars MVC project located in launchSettings.json. Something like "MSSQL_CONN_STR": "Server=PC\2014XP.."

Then in the Cars.Persistence class library you can access the Environment Variable like this

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 {
    if (!optionsBuilder.IsConfigured)
    {         
     optionsBuilder.UseSqlServer(Environment.GetEnvironmentVariable("MSSQL_CONN_STR");
    }
}
SWilko
  • 3,542
  • 1
  • 16
  • 26
1

Below is how I read connetion string from netcoreapp2.2. You can see how I config at here

I create 1 file name appsetting.json have structure like this

"WebJobSettings": {
        "DBConnectionString": "Data Source=.;Initial Catalog=CMSCore;Integrated Security=True"
    },

Then in my Program.cs

 public static class Program
        {
            public static IConfigurationRoot Configuration;

            public static void Main()
            {
                var serviceCollection = new ServiceCollection();
                ConfigureServices(serviceCollection);

                // create service provider
                var serviceProvider = serviceCollection.BuildServiceProvider();

                // entry to run app
                //serviceProvider.GetService<WebJob>().Run();
                serviceProvider.GetService<WebJob>().RunImageProcessQueue();
            }

            private static void ConfigureServices(IServiceCollection serviceCollection)
            {
                var currentDir = Directory.GetCurrentDirectory();

                // build configuration
                var configuration = new ConfigurationBuilder()
                    .SetBasePath(currentDir)
                    .AddJsonFile("appsettings.json", false)
                    .Build();
                serviceCollection.AddOptions();

                serviceCollection.Configure<WebJobSettings>(configuration.GetSection("WebJobSettings"));
                serviceCollection.Configure<QueueSettings>(configuration.GetSection("QueueSettings"));
                serviceCollection.Configure<AssetSettings>(configuration.GetSection("AssetSettings"));

                // add app
                serviceCollection.AddTransient<WebJob>();
            }

Then simply Configuration pattern in my WebJob.cs file like this

public class WebJob
{
    private readonly IOptions<WebJobSettings> _webJobSettings;
    private readonly IOptions<QueueSettings> _queueSettings;
    private readonly IOptions<AssetSettings> _assetSettings;

    public WebJob(
        IOptions<WebJobSettings> webJobSettings,
        IOptions<QueueSettings> queueSettings,
        IOptions<AssetSettings> assetSettings)
    {
        _webJobSettings = webJobSettings;
        _queueSettings = queueSettings;
        _assetSettings = assetSettings;
    }
Tony Ngo
  • 19,166
  • 4
  • 38
  • 60
1

From this issue , the connection string added in the DbContext when scaffolding DbContext is default settings .If you don't want to have such a long and ugly connection string to display, you could use Named connection strings instead .

From this discussion , you could use -Connection name in the Scaffold-DbContext command to get the connection string which is set in appsetting.json of web application

Scaffold-DbContext -Connection name=DefaultConnection  Microsoft.EntityFrameworkCore.SqlServer -OutputDir DbModels

"ConnectionStrings": {
  "DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=MVC2_2Db;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
},

The above code will generate the following code in DbContext

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer("name=DefaultConnection");
        }
    }

Or ,if you set the external connection string , you could remove the hard coded connection strings due to it will only be called when you forget to set the connection to the database .You could refer to the code added in Startup.cs show as below:

 var connection = @"Server=(localdb)\mssqllocaldb;Database=MVC2_2Db;Trusted_Connection=True;ConnectRetryCount=0";

 services.AddDbContext<Cars.Persistence.DbModels.MVC2_2DbContext>(options => options.UseSqlServer(connection));
Xueli Chen
  • 11,987
  • 3
  • 25
  • 36