ASP.NET процессор Core 2 архитектуры и паттернов проектирования


Я начал неделю назад, чтобы разделить проект на слои: слой бизнес-логики, слой доступа к данным и UI слоя. Я взял в качестве примера этот проект: https://github.com/dotnet-architecture/eShopOnWeb. Я хотел бы знать, если моя архитектура-это правильно. Я всегда старался делать вещи мой путь, а затем через несколько недель или больше, я узнал, что путь я делаю это не самым лучшим образом, так что я думаю, я буду учиться намного больше, если кто-то с большим опытом может сказать мне, что я делаю неправильно.
Мой проект ссылка https://github.com/qpblaze/CollegeGrades. Для этого, я не хочу, чтобы код, но структура проекта.

Также, что-то мне неясно в отношении UserService.cs. В моих прошлых проектов я сделал свой собственный аккаунт-менеджера, но теперь я пытался использовать удостоверения, предоставленные корпорацией Майкрософт и я не знаю, если это правильный путь, чтобы сделать еще один сервис, который реализует интерфейс и выполнять методы, тож оттуда. Я сделал, ID, как это, потому что я думал, что я мог переключиться на другой аккаунт-менеджер и было бы гораздо проще реализовать этот интерфейс и сократить влияние на остальную часть кода. Файлы, которые я хочу рассмотреть:

Следующий код отвечает за регистрацию, выполните вход в пользователя и добавление его в роль:

Приложение UserService.в CS

Вот это реализация IUserService.cs интерфейс. Это основано на менеджеров личности.

public interface IUserService
{
    Task RegisterAsync(User user, string password);
    Task<string> GenerateEmailConfirmationTokenAsync(User user);

    Task SignInAsync(string email, string password);

    Task<User> FindByIdAsync(string id);
    Task ConfirmEmailAsync(string userID, string code);
    Task SignOutAsync();
}

public class UserService : IUserService
{
    #region Private Properties

    private readonly UserManager<User> _userManager;
    private readonly SignInManager<User> _signInManager;
    private readonly IRoleRepository _roleRepository;

    #endregion Private Properties

    #region Constructor

    public UserService(
        UserManager<User> userManager,
        SignInManager<User> signInManager,
        IRoleRepository roleRepository)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _roleRepository = roleRepository;
    }

    #endregion Constructor

    #region Private Methods

    private string GetProfileImageURL()
    {
        return "http://www.gravatar.com/avatar/" + Guid.NewGuid().ToString().Replace('-', '0') + "?&default=identicon&forcedefault=1&s=300";
    }

    private async Task AddToRoleAsync(User user, string name)
    {
        bool exists = await _roleRepository.RoleExistsAsync(name);
        if (!exists)
            await _roleRepository.CreateAsync(name);

        await _userManager.AddToRoleAsync(user, name);
    }

    #endregion Private Methods

    public async Task RegisterAsync(User user, string password)
    {
        // Default properties
        user.ProfileImage = GetProfileImageURL();

        var result = await _userManager.CreateAsync(user, password);
        if (!result.Succeeded)
        {
            throw new InvalidInputException(result.Errors.ToString());
        }

        await AddToRoleAsync(user, "Student");
    }

    public async Task<string> GenerateEmailConfirmationTokenAsync(User user)
    {
        return await _userManager.GenerateEmailConfirmationTokenAsync(user);
    }

    public async Task SignInAsync(string email, string password)
    {
        var result = await _signInManager.PasswordSignInAsync(email, password, false, false);

        if (!result.Succeeded)
            throw new InvalidInputException("Email", "Invalid email and/or password.");

        var user = await _userManager.FindByEmailAsync(email);
        if (!user.EmailConfirmed)
        {
            throw new InvalidInputException("Email", "The email is not cofirmed.");
        }
    }

    public async Task<User> FindByIdAsync(string id)
    {
        return await _userManager.FindByIdAsync(id);
    }

    public async Task ConfirmEmailAsync(string userID, string code)
    {
        if (userID == null)
            throw new ArgumentNullException(nameof(userID));
        if (code == null)
            throw new ArgumentNullException(nameof(code));

        var user = await FindByIdAsync(userID);

        if (user == null)
        {
            throw new ApplicationException($"Unable to load user with ID '{userID}'.");
        }

        var result = await _userManager.ConfirmEmailAsync(user, code);
    }

    public async Task SignOutAsync()
    {
        await _signInManager.SignOutAsync();
    }
}


RoleRepository.в CS

Это ответственная за управление ролями.

public interface IRoleRepository
{
    Task CreateAsync(string name);

    Task<bool> RoleExistsAsync(string name);
}

public class RoleRepository : IRoleRepository
{
    private readonly RoleManager<IdentityRole> _roleManager;

    public RoleRepository(RoleManager<IdentityRole> roleManager)
    {
        _roleManager = roleManager;
    }

    public async Task CreateAsync(string name)
    {
        var role = new IdentityRole
        {
            Name = name
        };

        await _roleManager.CreateAsync(role);
    }

    public async Task<bool> RoleExistsAsync(string name)
    {
        bool exists = await _roleManager.RoleExistsAsync(name);

        return exists;
    }
}

AccountController.в CS

И это, где я использую методы сверху для входа и регистрации пользователей.

[Authorize]
public class AccountController : Controller
{
    #region Private Properties

    private readonly IUserService _userService;
    private readonly IEmailSender _emailSender;
    private readonly IMapper _mapper;

    #endregion Private Properties

    #region Constructor

    public AccountController(
        IUserService userService,
        IEmailSender emailSender,
        IMapper mapper)
    {
        _userService = userService;
        _emailSender = emailSender;
        _mapper = mapper;
    }

    #endregion Constructor

    #region Register

    [HttpGet]
    [AllowAnonymous]
    [Route("register")]
    [RedirectLoggedUser]
    public IActionResult Register()
    {
        return View();
    }

    [HttpPost]
    [AllowAnonymous]
    [Route("register")]
    [RedirectLoggedUser]
    public async Task<IActionResult> Register(RegisterViewModel model)
    {
        if (!ModelState.IsValid)
            return View(model);

        User user = _mapper.Map<RegisterViewModel, User>(model);

        try
        {
            await _userService.RegisterAsync(user, model.Password);
        }
        catch (InvalidInputException ex)
        {
            ModelState.AddModelError(ex.Field, ex.Message);
            return View(model);
        }

        await SendConfirmationEmail(user);

        return RedirectToAction(nameof(ConfirmationEmailSent));
    }

    private async Task SendConfirmationEmail(User user)
    {
        var code = await _userService.GenerateEmailConfirmationTokenAsync(user);
        var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);

        await _emailSender.SendEmailConfirmationAsync(user.Email, callbackUrl);
    }

    #endregion Register

    #region LogIn

    [HttpGet]
    [AllowAnonymous]
    [Route("login")]
    [RedirectLoggedUser]
    public IActionResult LogIn()
    {
        return View();
    }

    [HttpPost]
    [AllowAnonymous]
    [Route("login")]
    [RedirectLoggedUser]
    public async Task<IActionResult> LogIn(LogInViewModel model, string returnUrl = null)
    {
        if (!ModelState.IsValid)
            return View(model);

        try
        {
            await _userService.SignInAsync(model.Email, model.Password);
        }
        catch (InvalidInputException ex)
        {
            ModelState.AddModelError(ex.Field, ex.Message);
            return View(model);
        }

        return RedirectToAction(nameof(HomeController.Index), "Home");
    }

    #endregion LogIn}


3182
5
задан 5 марта 2018 в 06:03 Источник Поделиться
Комментарии
1 ответ

Этот код хорошо написан. абстрагируясь от структуры личности является точно, как я сделал это в asp.net MVC и ядра asp.net . Двумя абстракциями, как представляется, не быть дырявыми, что является еще одним плюсом.

private void AddErrors(IdentityResult result) в AccountController который был отредактирован из можно было модифицировать для

private void AddErrors(InvalidInputException ex) {
ModelState.AddModelError(ex.Field, ex.Message);
}

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

Например

[HttpPost]
[AllowAnonymous]
[Route("register")]
[RedirectLoggedUser]
public async Task<IActionResult> Register(RegisterViewModel model) {
if (!ModelState.IsValid)
return View(model);

User user = _mapper.Map<RegisterViewModel, User>(model);

try {
await _userService.RegisterAsync(user, model.Password);
} catch (InvalidInputException ex) {
AddErrors(ex);
return View(model);
}

await SendConfirmationEmail(user);

return RedirectToAction(nameof(ConfirmationEmailSent));
}

Также заметил, что нет обработки ошибок в случае подтверждения по электронной почте не удается.

Однако InvalidInputException дырявое относится к выполнению от личности модели. Я получил вокруг этого, делая что-то наподобие того, как это было сделано в рамках.

 /// <summary>
/// Represents the minimal result of an identity operation
/// </summary>
public interface IIdentityResult : System.Collections.Generic.IEnumerable<string> {
bool Succeeded { get; }
}

public class DefaultIdentityResult : System.Collections.Generic.List<string>, IIdentityResult {
private DefaultIdentityResult(System.Collections.Generic.IEnumerable<string> errors) : base(errors) { }
private DefaultIdentityResult(params string[] errors) : base(errors) { }
public bool Succeeded { get { return Count == 0; } }

public static IIdentityResult Create(params string[] errors) {
return new DefaultIdentityResult(errors);
}

public static IIdentityResult Create(System.Collections.Generic.IEnumerable<string> errors) {
return new DefaultIdentityResult(errors);
}

static Lazy<IIdentityResult> success = new Lazy<IIdentityResult>(() => new DefaultIdentityResult());
public static IIdentityResult Success {
get {
return success.Value;
}
}

/// <summary>
/// Converts the value of the current <see cref="IIdentityResult"/> object to its equivalent string representation.
/// </summary>
/// <returns>A string representation of the current <see cref="IIdentityResult"/> object.</returns>
/// <remarks>
/// If the operation was successful the ToString() will return "Succeeded" otherwise it returned
/// "Failed : " followed by a comma delimited list of errors, if any.
/// </remarks>
public override string ToString() {
return Succeeded ?
"Succeeded" :
string.Format("{0} : {1}", "Failed", string.Join(",", this));
}
}

internal static class IdentityResultExtension {
internal static IIdentityResult AsIIdentityResult(this IdentityResult result) {
return DefaultIdentityResult.Create(result.Errors.Select(_ => _.Description));
}
}

Используя IUserService.RegisterAsync в качестве примера рефакторинг

public interface IUserService {
Task<IIdentityResult> RegisterAsync(User user, string password);

//...code removed for brevity
}

//....

public async Task<IIdentityResult> RegisterAsync(User user, string password) {

// Default properties
user.ProfileImage = GetProfileImageURL();

var identityResult = await _userManager.CreateAsync(user, password);
if (!identityResult.Succeeded) {
return identityResult.AsIIdentityResult();
}

var result = await AddToRoleAsync(user, "Student");

return result;
}

private async Task<IIdentityResult> AddToRoleAsync(User user, string name) {
bool exists = await _roleRepository.RoleExistsAsync(name);
if (!exists)
await _roleRepository.CreateAsync(name);

var identityResult = await _userManager.AddToRoleAsync(user, name);

return identityResult.AsIIdentityResult();
}

Действия контроллера будет выглядеть очень похоже на оригинал наличии удостоверения шаблон

[HttpPost]
[AllowAnonymous]
[Route("register")]
[RedirectLoggedUser]
public async Task<IActionResult> Register(RegisterViewModel model) {
if (!ModelState.IsValid)
return View(model);

User user = _mapper.Map<RegisterViewModel, User>(model);

var result = await _userService.RegisterAsync(user, model.Password);
if(result.Succeeded) {
await SendConfirmationEmail(user); //TODO: Refactor to get result of function
return RedirectToAction(nameof(ConfirmationEmailSent));
} else {
AddErrors(result);
}
return View(model);
}

private void AddErrors(IEnumerable<string> Errors) {
foreach (var error in Errors) {
ModelState.AddModelError("", error);
}
}

С той разницей, что он не был тесно связан с тож рамках проблемы

2
ответ дан 5 марта 2018 в 07:03 Источник Поделиться