In my app i have two DataContexts, ApplicationDbContext created by default ,which inherits from IdentityDbContext and DomainContext for my model. Now i am getting some issues when updating the database using Migrations, when i execute
Add-Migration to DomainContext in the migration file created is included the code to Drop all the tables related to ApplicationDbContext. I have googled and i have not found any satisfactory answer yet, the only rasonable solution suggest to mingle both DBContexts but that sound weird to me because ApplicationDbContext inherit from IdentityDbContext.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base( "DBContextConnString" )
{
}
}
public class DomainContext:DbContext {
public DomainContext() : base( "DBContextConnString" ) { }
public DbSet<State> States { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder ) {
modelBuilder.HasDefaultSchema( "Workflow" );
base.OnModelCreating( modelBuilder );
}
}
One of the The new fetures exposed by EF6 state "Multiple Contexts per Database removes the previous limitation of one Code First model per database when using Migrations or when Code First automatically created the database for you." (Read it here). But in practice is a pain to implement this or maybe i am missing something very very obvious.
My question in short, what is the way to go to keep this two different DBContexts living in armony?
After some experimentation i conclude that the more pragmatic way is to mingle both DbContext into one, so i decide to move my domain entities to IdentityDbContext.
Related
I am working on a .net core project where the requirement is to maintain an SQLite DB and an SQL Server DB simultaneously. I created two DbContext files SqlServerContext and SqliteContext and separate migration folders for them. These files are derived from a WorkerContext file that's derived from DbContext. The migration is working properly, as tables are created in both databases. But I could not make simultaneous data operation work.
This is the IKeyboardMouseActivityRepository. There are separate parts for using SqliteContext and SqlServerContext. I have to comment out one part when using the other. So I can do data entry in one DB at a time now.
public interface IKeyboardMouseActivityRepository :
IRepository<KeyboardMouseActivity, Guid, SqlServerContext>
// IRepository<KeyboardMouseActivity, Guid, SqliteContext>
{
}
public class KeyboardMouseActivityRepository :
IKeyboardMouseActivityRepository,
Repository<KeyboardMouseActivity, Guid, SqlServerContext>
// Repository<KeyboardMouseActivity, Guid, SqliteContext>
{
public KeyboardMouseActivityRepository(SqlServerContext dbContext)
: base(dbContext)
{
}
// public KeyboardMouseActivityRepository(SqliteContext dbContext)
// : base(dbContext)
// {
// }
}
This is the main Repository class.
public abstract class Repository<TEntity, TKey, TContext>
: IRepository<TEntity, TKey, TContext>
where TEntity : class, IEntity<TKey>
where TContext : DbContext
{
protected TContext _dbContext;
protected DbSet<TEntity> _dbSet;
public Repository(TContext context)
{
_dbContext = context;
_dbSet = _dbContext.Set<TEntity>();
}
// other methods such as Add, Remove etc.
}
My understanding is that since the context parameter is specified in KeyboardMouseActivityRepository, it only works for that specified context. How can I modify it so it works for both DbContext files and I can do data operation in both DB at the same time?
The repository you have defined is typed per-DbContext. If you want to have a repository that can update two known DbContext implementations then you can back off the Generic approach for the DbContexts and implement the repository to accept one of each in the constructor:
public abstract class Repository<TEntity, TKey>
: IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
protected SqlAppDbContext _sqlContext;
protected SqlLiteAppDbContext _sqlLiteContext;
protected DbSet<TEntity> _sqlDbSet;
protected DbSet<TEntity> _sqlLiteDbSet;
public Repository(SqlAppDbContext sqlContext, SqlLiteAppDbContext sqlLiteContext)
{
_sqlContext = sqlContext ?? throw new ArgumentNullException("sqlContext");
_sqlLiteContext = sqlLiteContext ?? throw new ArgumentNullException("sqlLiteContext");
_sqlDbSet = _sqlContext.Set<TEntity>();
_sqlLiteDbSet = _sqlLiteContext.Set<TEntity>();
}
// other methods such as Add, Remove etc.
}
Note that you will want to investigate and implement something like TransactionScope to help ensure that operations done via the repository are mutually committed or rolled back. For instance if you have code that attempts to update data in both DbSets and SaveChanges, if one succeeds and the other fails for any reason, usually the expectation would be they both roll back. Reads I expect would prioritize one DbSet over the other, but expect if you were to want to support something like a fail-over or situational load from one server or the other you will run into issues if it is at all possible that entities fetched from one DbContext are ever married up with entities fetched from the other. (entities loaded by _sqlContext cannot be associated with entities loaded by _sqlLiteContext) When updating entities and associating them via navigation properties you will be loading everything twice or playing a very dangerously error prone game of detaching and reattaching entities betewen DbContexts.
I would advise against using a Generic Repository pattern /w EF. This will paint you into various corners that will limit many of the capabilities that EF can provide for optimizing queries, working with projections, and performing operations like pagination, filtering, sorting, etc. efficiently without a lot of extra code or introducing pretty complex code into the repository.
Overall I wish you luck with the project, however a requirement and design like this will be a nest of hungry dragons for your time and sanity. :)
I am trying to add a UNIQUE constraint to the "Username" property of my "UserAccount" entity/class. With code-first, that would be no problem, but for model-first, I can't find anything on how to achieve that.
The designer does not support this feature. I cannot use annotations because the entity classes are auto-generated. I cannot use Fluent API because the OnModelCreating() method is not called in model-first and thus I have no DbModelBuilder instance.
The only thing I can think of is executing some kind of manual SQL statement at application start that creates the UNIQUE constraint, which kind of defeats the purpose of EF.
Here is my current DbContext class:
public partial class UserAccountsModelContainer : DbContext
{
public UserAccountsModelContainer()
: base("name=UserAccountsModelContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<UserAccount> UserAccounts { get; set; }
}
I won't even bother to post the UserAccount class since it's auto-generated and shouldn't be modified (I know that the DbContext is also auto-generated, but modifying it is possible).
Any help on this is appreciated!
First I will recommend you to switch to Entity Framework Code First, too. It gives you much more controll about every thing that is possible with EF.
I never used it before, but I know Model Conventions. They are applicable to the model configuration. Maybe it will be an approach to set up a convention for a defined model type/property that should be configured as unique constraint.
Based on the following it should be possible to modify the set up of model first on creating database.
Model Conventions are based on the underlying model metadata. There
are conventions for both CSDL and SSDL. Create a class that implements
IConceptualModelConvention from CSDL conventions and implement
IStoreModelConvention for SSDL convention.
Source: http://www.entityframeworktutorial.net/entityframework6/custom-conventions-codefirst.aspx
There are two types of model conventions, Conceptual (C-Space) and
Store (S-Space). This distinction indicates where in the pipeline a
convention is executed. A C-Space convention is applied to the model
that the application builds, whereas an S-Space convention is applied
to the version of the model.
Source: https://entityframework.codeplex.com/wikipage?title=Custom%20Conventions
Some more example implementations incl. explainations are findabe on msdn. I guess they are very helpful for your case.
One example from MSDN:
public class DiscriminatorRenamingConvention : IStoreModelConvention<EdmProperty>
{
public void Apply(EdmProperty property, DbModel model)
{
if (property.Name == "Discriminator")
{
property.Name = "EntityType";
}
}
}
It will rename the column Discriminator into EntityType. It is a very simple example but you could modify it to solve your problem to:
public class ModelBasedConvention : IConceptualModelConvention<EdmProperty>
{
public void Apply(EdmProperty property, DbModel model)
{
if (property.Name == "Username"
&& property.DeclaringType.GetType() == typeof(UserAccount))
{
property.AddAnnotation("UniqueKey", property);
}
}
}
I am building web APIs in ASP.NET Core 1.1.
I have a number different databases (for different systems) which have common base schemas for configuration items such as Configuration, Users and groups (about 25 tables in all). I am trying to avoid duplicating the quite extensive EF configuration for the shared part of the model by inheriting from a base class as shown in the diagram.
However, this does not work because of the Entity Framework (EF) requirement to pass DbContextOptions<DerivedRepository> as a parameter to the constructor, where DerivedRepository must match the type of the repository the constructor is called on. The parameter must then be passed down to the base DbContext by calling :base(param).
So when (for example) InvestContext is initialised with DbContextOptions<InvestContext>, it calls base(DbContextOptions<InvestContext>) and EF throws an error because the call to the ConfigurationContext constructor is receiving a parameter of type DbContextOptions<InvestContext> instead of the required type DbContextOptions<ConfigurationContext>. Since the options field on DbContext is defined as
private readonly DbContextOptions _options;
I can't see a way around this.
What is the best way to define the shared model once and use it multiple times? I guess I could create a helper function and call it from every derived context, but it's not nearly as clean or transparent as inheritance.
I would like to bring this post from the OP's GitHub issue to everyone's attention:
I was able to resolve this without a hack by providing a protected constructor that uses DbContextOptions without any type. Making the second constructor protected ensures that it will not get used by DI.
public class MainDbContext : DbContext {
public MainDbContext(DbContextOptions<MainDbContext> options)
: base(options) {
}
protected MainDbContext(DbContextOptions options)
: base(options) {
}
}
public class SubDbContext : MainDbContext {
public SubDbContext (DbContextOptions<SubDbContext> options)
: base(options) {
}
}
OK, I have got this working in a way which still uses the inheritance hierarchy, like this (using InvestContext from above as the example):
As stated, the InvestContext class receives a constructor parameter of type DbContextOptions<InvestContext>, but must pass DbContextOptions<ConfigurationContext> to it's base.
I have written a method which digs the connectionstring out of a DbContextOptions variable, and builds a DbContextOptions instance of the required type. InvestContext uses this method to convert its options parameter to the right type before calling base().
The conversion method looks like this:
protected static DbContextOptions<T> ChangeOptionsType<T>(DbContextOptions options) where T:DbContext
{
var sqlExt = options.Extensions.FirstOrDefault(e => e is SqlServerOptionsExtension);
if (sqlExt == null)
throw (new Exception("Failed to retrieve SQL connection string for base Context"));
return new DbContextOptionsBuilder<T>()
.UseSqlServer(((SqlServerOptionsExtension)sqlExt).ConnectionString)
.Options;
}
and the InvestContext constructor call changes from this:
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public InvestContext(DbContextOptions<InvestContext> options):base(ChangeOptionsType<ConfigurationContext>(options))
So far both InvestContext and ConfigurationContext work for simple queries, but it seems like a bit of a hack and possibly not something the designers of EF7 had in mind.
I am still concerned that EF is going to get itself in a knot when I try complex queries, updates etc. It appears that this is not a problem, see below)
Edit: I've logged this problem as an issue with the EF7 team here, and a team member has suggested a change to the EF Core core as follows:
"We should update the check to allow TContext to be a type that is derived from the current context type"
This would solve the problem.
After further interaction with that team member (which you can see on the issue) and some digging through the EF Core code, the approach I've outlined above looks safe and the best approach until the suggested change is implemented.
Depending on your requirements you can simply use the non type specific version of DbContextOptions.
Change these:
public ConfigurationContext(DbContextOptions<ConfigurationContext> options):base(options)
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public ConfigurationContext(DbContextOptions options):base(options)
public InvestContext(DbContextOptions options):base(options)
Then if you create your ConfigurationContext first, the classes that inherit it seem to get the same configuration. It may also depend on the order in which you initialize the different contexts.
Edit:
My working example:
public class QueryContext : DbContext
{
public QueryContext(DbContextOptions options): base(options)
{
}
}
public class CommandContext : QueryContext
{
public CommandContext(DbContextOptions options): base(options)
{
}
}
And in Startup.cs
services.AddDbContext<CommandContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<QueryContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
alternatively, in a test class:
var connectionString = "Data Source=MyDatabase;Initial Catalog=MyData;Integrated Security=SSPI;";
var serviceProvider = new ServiceCollection()
.AddDbContext<QueryContext>(options => options.UseSqlServer(connectionString))
.BuildServiceProvider();
_db = serviceProvider.GetService<QueryContext>();
I'm looking for information about using entity framework with an existing database, but to keep my poco classes in another library.
I've done this a number of times in the past, but I've always ended up with my model classes in my data access library using EF and my domain classes in a separate library. Inevitably this meant writing code to translate between my domain classes and my model classes. This seems pointless and inefficient since the classes are usually almost identical.
Can anyone point me to a walkthrough keeping my classes are materialized by EF in a separate library? I would need to be able to do some minor name correction (eg Filter_Rule --> FilterRule). I would also like to be able to keep anything EF specific in the data access library so that I can swap out the data access library if I need to.
Thanks,
Jason
This should be quite straightforward. Create a DbContext code-first style as normal, adding DbSets and configurations as necessary to tell EF about your database. Set your initializer to null so it doesn't try to mess with your existing database, and voila...
public class YourContext : DbContext
{
public DbSet<YourPoco> YourPocos { get; set; }
static YourContext()
{
Database.SetInitializer<YourContext>(null);
}
public YourContext() : base("database_name")
{
}
protected override void OnModelCreating(DbModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<YourPoco>().Property(x => x.FilterRule).HasColumnName("Filter_Rule");
//OR
builder.Configurations.Add(new YourPocoConfig());
//OR
builder.Configurations.AddFromAssembly(typeof (YourContext).Assembly);
}
}
public class YourPocoConfig : EntityTypeConfiguration<YourPoco>
{
public YourPocoConfig()
{
HasKey(x => x.Id);
Property(x => x.FilterRule).HasColumnName("Filter_Rule");
}
}
If you are worried about getting everything to match your database structure, you can use Entity Framework Tools for Visual Studio to reverse engineer your models, then match the configuraiton or copy the generated POCO's into your other library and convert the data annotations into respective EntityTypeConfiguration classes to keep the POCO's clean.
MSDN document on reverse engineering code-first.
For example, I have following DbContext classes.
public class AppDbContext : DbContext {
...
}
public class LogDbContext : DbContext {
...
}
public class FooDbContext : DbContext {
...
}
If a connection string named AppDbContext is on the App.Config and I want other DbContext classes to share the same connection string as AppDbContext, could I just pass the string "AppDbContext" as the parameter for the ctor of LogDbContext and FooDbContext. For example,
public class FooDbContext : DbContext {
public FooDbContext : base("AppDbContext") { }
}
Does it have any side effects ?
Update 2013/1/9
After trying #ShinH2S's suggestion and somethings, I give up this way, and decide give different Dbcontext derived classes with different connectionStrings and database. I have try a test project and put it on GitHub. It will throw a runtime exception when the entityframework detects the database scheme is changed because the AppDbContext and FooDbContext have different schemas. If I assign a DropCreateDatabaseIfModelChanges strategy to both DbContext derived classes, one of them will be dropped because the models is different to another.
Update 2017/10
This is an old problem. In my memory, EF6 and above versions can have different migration history for multiple context in the same migration table.
I prefer this answer at SO. I had not been coding with C# about 2 years.
IMHO, there is no side effects. But if it was me I will just create a base class that inherits from DbContext class BaseDbContextthen all the contexts (AppDbContext, LogDbContext and FooDbContext ) will derive from BaseDbContext.