dotnet / EntityFramework.Docs

Documentation for Entity Framework Core and Entity Framework 6
https://docs.microsoft.com/ef/
Creative Commons Attribution 4.0 International
1.59k stars 1.95k forks source link

Need a ConsoleApp example with Connection Strings #1344

Closed kae36 closed 4 years ago

kae36 commented 5 years ago

It would be nice to have an .net core 2.2 example of a windows console application using MS SQL Server connection strings. There doesn't seem to be any that I can find. console apps don't seem to use Startup, so some example for main would be nice.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Going-Gone commented 5 years ago

You can use a json file to store the strings, and reference them there.

kae36 commented 5 years ago

There are several examples of the JSON file format and db connection strings. There are also examples of code used in a Web Application to initialize the DbContext from the connection strings in the JSON file, but there are no examples of how to initialize a DbContext used in a Console Application. It would just be nice to have an example.

LinchaiTheShinigami commented 5 years ago

I tried using the example provided under the DotNet Framework example to create a console application.

But I am obtaining a NullReferenceException on the Configuration Manager when running the command: PM> dotnet.exe ef migrations add StudentDatabase

Stack Trace:

System.NullReferenceException: Object reference not set to an instance of an object.
   at CreateDatabaseModels.AssetContext.OnConfiguring(DbContextOptionsBuilder optionsBuilder) in E:\CodeProgramming\C#\Projects\HelloSQLServerDatabase\CreateDatabaseModels\Program.cs:line 57
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.Internal.InternalAccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Object reference not set to an instance of an object.

Main Program:

using Microsoft.EntityFrameworkCore;
using System;
using System.Configuration;

namespace CreateDatabaseModels
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.ReadKey();
        }
    }

    public class AssetContext:DbContext
    {
        public DbSet<EntityDataModel.Students> Students { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(ConfigurationManager.ConnectionStrings["test"].ConnectionString); //This is where it goes wrong!
            }
        }

    }
}

In a separate file:

using System;
using System.ComponentModel.DataAnnotations;
namespace CreateDatabaseModels.EntityDataModel
{
    public class Students
    {
        [Key] public int StudentId { get; set; }
        public int Marks { get; set; }
    }
}

In app.config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="setting" value="value"/>
  </appSettings>
  <connectionStrings>
    <add name="test" connectionString="Server=localhost;Database=StudentDatabase;Trusted_Connection=True"/>
  </connectionStrings>
</configuration>
montwell commented 5 years ago

This was my solution. I created my own ConfigurationManager with a lazy loaded singleton pattern and accessed as follows.

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            IConfigurationManager configurationManager = ConfigurationManager.Instance;

            optionsBuilder.UseSqlServer(configurationManager.ConnectionStrings("Database"));
        }
using Microsoft.Extensions.Configuration;
using System;
using System.IO;

namespace example
{
    public interface IConfigurationManager
    {
        string ConnectionStrings(string connectionString);
    }

    class ConfigurationManager : IConfigurationManager
    {
        private static Lazy<ConfigurationManager> instance = new Lazy<ConfigurationManager>(() => new ConfigurationManager());

        public static ConfigurationManager Instance => instance.Value;

        private readonly IConfigurationRoot configuration;

        private ConfigurationManager()
        {
            IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

            configuration = configurationBuilder.Build();
        }

        public string ConnectionStrings(string connectionString)
        {
            return configuration.GetConnectionString(connectionString);
        }
    }
}
LinchaiTheShinigami commented 5 years ago

@montwell - So you need to create your own implementation of IConfigurationManager so that the migration can read the contents of a config file?

montwell commented 5 years ago

Well it seems like in all the examples given you would need to load the appsettings.json in the context that you were using it. Then you would need to pass those parameters along through the entirety of your program. This seemed like the best way to be able to access the file throughout a complex program.

ajcvickers commented 4 years ago

Duplicate of #481

dchase-sir commented 3 years ago

I went to the duplicate 481 and didn't find any reference to documentation being created to address this, I am having the same issue, I am creating a .NET Core console application, and want to move the connection string out of the Context.cs. So far it looks like I need to basically make my own config file and just read it. The code to read such file is not in the Context.cs and I'm having trouble getting it to acknowledge the existence of the variable in another part of the program.

All of this just to move the connection string, which the link in the file does not address console apps at all - everything is for Web, which has internal wirings setup for you. I've spent a week now looking at how DI works in .NET core (basically answering every other question I've never asked), the entire time thinking I'm missing something or in the wrong part of the internet again.

Any help on where this documentation exists would be helpful, but please don't refer me to generic DI or ASP.NET Core Web documentation as the mechanism is granted ahead of time in that environment.

ajcvickers commented 3 years ago

@dchase-sir There is no code in EF Core to read configuration from a file. You can choose to use whatever mechanism you feel is appropriate in your code to store and read the connection string, and then pass that value to UseSqlServer or equivalent. This could be the .NET Core configuration system that ASP.NET Core uses, but it could also be a simple text file, and anything else you choose.

dchase-sir commented 3 years ago

Aj, Thank you - any tips on making that available to the DbContext? I tried setting the connection string as a public static variable in Program.cs (there is no Startup.cs, and if I add one the constructor does not get called), but it still couldn't see the public static variable even though it's in the same namespace. If I use DI, do I just use AddService/etc from Main? I appreciate your response it's a breath of fresh air regardless!

ajcvickers commented 3 years ago

@dchase-sir I typically don't use D.I. in a console application, so I just want to be able to use new to create a DbContext instance. If I have the connection string available at that time, then a pattern like this works well:

public class SomeDbContext : DbContext
{
    private readonly string _connectionString;

    public SomeDbContext(string connectionString)
    {
        _connectionString = connectionString;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseSqlServer(_connectionString);
}
dchase-sir commented 3 years ago

I just experienced a moment of clarity when I realized that in my program, I can just new the context like I did in EF6 and that the constructor in the Context with DbContextOptions is irrelevant in my scenario unless I choose to make it more complex.

Thank you!