Extending DbContext to avoid code duplication - c#

I have a project with multiple DbContext classes: one for every domain. Since EF Core isn't perfect I have some functionality I have to add to every DbContext like allowing it to handle the new JSON functionality of SQL Server 2017. There is other functionality I'm adding too but for the sake of brevity I have removed it from the example.
To add this JSON functionality to every DbContext I work with a BaseDbContext which is an abstract class that simply extends the DbContext. One of the prerequisites for the functionality is interception of SQL queries that are sent to the database. To perform this I make use of the tactic described here.
In the next few weeks we are moving into different development environments and I want to inject the correct connection string based on the current environment through the Options pattern. This doesn't play well with the interception tactic. Since to perform interception GetService<DiagnosticSource>() is called which actually triggers the OnConfiguring function BEFORE the continuation of the constructor of my SomeDbContext, which means my options are never filled in leading to a NullReferenceException.
How can I extend DbContext without triggering OnConfiguring while at the same time avoiding code duplication? I can also run a custom EF Core but I'd like to avoid that if at all possible.
public abstract class BaseDbContext : DbContext, IBaseDbContext
{
protected BaseDbContext()
{
DiagnosticListener listener = this.GetService<DiagnosticSource>() as DiagnosticListener; // This calls OnConfiguring.
listener.SubscribeWithAdapter(new JsonInterceptor());
}
[DbFunction("JSON_VALUE", "")]
public static string JsonValue(string source, string path)
{
throw new NotSupportedException();
}
[DbFunction("TRY_CAST", "")]
public static string TryCast(string source, string typeName)
{
throw new NotSupportedException();
}
}
public class SomeDbContext : BaseDbContext, ISomeDbContext
{
private readonly SomeDbContextOptions _options;
public DbSet<Order> Orders { get; set; }
public SomeDbContext(IOptions<SomeDbContextOptions> options)
{
_options = options.Value;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// This is called before _options is set meaning it is null in the line below.
optionsBuilder
.UseSqlServer(_options.ConnectionString);
}
}

Related

DbContext in separate class library project with dependency injection

I'm currently creating a WebAPI for a school project.
Before is worked with DI but i can't seem to get it working in the current context.
for the project i'm using multiple projects for the layer where my DAL needs to get the dbContext through DI.
DbContext:
public FICTIEContext(DbContextOptions<FICTIEContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
}
}
startup register:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<FICTIEContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Database")));
}
Call to the DAL Constructor:
public class LoginBL
{
private LoginDAL _LoginDAL;
public bool LoginValidation(string login, string password)
{
_LoginDAL = new LoginDAL(); //Gives the error. As i remember given that dependency injection works correctly it should work like this.
return _LoginDAL.LoginValidation(login, password); ;
}
}
for my DAL i'm using a c# class library project with a class where i have a constructor which has the DbContext as parameter:
private readonly FICTIEContext _Database;
public LoginDAL(FICTIEContext database)
{
_Database = database;
}
When using this code i can't seem to call the constructor without getting errors about not giving any parameters with the call to the DAL.
Which part am i missing withing my solution and how can i fix it.
Of course you need to provide an instance of FICTIEContext when creating an instance of a LoginDAL given your current implementation. That's the purpose of using dependency injection.
The question is whether you actually need or want dependency injection in this case? I doubt it. There should be no point of using nor testing the DAL class without a DbContext anyway.
In this case you could simply let the DAL class create an instance of the FICTIEContext internally. You may of course also provide an overload of the constructor that accepts a context if you want to:
private readonly FICTIEContext _Database;
public LoginDAL()
{
_Database = new FICTIEContext();
}
public LoginDAL(FICTIEContext database)
{
_Database = database;
}
Then the consumer of the class can choose whether to supply a custom FICTIEContext or let the LoginDAL class create a default one.

Entity Framwork Core Add-Migration fails

I'm trying to use Ef Core in my project.
The structure is a little different, in the sense that I'm not using EfCore insite the WebApi.csproj. In fact I have a different dll. and a DependenciesResolver.dll that handles all my dependency injection.
In my EfCore.dll I've installed both
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer
Now when I try to run the command (the dll in which I'm running is the EfCore.dll)
Add-Migration Name
I get this :
An error occurred while accessing the IWebHost on class 'Program'.
Continuing without the application service provider. Error: Object
reference not set to an instance of an object. Unable to create an
object of type 'StoreContext'. Add an implementation of
'IDesignTimeDbContextFactory' to the project, or see
https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns
supported at design time.
The structure of the sln is like this
WebApi | EfCore.dll | DependencyResolver.dll and I want to keep it this way, don't want to permit using EfCore in my WebApi.
What is the resolution for this issue ?
If this helps within the EfCore.dll I have this.
public sealed partial class StoreContext : DbContext, IStoreContext
{
private string _connectionString;
public StoreContext(string connectionString) : base()
{
_connectionString = connectionString;
Database.EnsureCreated();
}
/// db.tbls
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddOrderConfiguration();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
}
which is called by DependencyResolver like this
private DependenciesResolver RegisterInfrastructure()
{
_serviceCollection.AddScoped<StoreContext>(factory => new StoreContext(_connectionString));
return this;
}
and the DependencyResolver is then called by the WebApi
Please have a look at this: https://learn.microsoft.com/en-us/ef/core/miscellaneous/cli/dbcontext-creation
The error message clearly specifies the EF Core tools can't create an instance of your Context at design-time.
If you can define a constructor with no parameters for your StoreContext that would work, otherwise you need tell the tools how to create an instance of your context at design-time by defining a class that implements the IDesignTimeDbContextFactory interface.

Dependency Injection in Model classes (entities)

I am building an ASP.NET Core MVC application with Entity Framework Code-First.
I implemented a simple repository pattern, providing basic CRUD operations for all the model classes I have created.
I chose to follow all the recommendations provided in docs and DI is one of these.
In ~~.NET 5~~ (6 years later update: .net 5 was the alpha name of .net core 1.0) dependency injection works very well for any class that we do not directly instantiate (e.g.: controllers, data repositories, ...).
We simply inject them via the constructor, and register the mappings in the Startup class of the application :
// Some repository class
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
}
// In startup.cs :
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyRepository, MyRepository>();
The problem is that in some of my model classes, I would like to inject some of the dependencies I have declared.
But I think that I cannot use the constructor injection pattern because model classes are often explicitly instantiated. Therefore, I would need to provide myself with the dependencies, which I can't.
So my question is: is there another way than constructor injection to inject dependencies, and how? I was for example thinking of an attribute pattern or something like that.
As I already explained in a comment, when creating an object using new, there is nothing from the dependency injection framework that is involved in the process. As such, it’s impossible for the DI framework to magically inject things into that object, it simply doesn’t know about it.
Since it does not make any sense to let the DI framework create your model instances (models are not a dependency), you will have to pass in your dependencies explicitly if you want the model to have them. How you do that depends a bit on what your models are used for, and what those dependencies are.
The simple and clear case would be to just have your model expect the dependencies on the constructor. That way, it is a compile time error if you do not provide them, and the model has access to them right away. As such, whatever is above, creating the models, is required to have the dependencies the model type needs. But at that level, it’s likely that this is a service or a controller which has access to DI and can request the dependency itself.
Of course, depending on the number of dependencies, this might become a bit complicated as you need to pass them all to the constructor. So one alternative would be to have some “model factory” that takes care of creating the model object. Another alternative would also be to use the service locator pattern, passing the IServiceCollection to the model which can then request whatever dependencies it needs. Note that is generally a bad practice and not really inversion of control anymore.
Both these ideas have the issue that they modify the way the object is created. And some models, especially those handled by Entity Framework, need an empty constructor in order for EF to be able to create the object. So at that point you will probably end up with some cases where the dependencies of your model are not resolved (and you have no easy way of telling).
A generally better way, which is also a lot more explicit, would be to pass in the dependency where you need it, e.g. if you have some method on the model that calculates some stuff but requires some configuration, let the method require that configuration. This also makes the methods easier to test.
Another solution would be to move the logic out of the model. For example the ASP.NET Identity models are really dumb. They don’t do anything. All the logic is done in the UserStore which is a service and as such can have service dependencies.
The pattern often used in domain driven design (rich domain model to be specific) is to pass the required services into the method you are calling.
For example if you want to calculate the vat, you'd pass the vat service into the CalculateVat method.
In your model
public void CalculateVat(IVatCalculator vatCalc)
{
if(vatCalc == null)
throw new ArgumentNullException(nameof(vatCalc));
decimal vatAmount = vatcalc.Calculate(this.TotalNetPrice, this.Country);
this.VatAmount = new Currency(vatAmount, this.CurrencySymbol);
}
Your service class
// where vatCalculator is an implementation IVatCalculator
order.CalculateVat(vatCalculator);
Finally your service can inject another services, like a repository which will fetch the tax rate for a certain country
public class VatCalculator : IVatCalculator
{
private readonly IVatRepository vatRepository;
public VatCalculator(IVatRepository vatRepository)
{
if(vatRepository == null)
throw new ArgumentNullException(nameof(vatRepository));
this.vatRepository = vatRepository;
}
public decimal Calculate(decimal value, Country country)
{
decimal vatRate = vatRepository.GetVatRateForCountry(country);
return vatAmount = value * vatRate;
}
}
I know my answer is late and may not exactly what you're asking for, but I wanted to share how I do it.
First of all: If you want to have a static class that resolves your dependencies this is a ServiceLocator and it's Antipattern so try not to use it as you can.
In my case I needed it to call MediatR inside of my DomainModel to implement the DomainEvents logic.
Anyway, I had to find a way to call a static class in my DomainModel to get an instance of some registered service from DI.
So I've decided to use the HttpContext to access the IServiceProvider but I needed to access it from a static method without mention it in my domain model.
Let's do it:
1- I've created an interface to wrap the IServiceProvider
public interface IServiceProviderProxy
{
T GetService<T>();
IEnumerable<T> GetServices<T>();
object GetService(Type type);
IEnumerable<object> GetServices(Type type);
}
2- Then I've created a static class to be my ServiceLocator access point
public static class ServiceLocator
{
private static IServiceProviderProxy diProxy;
public static IServiceProviderProxy ServiceProvider => diProxy ?? throw new Exception("You should Initialize the ServiceProvider before using it.");
public static void Initialize(IServiceProviderProxy proxy)
{
diProxy = proxy;
}
}
3- I've created an implementation for the IServiceProviderProxy which use internally the IHttpContextAccessor
public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
private readonly IHttpContextAccessor contextAccessor;
public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
public T GetService<T>()
{
return contextAccessor.HttpContext.RequestServices.GetService<T>();
}
public IEnumerable<T> GetServices<T>()
{
return contextAccessor.HttpContext.RequestServices.GetServices<T>();
}
public object GetService(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetService(type);
}
public IEnumerable<object> GetServices(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetServices(type);
}
}
4- I should register the IServiceProviderProxy in the DI like this
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
.......
}
5- Final step is to initialize the ServiceLocator with an instance of IServiceProviderProxy at the Application startup
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
As a result now you can call the ServiceLocator in your DomainModel classes "Or and needed place" and resolve the dependencies that you need.
public class FakeModel
{
public FakeModel(Guid id, string value)
{
Id = id;
Value = value;
}
public Guid Id { get; }
public string Value { get; private set; }
public async Task UpdateAsync(string value)
{
Value = value;
var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
await mediator.Send(new FakeModelUpdated(this));
}
}
The built-in model binders complain that they cannot find a default ctor. Therefore you need a custom one.
You may find a solution to a similar problem here, which inspects the registered services in order to create the model.
It is important to note that the snippets below provide slightly different functionality which, hopefully, satisfies your particular needs. The code below expects models with ctor injections. Of course, these models have the usual properties you might have defined. These properties are filled in exactly as expected, so the bonus is the correct behavior when binding models with ctor injections.
public class DiModelBinder : ComplexTypeModelBinder
{
public DiModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders)
{
}
/// <summary>
/// Creates the model with one (or more) injected service(s).
/// </summary>
/// <param name="bindingContext"></param>
/// <returns></returns>
protected override object CreateModel(ModelBindingContext bindingContext)
{
var services = bindingContext.HttpContext.RequestServices;
var modelType = bindingContext.ModelType;
var ctors = modelType.GetConstructors();
foreach (var ctor in ctors)
{
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
if (parameters.All(p => p != null))
{
var model = ctor.Invoke(parameters);
return model;
}
}
return null;
}
}
This binder will be provided by:
public class DiModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
return new DiModelBinder(propertyBinders);
}
return null;
}
}
Here's how the binder would be registered:
services.AddMvc().AddMvcOptions(options =>
{
// replace ComplexTypeModelBinderProvider with its descendent - IoCModelBinderProvider
var provider = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
var binderIndex = options.ModelBinderProviders.IndexOf(provider);
options.ModelBinderProviders.Remove(provider);
options.ModelBinderProviders.Insert(binderIndex, new DiModelBinderProvider());
});
I'm not quite sure if the new binder must be registered exactly at the same index, you can experiment with this.
And, at the end, this is how you can use it:
public class MyModel
{
private readonly IMyRepository repo;
public MyModel(IMyRepository repo)
{
this.repo = repo;
}
... do whatever you want with your repo
public string AProperty { get; set; }
... other properties here
}
Model class is created by the binder which supplies the (already registered) service, and the rest of the model binders provide the property values from their usual sources.
HTH
Is there another way than constructor injection to inject dependencies, and how?
The answer is "no", this cannot be done with "dependency injection". But, "yes" you can use the "service locator pattern" to achieve your end-goal.
You can use the code below to resolve a dependency without the use of constructor injection or the FromServices attribute. Additionally you can new up an instance of the class as you see fit and it will still work -- assuming that you have added the dependency in the Startup.cs.
public class MyRepository : IMyRepository
{
public IMyDependency { get; } =
CallContextServiceLocator.Locator
.ServiceProvider
.GetRequiredService<IMyDependency>();
}
The CallContextServiceLocator.Locator.ServiceProvider is the global service provider, where everything lives. It is not really advised to use this. But if you have no other choice you can. It would be recommended to instead use DI all the way and never manually instantiate an object, i.e.; avoid new.
I'm simply adding some supplemental information here to the answers provided that can help.
IServiceProvider was provided in the accepted answer, but not the important IServiceProvider.CreateScope() method. You can use it to create scopes as necessary that you added through ConfigureServices.
I'm not sure if IServiceProvider is actually a Service Locator pattern behind the scenes or not, but it's how you create scopes as far as I know. At least in the case if it is a Service Locator pattern, it's the official one for today in .NET, and so it's not compounded by the problems of writing your own Service Locator, which I also agree is anti-pattern.
Example, Startup.cs/ConfigureServices and Configure:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SomeDbContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("Databases").GetSection("SomeDb")["ConnectionString"]);
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}, ServiceLifetime.Scoped);
services.AddMvcCore().AddNewtonsoftJson();
services.AddControllersWithViews();
}
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)
{
...
IServiceScope scope = provider.CreateScope();
SomeDbContext context = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
SomeModelProxyClass example = new SomeModelProxyClass(context);
await example.BuildDefaults(
Configuration.GetSection("ProfileDefaults").GetSection("Something"),
Configuration.GetSection("ProfileDefaults").GetSection("SomethingSomething"));
scope.Dispose();
}
The above is for doing some default interactions on Startup, maybe if you need to build some default records in your database on a first usage, just as an example.
Ok so let's get to your repository and dependency though, will they work?
Yep!
Here's a test in my own CRUD project, I made a simple minimalist implementation of your IMyDependency and IMyRepository like so, then added them scoped as you did to Startup/ConfigureServices:
public interface IMyRepository
{
string WriteMessage(string input);
}
public interface IMyDependency
{
string GetTimeStamp();
}
public class MyDependency : IMyDependency
{
public MyDependency()
{
}
public string GetTimeStamp()
{
return DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
}
}
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public string WriteMessage(string input)
{
return input + " - " + _myDependency.GetTimeStamp();
}
}
Here ContextCRUD is a Model class from my own project not derived from Scaffold-DbContext tooling like my other database classes, it's a container of logic from those scaffold Model classes, and so I put it in the namespace Models.ProxyModels to hold its own business logic for doing CRUD operations so that the Controllers are not gummed up with logic that should be in the Model:
public ContextCRUD(DbContext context, IServiceProvider provider)
{
Context = context;
Provider = provider;
var scope = provider.CreateScope();
var dep1 = scope.ServiceProvider.GetService<IMyRepository>();
string msg = dep1.WriteMessage("Current Time:");
scope.Dispose();
}
Debugging I get back the expected results in msg, so it all checks out.
The calling code from the Controller for reference, just so you can see how IServiceProvider is passed from upstream by constructor injection in the Controller:
[Route("api/[controller]")]
public class GenericController<T> : Controller where T: DbContext
{
T Context { get; set; }
ContextCRUD CRUD { get; set; }
IConfiguration Configuration { get; set; }
public GenericController(T context, IConfiguration configuration, IServiceProvider provider)
{
Context = context;
CRUD = new ContextCRUD(context, provider);
Configuration = configuration;
}
...
You can do it, check out [InjectionMethod] and container.BuildUp(instance);
Example:
Typical DI constructor (NOT NEEDED IF YOU USE InjectionMethod) public
ClassConstructor(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }
This attribute causes this method to be called to setup DI.
[InjectionMethod] public void Initialize(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }

ASP.NET vNext EF7 dbContext issues

I am starting a vNext project, and I'm having some issues kicking it off the ground. I have added a table to the ApplicationDbContext class, and it successfully created the table in the db (which in my case is in Azure). However, I can't seem to correctly instantiate a dbContext to use in my Controllers.
In my experience with previous ASP.NET EF projects, I could instantiate the ApplicationDbContext class without passing it any parameters, but in the case of vNext however, it seems to expect a number of things (IServiceProvider, and IOptionsAccessor<DbContextOptions>). I have tried creating a parameter-less constructor, but the App breaks due to not knowing what connection strings to use. My code is below -- as you see in the OnConfiguring(DbContextOptions options) override, I force the connection string in via the DbContextOptions, but that's obviously not ideal, and I feel like I'm just not understanding where those two IServiceProvider, and IOptionsAccessor parameters need to come from.
Thanks for any help!
namespace Project.Models
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string CompanyName { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
private static bool _created = false;
public DbSet<Business> Businesses { get; set; }
public ApplicationDbContext()
: base()
{
if (!_created)
{
Database.EnsureCreated();
_created = true;
}
}
protected override void OnConfiguring(DbContextOptions options)
{
var configuration = new Configuration();
configuration.AddJsonFile("config.json");
configuration.AddEnvironmentVariables();
options.UseSqlServer(configuration.Get("Data:DefaultConnection:ConnectionString"));
}
public ApplicationDbContext(IServiceProvider serviceProvider, IOptionsAccessor<DbContextOptions> optionsAccessor)
: base(serviceProvider, optionsAccessor.Options)
{
// Create the database and schema if it doesn't exist
// This is a temporary workaround to create database until Entity Framework database migrations
// are supported in ASP.NET vNext
if (!_created)
{
Database.EnsureCreated();
_created = true;
}
}
}
}
IServiveProvider and IOptionAccessor are injected by the Dependency Injection
the ASP.Net Core DI has limitation, you cannot have more than one constructor.
Read this: http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx

EF code first: inherited dbcontext creates two databases

I'm trying to create a base dbcontext that contains all the common entities that will always be reused in multiple projects, like pages, users, roles, navigation etc.
In doing so I have a ContextBase class that inherits DbContext and defines all the DbSets that I want. Then I have a Context class that inherits ContextBase where I define project specific DbSets. The classes are defined as follows:
public class ContextBase : DbContext
{
public virtual DbSet<User> Users { get; set; }
//more sets
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UsersConfiguration());
//add more configurations
}
}
public class Context : ContextBase
{
public DbSet<Building> Buildings { get; set; }
//some more project specific sets
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new BuildingsConfiguration());
//add more project specific configs
}
}
In my global.asax:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
where Configuration referes to a class inheriting DbMigrationsConfiguration and overriding the Seed method.
The two context classes are defined in the same namespace, but cross assembly (in order that I may update the base project in multiple existing projects without touching the project specific code) - not sure if this is relevant.
MY PROBLEM:
When running this code, it works fine, but when looking in the Database, it actually creates two different databases!! One containing all the base entity tables and one containing BOTH base and custom tables. CRUD operations are only performed on the custom version (which is obviousely what I want), but why does it create the schema of the other one as well?
Any help is appreciated, thanks!
UPDATE:
The following code is what I ended up with. It isn't ideal, but it works. I would still love to get feedback on ways to improve this, but in the meantime I hope this helps further the process. I REALLY DO NOT RECOMMEND DOING THIS! It is extremely error prone and very frustrating to debug. I'm merely posting this to see if there is any better ideas or implementations to achieve this.
One (but not the only) issue still existing is that the MVC views have to be manually added to projects. I've added it to the Nuget package, but it takes 2 to 3 hours to apply a nuget package with so many files when VS is connected to TFS. With some more work and a custom View engine the views can be precompiled (http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html).
The solution is split into the Base Framework projects and the Custom projects (each category includes its own models and repository pattern). The framework projects are packaged up in a Nuget package and then installed in any custom projects allowing the common functionality of any project like user, role and permission management, content management, etc (often referred to as the Boiler Plate) to be easily added to any new projects. This allows any improvements of the boilerplate to be migrated in any existing custom projects.
Custom Database Initializer:
public class MyMigrateDatabaseToLatestVersion : IDatabaseInitializer<Context>
{
public void InitializeDatabase(Context context)
{
//create the base migrator
var baseConfig = new FrameworkConfiguration();
var migratorBase = new DbMigrator(baseConfig);
//create the custom migrator
var customConfig = new Configuration();
var migratorCustom = new DbMigrator(customConfig);
//now I need to check what migrations have not yet been applied
//and then run them in the correct order
if (migratorBase.GetPendingMigrations().Count() > 0)
{
try
{
migratorBase.Update();
}
catch (System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
{
//if an error occured, the seed would not have run, so we run it again.
baseConfig.RunSeed(context);
}
}
if (migratorCustom.GetPendingMigrations().Count() > 0)
{
try
{
migratorCustom.Update();
}
catch (System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException)
{
//if an error occured, the seed would not have run, so we run it again.
customConfig.RunSeed(context);
}
}
}
}
Framework's DB Migrations Configuration:
public class FrameworkConfiguration: DbMigrationsConfiguration<Repository.ContextBase>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
public void RunSeed(Repository.ContextBase context)
{
Seed(context);
}
protected override void Seed(Repository.ContextBase context)
{
// This method will be called at every app start so it should use the AddOrUpdate method rather than just Add.
FrameworkDatabaseSeed.Seed(context);
}
}
Custom Project's DB Migrations Configuration:
public class Configuration : DbMigrationsConfiguration<Repository.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
public void RunSeed(Repository.Context context)
{
Seed(context);
}
protected override void Seed(Repository.Context context)
{
// This method will be called at every app start so it should use the AddOrUpdate method rather than just Add.
CustomDatabaseSeed.Seed(context);
}
}
The custom DbContext
//nothing special here, simply inherit ContextBase, IContext interface is purely for DI
public class Context : ContextBase, IContext
{
//Add the custom DBsets, i.e.
public DbSet<Chart> Charts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//Assign the model configs, i.e.
modelBuilder.Configurations.Add(new ChartConfiguration());
}
}
Framework DbContext:
//again nothing special
public class ContextBase: DbContext
{
//example DbSet's
public virtual DbSet<Models.User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder);
}
In the global.asax AppStart:
//first remove the base context initialiser
Database.SetInitializer<ContextBase>(null);
//set the inherited context initializer
Database.SetInitializer(new MyMigrateDatabaseToLatestVersion());
In the web.config:
<connectionStrings>
<!--put the exact same connection string twice here and name it the same as the base and overridden context. That way they point to the same database. -->
<add name="Context" connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=CMS2013; Integrated Security=SSPI;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient"/>
<add name="ContextBase" connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=CMS2013; Integrated Security=SSPI;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient"/>
</connectionStrings>
(from the comments)
You're creating ContextBase objects directly, apparently as new T() in a generic method with ContextBase as a generic type argument, so any initialisers for ContextBase also run. To prevent creating ContextBase objects (if it should never be instantiated directly, if the derived context should always be used), you can mark the class as abstract.
Your ContextBase seems to have an initializer as well.. You can remove this by
Database.SetInitializer<ContextBase>(null);

Categories