The IDbCommandInterceptor interface is not very well documented. And I've only found a few scarce tutorials on it:
http://www.entityframeworktutorial.net/entityframework6/database-command-interception.aspx
https://msdn.microsoft.com/en-us/data/jj556606%28v=vs.113%29.aspx
https://entityframework.codeplex.com/wikipage?title=Interception
https://www.tutorialspoint.com/entity_framework/entity_framework_command_interception.htm
https://msdn.microsoft.com/en-us/data/dn469464%28v=vs.113%29.aspx
And a few SO questions:
Entity Framework 6 - Timing queries
Getting DbContext from implementation of IDbCommandInterceptor
These are the suggestions on hooking I've found:
1 - The static DbInterception class:
DbInterception.Add(new MyCommandInterceptor());
2 - Doing the above suggestion in a DbConfiguration class
public class MyDBConfiguration : DbConfiguration {
public MyDBConfiguration() {
DbInterception.Add(new MyCommandInterceptor());
}
}
3 - Using the config file:
<entityFramework>
<interceptors>
<interceptor type="EFInterceptDemo.MyCommandInterceptor, EFInterceptDemo"/>
</interceptors>
</entityFramework>
Although I couldn't figure out how to hook the DbConfiguration class to the DbContext, and neither what to put in the type part of the config method. Another example I found seemed to suggest that you write the namespace of a logger:
type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework"
I noted that DataBaseLogger implements IDisposable, IDbConfigurationInterceptor and
IDbInterceptor. IDbCommandInterceptor also implements IDbInterceptor, so I tried (without success) to format it like this:
type="DataLayer.Logging.MyCommandInterceptor, DataLayer"
And when I called the static DbInterception class directly, it added another interceptor every call. So my quick and dirty solution was to utilize static constructors:
//This partial class is a seperate file from the Entity Framework auto-generated class,
//to allow dynamic connection strings
public partial class MyDbContext // : DbContext
{
public Guid RequestGUID { get; private set; }
public MyDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
DbContextListeningInitializer.EnsureListenersAdded();
RequestGUID = Guid.NewGuid();
//Database.Log = m => System.Diagnostics.Debug.Write(m);
}
private static class DbContextListeningInitializer
{
static DbContextListeningInitializer() //Threadsafe
{
DbInterception.Add(new MyCommandInterceptor());
}
//When this method is called, the static ctor is called the first time only
internal static void EnsureListenersAdded() { }
}
}
But what are the proper/intended ways to do it?
I figured out that my DbContext class just needed to have the DbConfigurationType attribute, to attach a configuration at runtime:
[DbConfigurationType(typeof(MyDBConfiguration))]
public partial class MyDbContext // : DbContext
{
public MyDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
{ }
}
public class MyDBConfiguration : DbConfiguration {
public MyDBConfiguration() {
this.AddInterceptor(new MyCommandInterceptor());
}
}
The docs suggests that you can just put it in Application_Start:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
DbInterception.Add(new SchoolInterceptorTransientErrors());
DbInterception.Add(new SchoolInterceptorLogging());
}
The important part is that it only get's called once.
Related
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.
I have my controller like this
public class MyController : Controller
{
private IEntityRepository accountsRepo;
private IEntityRepository dataRepo;
public MyController(IEntityRepository accs, IEntityRepository data)
{
accountsRepo = accs;
dataRepo = data;
}
.....
}
And I installed container this way:
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IEntityRepository>()
.ImplementedBy<AccountsRepository>()
.Named("accs")
.LifestyleTransient(),
Component.For<IEntityRepository>()
.ImplementedBy<DataRepository>()
.Named("data")
.LifestyleTransient());
}
}
Also I have facilities setted up:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
Kernel.Register(
Component.For<DbContext>()
.ImplementedBy<AccountsContext>()
.LifestylePerWebRequest(),
Component.For<DbContext>()
.ImplementedBy<DataContext>()
.LifestylePerWebRequest());
}
}
}
...and installed:
public class PersistenceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<PersistenceFacility>();
}
}
So when I'm using my controller both parameters are injected with AccountsRepository instance (which was registered first). Of course I wanna see "data" being DataRepository respectively. Please, explain me proper way to deal with this kind of injection.
EDIT
As #roman suggested I have implemented generic repositories:
public interface IRepository : IDisposable
{
void SaveChanges();
void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams);
}
public interface IEntityRepository<T> : IRepository
{
T Context { get; set; }
DbSet<TEntity> Set<TEntity>() where TEntity : class;
}
public class AccountsRepository : IEntityRepository<AccountsContext>
{
public AccountsContext Context { get; set; }
public AccountsRepository(AccountsContext c)
{
Context = c;
}
public DbSet<TEntity> Set<TEntity>() where TEntity : class
{
return Context.Set<TEntity>();
}
public virtual void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams)
{
Context.Database.ExecuteSqlCommand(procedureCommand, sqlParams);
}
public virtual void SaveChanges()
{
Context.SaveChanges();
}
public void Dispose()
{
if (Context != null)
Context.Dispose();
}
}
DataRepository looks the same way, my be at some point I will decide to have just one concrete class EntityRepository, but it not relevant to exceptions I receiving.
So after cosmetic interfaces changes my contreller become:
public class HomeController : Controller
{
private IEntityRepository<AccountsContext> accountsRepo;
private IEntityRepository<DataContext> dataRepo;
public HomeController(IEntityRepository<AccountsContext> accs, IEntityRepository<DataContext> data)
{
accountsRepo = accs;
dataRepo = data;
}
....
}
Also I have changed installer code:
container.Register(
Component.For<IEntityRepository<AccountsContext>>()
.ImplementedBy<AccountsRepository>()
.LifestyleTransient(),
Component.For<IEntityRepository<DataContext>>()
.ImplementedBy<DataRepository>()
.LifestyleTransient());
And now during controller resolving proccess
return (IController) kernel.Resolve(controllerType);
I catching
Can't create component 'MyMVCProj.DAL.AccountsRepository' as it has dependencies to be satisfied.
'MyMVCProj.DAL.AccountsRepository' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.
Castle.MicroKernel.Handlers.HandlerException: Can't create component 'MyMVCProj.DAL.AccountsRepository' as it has dependencies to be satisfied.
'MyMVCProj.DAL.AccountsRepository' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.
But I have installed AccountsContext in facility logic.
EDIT++
According to #Roman suggestion I have tweaked my facility this way:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
Kernel.Register(
Component.For<DbContext>()
.ImplementedBy<AccountsContext>()
.Named("accctx")
.LifestylePerWebRequest(),
Component.For<DbContext>()
.ImplementedBy<DataContext>()
.Named("datactx")
.LifestylePerWebRequest());
}
}
and also repositories installler:
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IEntityRepository<AccountsContext>>()
.ImplementedBy<AccountsRepository>()
.Named("accs")
.LifestyleTransient()
.DependsOn(Dependency.OnComponent(typeof (DbContext), "accctx")),
Component.For<IEntityRepository<DataContext>>()
.ImplementedBy<DataRepository>()
.Named("data")
.LifestyleTransient()
.DependsOn(Dependency.OnComponent(typeof (DbContext), "datactx")));
}
}
This is the exception I get now:
Can't create component 'accs' as it has dependencies to be satisfied.
'accs' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.
But trying to solve this brute forcing the code I ended with working solution, just installing concrete implementations of DBContext:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
Kernel.Register(
Component.For<AccountsContext>().LifestylePerWebRequest(),
Component.For<DataContext>().LifestylePerWebRequest());
}
}
And kernel's components now are:
AccountsContext PerWebRequest
AccountsRepository / IEntityRepository<AccountsContext> Transient
DataContext PerWebRequest
DataRepository / IEntityRepository<DataContext> Transient
And before they were:
AccountsContext / DbContext PerWebRequest
AccountsRepository / IEntityRepository<AccountsContext> Transient
DataContext / DbContext PerWebRequest
DataRepository / IEntityRepository<DataContext> Transient
So the new questions are:
Have I did all stuff idiomatically?
Why this behaviour - there already was AccountContext with little mention of it dependencies.
The fact that you expect two instances of same interface, yet you require different behavior for them (by injecting them to two different parameters), implies - in my opinion - that they shouldn't be the same interface, because they have different roles, or responsibilities. It would make sense to me more, if IEntityRepository was a generic class and then you would require in MyController two different generic interface types:
public class MyController(IEntityRepository<Account> acc, IEntityRepository<Data> data)
Nevertheless, If you still want to do that kind of thing, I suggest you use a CollectionResolver that will allow MyController class to get an IEnumerable. That way you'll get both instances, but it'll be up to you to select the appropriate one to use depending on your needs, which I'll stress again, I think is the wrong approach for this.
To use CollectionResolver you need to register it with the Windsor container like this:
var container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
And then, MyController will look like this:
public class MyController(IEnumerable<IEntityRepository> repositories)
{
accountsRepo = repositories.Where(...);
dataRepo = repositories.Where(...);
}
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.
I'm trying to use the interface IPluralizationService to customize the pluralization of my entities without success!
Necessary that all entities are pluralized using the Inflector library.
Attempts
class Config : DbConfiguration
{
public Config()
{
SetPluralizationService(new CustomPluralization());
}
}
class CustomPluralization : IPluralizationService
{
public string Pluralize(string word)
{
return word.Pluralize();
}
public string Singularize(string word)
{
return word.Singularize();
}
}
In my context;
modelBuilder.Configurations.Add<Config>(.. ?? ..)
According to msdn's article Code-Based Configuration (EF6 onwards) section Using DbConfiguration, it is sufficient to simply place your DbConfiguration class in the same assembly as your DbContext class.
Nevertheless you can specify it manually, as explained in the article by using either the config file or an annotation in your DbContext.
Config file:
<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly">
<!-- Your EF config -->
</entityFramework>
Annotation:
[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")]
public class MyContextContext : DbContext
{
}
Or
[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContextContext : DbContext
{
}
Note:
These examples are directly from the article I linked
I'm pretty new to ASP.NET WebApi project, but hopefully I'll put everything straight enough. After creating couple CRUD Controllers a brilliant idea come to my mind - write generic base CRUD-web-API controller for all of them and do not mess with rewriting same code.
After successful implementation of such class I faced problem with dependency resolving which is still working fine for non-generic/-inherited controllers.
Simple request (GET, POST, etc.) gives:
Type 'UsersController' does not have a default constructor","ExceptionType":"System.ArgumentException"
Default constructor without injections works fine. Obviously I have a problem with Ninject configuration.
public abstract class BaseCRUDController<T> : ApiController where T : class, IClientEntity
{
private readonly Repository<T> _repo;
private readonly IDbContextDataProvider _context;
// With this ctor everything works well
public BaseCRUDController()
{
this._context = new ModelContext();
this._repo = new Repository<T>(this._context);
}
// Injection is not working ((
public BaseCRUDController(IDbContextDataProvider context)
{
this._context = context;
this._repo = new Repository<T>(context);
}
And concrete Controller for User entity:
public class UsersController : BaseCRUDController<User>
{
UsersController(IDbContextDataProvider context) : base(context) { }
UsersController() : base() { }
}
And Ninject config itself:
public class DataProviderModule : NinjectModule
{
public override void Load()
{
this.Bind<IDbContextDataProvider>().To<ModelContext>().InSingletonScope();
}
}
public class NinjectResolver
{
// Instantinate Ninject dependencies resolver
public static System.Web.Http.Dependencies.IDependencyResolver GetConfiguredDependencyResolver()
{
IKernel kernel = new StandardKernel(new DataProviderModule());
System.Web.Http.Dependencies.IDependencyResolver njResolver = new NinjectResolver(kernel);
return njResolver;
}
}
And Application_Start
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
var config = GlobalConfiguration.Configuration;
config.DependencyResolver = NinjectResolver.GetConfiguredDependencyResolver();
WebApiConfig.Register(config);
What am I doing wrong here?
NOTE: This approach works well if I have:
public class UsersController : ApiController
{
UsersController(IDbContextDataProvider context)
{
....
}
...
Oh.. I've spent hours trying different approaches. It was madness. And the funny part here is that Ninject is working well and code is correct except one accessibility modifier. Adding public modifier to UsersController ctor fixed the issue.
public class UsersController : BaseCRUDController<User>
{
public UsersController(IDbContextDataProvider context) : base(context) { }
...
PS. Write your code carefully...