1

I have an application that syncs data from a MySql database to a SQL Server database.

Considering those two DbContext services:

services.AddDbContext<SqlServerContext>(options => options
    .UseSqlServer(Configuration.GetConnectionString("SqlServer")));

services.AddDbContext<MySqlContext>(options => options
    .UseMySql(Configuration.GetConnectionString("MySql"))
    .AddInterceptors(new MySqlInterceptor()));

In the MySqlInterceptor(); I want to inject/resolve/use a Service or even the SqlServerContext, in order to get configurations to modify the CommandText.

Any ideas?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Idrees
  • 711
  • 1
  • 7
  • 31

2 Answers2

1

Depending on the method you are going to override you will receive CommandEventData object in the method definition which has the DbContext as property.

As to the services and configurations you can configure the interceptor before registration.
Instead of this:

services.AddDbContext<MySqlContext>(options => options
    .UseMySql(Configuration.GetConnectionString("MySql"))
    .AddInterceptors(new MySqlInterceptor()));

you can do

var interceptor = new MySqlInterceptor(service1, service2 ... etc);
services.AddDbContext<MySqlContext>(options => options
 .UseMySql(Configuration.GetConnectionString("MySql"))
 .AddInterceptors(interceptor))

How to resolve the interceptor instance:
If you need to auto-wire the dependencies of the interceptor you can do the following

services.AddTransient<Service1>();
services.AddTransient<Service2>();
services.AddTransient<MySqlInterceptor>();
// resolve the instalce of the interceptor
var serviceProvider = services.BuildServiceProvider();
var interceptor = serviceProvider.GetService<MySqlInterceptor>();
// configure mysql context and interceptor
services.AddDbContext<MySqlContext>(options => options
 .UseMySql(Configuration.GetConnectionString("MySql"))
 .AddInterceptors(interceptor))
vasil oreshenski
  • 2,788
  • 1
  • 14
  • 21
  • A pardon my lack of knowledge: Is there a way that the the instance of `MySqlInterceptor` also get service1 injected? Like `MySqlInterceptor(IService1)`? – Idrees Nov 15 '19 at 18:13
  • Throws a warning: `Warning ASP0000 Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.` – Idrees Nov 15 '19 at 23:15
  • 1
    This is a minor observation but worth mentioning: the first suggestion doesn't provide much, you can always `.AddInterceptors(MySqlInterceptor(service1)))` – Idrees Nov 15 '19 at 23:19
  • if you think about it, ConfigureService is for container setup really, so creating a concrete inside of it defeats the purpose. – Idrees Nov 16 '19 at 05:45
  • I have an idea but it is too late and have to get some sleep; if I get something useful I’ll maybe post in an answer, we’ll see. Thanks for taking the time looking into this. – Idrees Nov 16 '19 at 05:48
  • 1
    @Yes i agree. It is useful when you have complex dependency graph (for example Servcie1 depends on another services ... etc) so you don't have to build the graph by hand. Post your idea i am interested in your solution. – vasil oreshenski Nov 16 '19 at 08:41
0

As @vasil mentioned in his answer:

Depending on the method you are going to override, you will receive CommandEventData object in the method definition which has the DbContext as property.

In my case though, I wanted to resolve a service that used another DbContext, which proved to be cumbersome; so instead I ended up putting the settings I needed into appsettings.json, and used IConfiguration service to get the setting value and sent it to the Interceptor constructor:

services.AddDbContext<MySqlContext>(options => options
    .UseMySql(Configuration.GetConnectionString("MySql"))
    .AddInterceptors(new MySqlInterceptor(Configuration["SettingValue"])));

Note: If you landed on this answer and was looking for a way to resolve a service inside the ConfigureService method, without calling BuildServiceProvider which, like David Fowler says on a Github issue, you'd be:

building the container while trying to build it

and you'll end up having:

2 containers and one of them will never be disposed.

You can do what Nkosi suggests in his answer:

services.AddScoped<IService>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
                x.GetRequiredService<IAnotherOne>(), 
                ""));
Idrees
  • 711
  • 1
  • 7
  • 31