Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you define DBContext properly in a Class Library Project?

I'm using entity framework 7 and created two projects. One project as a ASP.NET 5 Web API project and the other is a Class Library Project (Package) where I wanted to store all my Data Access Layer logic in. That way I can use this package for another reporting project down the road and other additional services I may make.

Basically I have a simple post in my controller of the web api project that calls a function in my data library project. When the function initiates the database it says database not defined even tho it's defined in both projects.


ERROR

{"No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services."}

CODE

Data Library Project: InsertPerson

public int InsertPerson(tbl_Person person)
{
    using (var db = new AppContext())
    {
        try
        {
            db.tbl_Person.Add(person);
            db.SaveChanges();
            return person.PersonID;
        }

        catch
        {
            return 0;
        }
    }
}

CONFIGURATION FILES

Web API Project: Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<AppContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>();

    services.AddMvc();
}

Web API Project: appsettings.json

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=localhost;Database=testDB;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Web API Project: project.json

{
  "userSecretsId": "blah,
  "version": "1.0.0-rc1-final",
  "compilationOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "DataLibrary": "",
    "Microsoft.AspNet.Authentication.OAuthBearer": "1.0.0-beta7",
    "Microsoft.AspNet.Http.Abstractions": "1.0.0-rc1-final",
    "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-final",
    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
    "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final",
    "Remotion.Linq": "2.0.1"
  },

  "commands": {
    "web": "Microsoft.AspNet.Server.Kestrel"
  },

  "frameworks": {
    "dnx451": { },
    "dnxcore50": { }
  },

  "exclude": [
    "wwwroot",
    "node_modules"
  ],
  "publishExclude": [
    "**.user",
    "**.vspscc"
  ]
}

Data Library Project: Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<AppContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>();

    services.AddMvc();
}

Data Library Project: appsettings.json

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=localhost;Database=testDB;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Data Library Project: AppContext.cs

namespace DataLibrary
{
    public class AppContext : IdentityDbContext<ApplicationUser>
    {
        public DbSet<tbl_Person> tbl_Person { get; set; }

        public static AppContext Create()
        {
            return new AppContext();
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            new tbl_PersonMap(builder.Entity<tbl_Person>());
            base.OnModelCreating(builder);
        }
    }
}
like image 392
chris Avatar asked Oct 19 '22 20:10

chris


1 Answers

It looks like your issue in the sample code is that you are newing up the AppContext instance instead of resolving it from the ServiceProvider. When you initialize a DbContext this way, a new ServiceProvider is implicitly created for the context and none of the configuration in Startup.cs is respected by that context. This is a pattern you would want to use if you were configuring your context in OnConfiguring and not interested in dependency injection in the rest of your application.

In this kind of situation, I would expect you to get the AppContext through constructor injection. For this kind of solution, you would also need to register your Data Access Layer classes in the ServiceCollection. The result should look a bit more like this:

Data Library Project: AppContext.cs

namespace DataLibrary
{
    public class AppContext : IdentityDbContext<ApplicationUser>
    {
        public DbSet<tbl_Person> tbl_Person { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            new tbl_PersonMap(builder.Entity<tbl_Person>());
            base.OnModelCreating(builder);
        }
    }
}

Data Library Project: PersonHelper.cs

namespace DataLibrary
{
    class PersonHelper
    {
        private readonly AppContext db;

        public PersonHelper(AppContext context)
        {
            db = context;
        }

        public int InsertPerson(tbl_Person person)
        {
            try
            {
                db.tbl_Person.Add(person);
                db.SaveChanges();
                return person.PersonID;
            }
            catch
            {
                return 0;
            }
        }
    }
}

Web API Project: Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<AppContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>();

    services.AddScoped<PersonHelper>();

    services.AddMvc();
}

Web API Project: MyController.cs

[Route("api/[controller]")]
public class MyController : Controller
{
    private readonly personHelper helper;

    public MyController(PersonHelper helper)
    {
        this.helper = helper;
    }

    // POST api/my
    [HttpPost]
    public void Post([FromBody]string value)
    {
        var person = new tbl_Person
        {
          // ...
        }

        return helper.InsertPerson(person);
    }
}

you may even consider adding an extension method on IServiceCollection for your Data Access Layer classes to help reduce duplication in your configuration, especially as you add more common services.

like image 107
MiKary Avatar answered Oct 31 '22 19:10

MiKary