16

I have tried to figure this out, but I am stuck.

I have a Net Core 2 application with Service/Repo/Api/Angular layers - but now I want to 'bolt on' a console application and access all the goodies I have already built up. I seem to be in a mess of static objects and DI and null parameters. Anyway, here is a simplified version of my code.

namespace SimpleExample
{

    class Program
    {

        private static ApplicationDbContext _appDbContext;

        public Program(ApplicationDbContext appDbContext)
        {
            _appDbContext = appDbContext;
        }

        static void Main(string[] args)
        {
            var instance = new Program();  // this doesn't work!
            var instance = new Program(_appDbContext);  // neither does this!
            instance.GetData();
        }


        private void GetData()
        {
            Console.WriteLine("Let's read some data! Press a key to continue.");
            Console.ReadLine();

            var data = "my data";
            var result = GetId(data);
        }


        private string GetId(string original)
        {
            var data = _appDbContext.Data
                .Where(x => x.Name == original.Trim)
                .FirstOrDefault();

            return data;

        }
    }

}

I am getting the classic

'An object reference is required for the non-static field'

error. Then from investigating on here I changed things to static and then everything becomes null.

It's not just the DbContext I am trying to inject. I'm also trying to inject

private ManagerService _managerService;

but getting same errors.

Update

If I try

private static ApplicationDbContext _appDbContext = new ApplicationDbContext();

as suggested a few times below, then I get the error

There is no argument given that corresponds to the required formal parameter 'options' of 'ApplicationDbContext.ApplicationDbContext(DbContextOptions)'

halfer
  • 19,824
  • 17
  • 99
  • 186
spankymac
  • 746
  • 2
  • 7
  • 14

2 Answers2

41

OK, I have figured this out, and I'll post my answer for anyone else struggling in this situation.

When you launch the console app, your normal startup.cs doesn't execute, so you have to put a lot of that code in your console app.

    private static SiteService _siteService;
    private static ApplicationDbContext _appDbContext;

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

        services.AddTransient<ISiteInterface, SiteRepo>();
        services.AddTransient<SiteService>();
        services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer("blah-blah"));            

        var serviceProvider = services.BuildServiceProvider();                                             
        _siteService = serviceProvider.GetService<SiteService>();
        _appDbContext = serviceProvider.GetService<ApplicationDbContext>();    
        GetData();
    }

and now your _appDbContext will be available throughout the rest of your console app.

Hope that helps!

spankymac
  • 746
  • 2
  • 7
  • 14
  • where is ServiceCollection defined? – Mike W Jan 20 '21 at 15:32
  • 3
    It's from * using Microsoft.Extensions.DependencyInjection; * Add that to the top. – spankymac Jan 22 '21 at 05:50
  • 1
    I'm not sure if this is because I'm using .NET 6 but I ran into a lifetime issue. By default context and options are scoped but that lifetime type is available only in asp.net apps, not in console apps. So I had to explicitly set the scope to singleton or transient. I'm wondering if you ran into that issue when you did this. – Stack Undefined Jun 01 '22 at 14:44
2

Basically, if you do not plan extensive usage of DbContext nor use DI, there is no need for ServiceProvider. Just remember to make DbContext instance short living and use it for single unit-of-work, not longer.

Your context may look like this:

using Microsoft.EntityFrameworkCore;

namespace YourNamespace;

public class ApplicationContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Your conn string");
    }

    public DbSet<YourType> YourEntity { get; set; }
}

You can pass conn string by ApplicationContext ctor as well. This is nicely explained here by Microsoft .

Then you can utilise your ApplicationContext like this:

// Unit-of-work closed in using statement
// Here you can query/update your DbContext
using (var dbContext = new ApplicationContext())
{
    var queryResult = dbContext.YourEntity.Where(....);
}

You can prepare number of such units-of-work as separate methods for querying a database.
Your repository service can consist of these methods.
Then you can instantiate the service as needed.

Marek
  • 69
  • 4