1

I am using EF Core 3.1 and have several custom services provided via dependency injection, e.g., an ILogger. How can these services be resolved from within a model, such as when I want to log something from the model?

Adding the dependency to the constructor (like in other classes) means that an empty constructor would not work... which is what is used in all the EF Core examples I can find:

new Author{ FirstName = "William", LastName = "Shakespeare" };

... which suggests DI in a model constructor would be an anti-pattern.

But since models do not inherit, I'm not sure how to get my hands on the other services at runtime (other than a hack like saving a static reference to IServiceProvider from the Startup class).

One might argue that models should not contain business logic, and therefore do not need access to services. However, the problem remains — there are times I need to do things with a model that are not triggered by a web request / controller (and therefore, I have no access to the DI services).


Edit: the scenario is actually more complex than a simple logger.

A simplified example of where else I need services looks like this (assuming a I18n service):

public string Name => GetService<I18n>().Localize(this.translationKey);

The model in this case might be rendered into JSON for the client.

Trying to put this localization code in the Controller or other service seems like a road to copy-pasta spaghetti code. The Name property is required in many different cases, not just a single controller path. For example, it might be concatenated into another localized string elsewhere. Being forced to do the name localization explicitly from outside the model would be extremely repetitive and burdensome, especially once this example was scaled out to the scope of my app.

I could come up with many more such examples unrelated to logging or I18n.

Another example: how would I know the DbContext from which the Model was loaded? An app with two databases like mine can easily lose track of this lineage. It seems right now the controller which created the model needs to track this explicitly, whereas if models supported DI they would be able to capture the DbContext in the constructor.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Zane Claes
  • 14,732
  • 15
  • 74
  • 131
  • Where are you going to call these injected methods? It sounds like you wan to call them from inside your model, but where will that model be instantiated? Can't you just inject your service to the caller and do your logging there? I assume you don't want to do this for some reason, but could you expand on why that is the case? – Bassie May 12 '20 at 23:29
  • 1
    a model really shouldn't do that. you should project out further – Daniel A. White May 13 '20 at 00:06
  • I think this will depend on how your application is structured. For instance, say you are using DDD pattern with MediatR - then why not wrap each request in a pipeline behaviour, where that pipeline behaviour has the required services injected? this way your model stays clean, and so does your command/query, but you can inject whatever you need in the middleware. Otherwise, you will need to show us what exactly you are trying to do, as accessing the "injected service" from the model will depend on how your project actually looks – Bassie May 13 '20 at 00:07
  • @Bassie Thanks for the thoughts. I've added an edit with 2 more examples in attempts to clarify. Daniel, I addressed your comment already in my OP ("one might argue that...") – Zane Claes May 13 '20 at 00:15
  • 1
    @ZaneClaes Did you see this question: https://stackoverflow.com/questions/51082764/dependency-injection-into-entity-class ? It looks like it is possible, but not recommended. There is probably a right place/way to do it, depending on how your project looks – Bassie May 13 '20 at 00:17
  • Perfect, thank you @Bassie — my searches missed that. The docs linked in that post mention that the "anti-pattern" is because it couples models to EF. In my case, that's fine. – Zane Claes May 13 '20 at 00:30

1 Answers1

1

As per this question and discussion in comments, this is possible, but not recommended.

From MSDN:

Injecting the DbContext like this is often considered an anti-pattern since it couples your entity types directly to EF Core. Carefully consider all options before using service injection like this.

If this is not a problem for you, then we can inject to our entities like this:

private IInjectedService _service;

private Entity(IInjectedService service)
{
    EnsureArg.IsNotnull(service, nameof(service));

    _service = service;
}

This is generally frowned upon, and normally we would inject those services into the caller (e.g. QueryHandler or Service)

Bassie
  • 9,529
  • 8
  • 68
  • 159