Внедрить ApplicationUser в ASP.NET Core MVC

У меня есть класс, для которого требуется ApplicationUser (из ASP.NET Identity). Экземпляр должен быть текущим пользователем.

public class SomeClass
{
    public SomeClass(ApplicationUser user)
    {

В настоящее время я делаю инъекцию текущего пользователя из контроллера:

var currentUser = await _userManager.GetUserAsync(User);
var instance = new SomeClass(currentUser);

Теперь я хочу использовать инъекцию зависимостей, предоставленную Microsoft. Я не могу понять, как мне добавить ApplicationUser в сервисы. Для этого требуется User, который является свойством контроллера.

Так как же внедрить ApplicationUser (экземпляр текущего пользователя) через DI, предоставленный Microsoft?


comment
Как правило, вы должны внедрять зависимости, а не данные. Лучше предоставить желаемый контекст (вызывающего пользователя в вашем случае) при вызове каждого метода (или явно, вызвав некоторый Init(context)), чтобы потребители типа были более осведомлены о реальных потребностях. Автоматическое внедрение контекста за кулисами с помощью DI улучшит ваши навыки DI, но может привести к несоответствиям в будущем.   -  person haim770    schedule 10.04.2017
comment
@ haim770 спасибо. Я думаю, что я просто ленив, не желая предоставлять его при каждом вызове метода. Init(context), о котором вы сказали, может быть альтернативой, интересно, как мне заставить его вызываться.   -  person dpp    schedule 10.04.2017
comment
Как сказал @ haim770, предотвратите внедрение данных времени выполнения в ваши компоненты. Подробнее см. здесь.   -  person Steven    schedule 10.04.2017


Ответы (1)


Вы можете внедрить как UserManager<ApplicationUser>, так и IHttpContextAccessor в конструктор вашего класса, а затем:

public class SomeClass
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IHttpContextAccessor _context;
    public SomeClass(UserManager<ApplicationUser> userManager,IHttpContextAccessor context)
    {
        _userManager = userManager;
        _context = context;
    }

    public async Task DoSomethingWithUser() {
        var user = await _userManager.GetUserAsync(_context.HttpContext.User);
        // do stuff
    }
}

Если вы не хотите напрямую зависеть от IHttpContextAccessor, но все же хотите использовать DI, вы можете создать интерфейс для доступа к вашему пользователю:

public interface IApplicationUserAccessor {
    Task<ApplicationUser> GetUser();
}

public class ApplicationUserAccessor : IApplicationUserAccessor {
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IHttpContextAccessor _context;
    public ApplicationUserAccessor(UserManager<ApplicationUser> userManager, IHttpContextAccessor context) {
        _userManager = userManager;
        _context = context;
    }

    public Task<ApplicationUser> GetUser() {
        return _userManager.GetUserAsync(_context.HttpContext.User);
    }
}

Затем зарегистрируйте его в DI-контейнере и введите в SomeClass:

public class SomeClass
{
    private readonly IApplicationUserAccessor _userAccessor;
    public SomeClass(IApplicationUserAccessor userAccessor)
    {
        _userAcccessor = userAccessor;
    }

    public async Task DoSomethingWithUser() {
        var user = await _userAccessor.GetUser();
        // do stuff
    }
}

Другие варианты включают (как упоминалось в комментариях) ничего не вводить, но требуют передачи ApplicationUser в качестве аргумента методам, которые его требуют (хороший вариант), и требуют инициализации перед использованием любых методов со специальным методом Initialize(user) (не так хорошо, потому что вы не можете быть уверены, что это метод вызывается перед использованием других методов).

person Evk    schedule 10.04.2017
comment
В случае, если SomeClass объявлен в другой сборке, это заставит его зависеть от Microsoft.AspNetCore.Http - person haim770; 10.04.2017
comment
@ haim770 вы правы, лучше вообще не вводить ApplicationUser, а просто принять его как параметр метода. Однако, если OP все еще хочет получить его через DI - это возможный вариант. - person Evk; 10.04.2017
comment
Мои мысли точно, это альтернатива, но я не хочу быть привязанным к HttpContext - person dpp; 10.04.2017
comment
@dpp, вы можете избежать этого, и вы даже можете внедрить сам ApplicationUser, но я бы не советовал этого делать. Делать тяжелые вещи, которые могут бросить во время разрешения зависимостей, не очень хорошая идея. Если вы хотите пойти по маршруту инъекции - создайте какой-нибудь IApplicationUserAccessor с методом Task<ApplicationUser> GetUser() и внедрите его в свой класс. - person Evk; 10.04.2017
comment
@dpp Я обновил ответ примером с IApplicationUserAccessor. - person Evk; 10.04.2017
comment
Спасибо за совет. - person dpp; 10.04.2017
comment
Замечательно! :) IApplicationUserAccessor можно заменить, так что я не зависим от HttpContext. - person dpp; 10.04.2017
comment
Давайте продолжим обсуждение в чате. - person dpp; 10.04.2017
comment
Проблема с этим подходом заключается в том, что объект ввода (контроллер) находится в отдельной сборке, где ApplicationUser НЕ определен. Например, учтите, что этой отдельной сборке требуется доступ к UserManager‹ApplicationUser›, но она не может ссылаться на основное веб-приложение, поскольку на самом деле это веб-приложение ссылается на эту библиотеку классов. - person Lord of Scripts; 05.07.2017