I use DbContext from custom assembly. LINQPad fails to run any query with this error message:
'project.model.Context' is inaccessible due to its protection level
Inconsistent accessibility: base class 'project.model.Context' is less accessible than class 'UserQuery'
Context class was marked as internal, but making it public didn't solved the issue.
public partial class Context : DbContext
{
public Context()
: base("name=Context")
{
}
// all constructors are public
Found this, but it didn't helped either.
Turned out I was referencing wrong DLL file.
Switching TypeAccess in EDMX to Public (or just editing generated files) had helped.
Related
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 saw a post today about implementing SqlAzureExecutionStrategy:
http://romiller.com/tag/sqlazureexecutionstrategy/
However, all examples I can find of this use a Configuration that inherits from DbConfiguration. My project is using EF6 Code First Migrations, and the Configuration it created inherits from DbMigrationsConfiguration. This class doesn't contain a definition for SetExecutionStrategy, and I can find no examples that actually combine SqlAzureExecutionStrategy (or any SetExecutionStrategy) with DbMigrationsConfiguration.
Can this be done?
If anyone else comes across this question, this is what we figured out:
Create a custom class that inherits from DbConfiguration (which has SetExecutionStrategy):
public class DataContextConfiguration : DbConfiguration
{
public DataContextConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
}
}
Then add this attribute to your DataContext, specifying that it is to use your custom class:
[DbConfigurationType(typeof(DataContextConfiguration))]
public class DataContext : DbContext, IDataContext
{
...
}
After more investigation, now I think the correct answer is that:
DbMigrationsConfiguration is completely separate and only configures the migration settings. That's why it doesn't inherit from or have the same options as DbConfiguration.
It is not loaded, and is irrelevant, for actual operation.
So you can (and should) declare a separate class based on DbConfiguration to configure the runtime behaviour.
I added some tracing and I saw that the first time you use a DatabaseContext in an application, it runs up the migration, and the migration configuration.
But, the first time the DatabaseContext is actually used (e.g. to load some data from the database) it will load your DbConfiguration class as well.
So I don't think there is any problem at all.
I have existing .net 4 application which is based on object context.
Now I'm adding DbContext in to existing application by inheriting DbContext and call constructor method and pass the existing object context. i.e.
public class DemoModelEntitiesDbContext : DbContext
{
public DemoModelEntitiesDbContext():base(new DemoModelEntities(), dbContextOwnsObjectContext:true)
{
}
public DbSet<ELMAH_Error> ELMAH_Error { get; set; }
}
Than When I call,
using (DemoModelEntitiesDbContext context = new DemoModelEntitiesDbContext())
{
foreach (ELMAH_Error entity in context.ELMAH_Error.ToList())
{
Console.WriteLine(entity.ID);
}
}
I am getting following error message,
"The type 'ObjectContextDemo.ELMAH_Error' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject."
I checked my existing entities are inheriting from EntityObject.
How to add DbContext in to existing application and not changing existing code?
You could cast to object context but then you gain nothing from DbContext if you 100% stick with existing code. but Ive done that with POCOs, not EntityObjects and can't confirm that it would work with entityobjects.
Otherwise it's not possible to do that without changes. DbContext does not support EntityObject. DbContext is designed for POCOs. Plus there are other code differences between DbContext and ObjectContext. You'd have to change even if you were already using POCOs. If you are using the EF designer, you'd have to start by using a different code gen template (ef5 DbContext template). But that will result in very different classes and plenty of changes to your EF calls in your app.
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.
How can you make EF migrations work without hardcoding a nameorconnectionstring in the base of the required parameterless constructor?
EF migrations is forcing you to add a default constructor to the custom dbcontext. In the base of the dbcontext you have to provide a nameorconnectionstring.
public class CustomDbContext : DbContextBase
{
public CustomDbContext ()
: base("theconnectionstringwedontwanttoset") //hardcoded connection string
{
}
The fact that we need to hardcode the constructor is something we cant work with since in our client applications we work without a config file and we dynamically build up the connection since we are connecting to many different databases (server, local sql compact).
After exploring the DbMigrationsConfiguration class I found a DbConnectionsInfo property named TargetDatabase which can be set in the constructor. But even this solution did not work. This is what I did:
public sealed class Configuration : DbMigrationsConfiguration<CustomDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
//Setting the TargetDatabase in the hope it will use it in the migration
TargetDatabase = new DbConnectionInfo("Server=xxx;Database=xxx;Trusted_Connection=True", "System.Data.SqlClient");
}
public class MigrationInitializer : MigrateDatabaseToLatestVersion<CustomDbContext, Configuration>
{
}
I can see that eventually the Activator.CreateInstance is used within DbMigrator and I expected from this class to use the TargetDatabase.Create...or something.
Any help or feedback is welcome.
Thanks in advance,
Wilko
it is possible yes See answers here
I also included a test program in this question.