How to use an internal DbContext?
I wonder how can I hide the DbContext object so that other libraries of my project not directly access.
I put my DbContext as internal in the library, and apparently should work, however when I run the application, the following error appears:
The target context 'Context' is not constructible. Add a default constructor or Provide an Implementation of IDbContextFactory
Could someone help me?
My implementation of data layer is:
[DbConfigurationType(typeof (ConfigContext))]
internal class Context : DbContext
{
internal Context()
: base(ConfigDataBase.GetSqlServerString(ConfigZnfce.Instance.SqlServerInstance))
{
}
//More code below
}
public class ConfigContext: DbConfiguration
{
public ConfigContext()
{
SetDefaultConnectionFactory(new System.Data.Entity.Infrastructure.LocalDbConnectionFactory("v11.0"));
SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance);
SetDatabaseInitializer(new CreateDatabaseIfNotExists<Context>());
SetDatabaseInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
}
}
I want all the other libraries are required to go through a unit of work and repositories in order to do any operation with the database
[SOLVED]
I left the Context class as "internal" and set the constructor as "public" as in the code below:
[DbConfigurationType(typeof (ConfigContext))]
internal class Context : DbContext
{
public Context()
: base(ConfigDataBase.GetSqlServerString(ConfigZnfce.Instance.SqlServerInstance))
{
}
//More code below
}
The virtual DBSet<> properties on the DbContext class must be public (as you found out in your solution.
If you're using TT templates, you can make this happen by changing the DbSet(EntitySet entitySet) function. Notice that the term public replaces the original {0} parameter in the string format.
public string DbSet(EntitySet entitySet) {
return string.Format(
CultureInfo.InvariantCulture,
"public virtual DbSet<{0}> {1} {{ get; set; }}",
_typeMapper.GetTypeName(entitySet.ElementType),
_code.Escape(entitySet));
}
Now, you can have an internal DbContext, internal objects, and still have the public automatic properties so EF can do its data-binding.
Related
I use EF Core, inside my dbcontext class I have overridden the onModelCreating method to configure one property in a class to be auto-increment in SQL Server.
This is my DbContext class:
public class AppDbContext:IdentityDbContext<AppUser>
{
public AppDbContext(DbContextOptions options) : base(options)
{
}
public AppDbContext()
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Service>()
.Property(service => service.ID)
.UseIdentityColumn(1, 1);
}
public virtual DbSet<AppUser> AppUsers { get; set; }
public virtual DbSet<Service> Services { get; set; }
}
If you notice that my DbContext class is inheriting from IdentityDbContext because I use identity.
The problem: I get this error:
CS0121
The call is ambiguous between the following methods or properties: 'Microsoft.EntityFrameworkCore.OraclePropertyBuilderExtensions.UseIdentityColumn(Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder, int, int)' and 'Microsoft.EntityFrameworkCore.SqlServerPropertyBuilderExtensions.UseIdentityColumn(Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder, int, int)'
Error Screenshot 1
Error Screenshot 2
Additional info
I reference another project in my solution this project will play the repository role, and he prepared to work with SQL Server, Oracle and MySQL.
The project I depend on is my own project, and it is open source that is the repository link
Please any help to fix this issue?
I guess you cannot just remove the one of two problematic using.
Since those methods are extension methods (so static method) you could use them as static method :
Microsoft.EntityFrameworkCore.OraclePropertyBuilderExtensions.UseIdentityColumn(builder.Entity<Service>().Property(service => service.ID), 1, 1);
Or using static import :
using static Microsoft.EntityFrameworkCore.OraclePropertyBuilderExtensions;
Then
UseIdentityColumn(builder.Entity<Service>().Property(service => service.ID), 1, 1)
I know is not as beautiful as the fluent way of writing it but I don't think there is an alternative if you need all your using.
(I assumed you wanted to keep the Oracle one)
I'm creating a base C# library for ASP.NET MVC 5 projects for the company I work for, so when we have to share source code, we would include references to Enterprise.Classes library and the original dependencies are not required since this class would integrate all functionality.
For the DbContext, we have created a custom context that inherits from IdentityDbContext, but whenever it is inherited to create new contexts, the Microsoft.AspNet.Identity.EntityFramework Nuget package is required to be installed to work.
So far this is the code for the custom DbContext:
[DbConfigurationType(typeof(EnterpriseDbConfiguration))]
public class EnterpriseDbContext : IdentityDbContext<EnterpriseUser, EnterpriseRole, long, EnterpriseUserLogin,
EnterpriseUserRole, EnterpriseUserClaim>
{
public EnterpriseDbContext(string contextName = "DefaultEnterpriseConnection") : base(contextName)
{
/* Entities, Configurations and stuff */
}
}
And here is an example of the EnterpriseUser class (which would be the IdentityUser class):
public class EnterpriseUser : IdentityUser<long, EnterpriseUserLogin, EnterpriseUserRole, EnterpriseUserClaim>
{
public EnterpriseUser()
{
RegistrationDate = DateTime.Now;
Enabled = true;
}
/* Additional Properties */
}
Here is an example of an inheriting context, when EnterpriseDbContext is marked for inheritance it requires the Microsoft.AspNet.Identity.EntityFramework Nuget package to be installed:
public class InheritedEnterpriseContext : EnterpriseInfrastructure.Data.EnterpriseDbContext
{
}
How can I build the EnterpriseDbContext so when it is inherited it won't require the Nuget package to be installed?
The issue will be that by exposing a class that directly inherits, consumers of that class will need to know about all public shared members, requiring a dependency on the AspNet.Identity.EntityFramework library whether you intend to use them or not.
Edit: Corrections around IdentityDbContext and it's purpose...
IdentityDbContext is used for ASP.Net's authentication/authorization so your options are to either extend IdentityDbContext in which case you will need to reference ASPNet.Identity.EntityFramework anywhere your DbContext is referenced, or use separate contexts for your Identity and Application information.
You can define your EnterpriseUser/Role classes and construct an IdentityDbContext Instance to provide to your UserStore etc. for Auth. Then define your EnterpriseDbContext to extend DbContext. It too can define DbSets for EnterpriseUser/Role as you see fit. This DbContext can be shared/referenced by other code. Any MVC project that you want to use the IdentityDbContext will obviously need ASPNet.Identity.EntityFramework. Other libraries would use your EnterpriseDbContext /w just the normal EF reference.
public class EnterpriseDbContext : DbContext
{
public DbSet<EnterpriseUser> EnterpriseUsers { get; set; }
// .. Your data entities.
}
If EnterpriseUser extends IdentityUser you will likely still have a dependency to .Identity.EntityFramework. In this case you should define an alternative "User" entity for your EnterpriseDbContext that proxies for EnterpriseUser and use that in it's place for the application DbContext.
I.e.
// Can't use this in our EnterpriseDbContext because IdentityUser will require ref. to .Identity.EntityFramework
public class EnterpriseUser : IdentityUser<Guid>
{
// Stuff.
}
// Use this instead in our EnterpriseDbContext to map to the same table
[Table("EnterpriseUsers")]
public class User
{
public Guid Id { get; set; }
public string UserName { get; set; }
// Stuff...
}
Currently, my DbContext class has this code:
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
What I'd like to do, is inspect the subdomain and use that for the database name, so something like http://abc.mysite.com/ would use a connection string with database name abc.
But how do I manipulate the value of "DefaultConnection" in the constructor?
DBContext will take a name or a connection string in its constructor. That constructor is not usually exposed if you have a generated model.
You can use a partial class to expose that constructor:
public partial class DataEntities
{
public DataEntities(string connectionString) : base(connectionString)
{
}
}
I have done that before. My project was set up for DI with Castle Windsor and one of my IWindsorInstallers was DataAccessInstaller responsible for registering, among other classes like repositories, my database context and here is the relevant code:
container.Register(Component
.For<MyDatabaseContext>().Forward<DbContext>()
.ImplementedBy<MyDatabaseContext>()
.LifestylePerWebRequest()
.UsingFactoryMethod(context =>
{
return MyDatabaseContextFactory.Create(HttpContext.Current.Request.Url);
}));
You can have several connection strings set up in your web.config matching your domain.
My context factory implementation:
public static class MyDatabaseContextFactory
{
public static MyDatabaseContext Create(Uri uri)
{
return new MyDatabaseContext(uri.GetTopDomain());
}
}
If you just have a simple project and don't even have DI, you can still make use of a factory that finds out what the website use and instantiates a database context with the appropriate connection string.
Needless to say, your current database context constructor doesn't have to change.
I am working on a code first database, but when I try to do "Update-Database" I get the following error.
The target context 'AllMid.DL.Repository.Implementation.AllMidContext' is not constructible.
Add a default constructor or provide an implementation of IDbContextFactory.
Now it is apparent to me that the problem is that I don't have a default constructor or a an implementation of the IDbContextFactory interface, but in the sample project I am using I am seeing this done without either. Does anyone know how to go about this?
I currently have a DbContext resembling this.
internal class AllMidContext : DbContext, IAllMidContext
{
public DbSet<TreeEntity> Tree { get; set; }
public AllMidContext(IConfigurationAccess configAccess) : base(configAccess.GetDefaultConnectionString())
{
}
}
The configAccess should be being injected by structure map.
and a DataContextAccess class like this
internal class DataContextAccess : IDataContextAccess
{
private readonly IConfigurationAccess _configAccess;
public DataContextAccess(IConfigurationAccess configAccess)
{
_configAccess = configAccess;
}
public IAllMidContext GetAllMidContext()
{
return new AllMidContext(_configAccess);
}
}
Now the question is is there a way to do this without a default constructor or a factory? My dependency injection will always input the parameter so how can I get EF to use my custom constructor?
I believe that update-database is looking for a constructor with no input. what you can do is to write a constructor with no inputs and do your injection in its body.
I'm using multiple DbContext(Bounded DbContext) in my application. So I have following codes:
public class EfUnitOfWork<TContext> : IUnitOfWork where TContext : BaseDbContext, new()
{
...
}
//*******************************Module1**************************************
public interface IModule1UnitOfWork:IUnitOfWork
{
}
public class Module1EfUnitOfWork : EfUnitOfWork<Module1DbContext>,IModule1UnitOfWork
{
public Module1EfUnitOfWork ()
: base(new Module1DbContext())
{
}
}
public class Module1DbContext: BaseDbContext
{
static Module1DbContext()
{
Database.SetInitializer<Module1DbContext>(null);
}
public Module1DbContext()
: base("name=MyDatabase")
{
}
}
//*******************************Module2**************************************
public interface IModule2UnitOfWork:IUnitOfWork
{
}
public class Module2EfUnitOfWork : EfUnitOfWork<Module2DbContext>,IModule2UnitOfWork
{
public Module2DbContext()
: base(new PlanningDbContext())
{
}
}
public class Module2DbContext: BaseDbContext
{
static Module2DbContext()
{
Database.SetInitializer<Module2DbContext>(null);
}
public Module1DbContext()
: base("name=MyDatabase")
{
}
}
I'm using StructureMap 3 as my IoC container too and used following codes:
ObjectFactory.Configure(x =>x.For(typeof(IModule1UnitOfWork))
.Use(typeof(Module1EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton));
x.For(typeof(IUnitOfWork))
.Use(typeof(Module1EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton)));
ObjectFactory.Configure(x =>x.For(typeof(IModule2UnitOfWork))
.Use(typeof(Module2EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton));
x.For(typeof(IUnitOfWork))
.Use(typeof(Module2EfUnitOfWork))
.SetLifecycleTo((Lifecycles.Singleton)));
It works fine, but when I want to use these codes for Integration Test, I have one problem.
I want to use Sql Server CE for my Integration Test, so I have to pass my test's ConnectionString(that specify the Sql Server CE database file) to the module's DbContexts, I used my module's DbContexts as Generic Parameters in the EfUnitOfWork<> class and so it should has parameterless constructor, but as you see I used my module's DbContext parameterless constructor for specifying my Database:
public Module1DbContext()
: base("name=MyDatabase");
and couldn't pass anything to it.
So, how could I use my existing DbContexts for Integration Test?
"name=" only designate a connection string, so it is an indirect parameter read from an app.config file.
So imho, you should just use a different app.config for your tests.