2017-11-18 1 views
2

공통 종속성 중 일부를 캡슐화하는 Asp.Net Core 2.0 웹 응용 프로그램에서 BaseController를 만들면 여전히 실제 컨트롤러에 필요합니다..Net Core 2.0의 컨트롤러 및 BaseController에서 의존성 주입 복제

예를 들어 기본 MVC 6 웹 응용 프로그램의 표준 계정 및 관리 컨트롤러. 내가 구축하고 사용자 지정 웹 응용 프로그램 템플릿 나는 세 가지 다른 컨트롤러에 계정 컨트롤러를 리팩토링에서

public class AccountController : Controller 
{ 
    private readonly UserManager<ApplicationUser> _userManager; 
    private readonly SignInManager<ApplicationUser> _signInManager; 
    private readonly IEmailSender _emailSender; 
    private readonly ILogger _logger; 

    public AccountController(
     UserManager<ApplicationUser> userManager, 
     SignInManager<ApplicationUser> signInManager, 
     IEmailSender emailSender, 
     ILogger<AccountController> logger) 
    { 
     _userManager = userManager; 
     _signInManager = signInManager; 
     _emailSender = emailSender; 
     _logger = logger; 
    } 
    //rest of code removed 
} 

public class ManageController : Controller 
{ 
    private readonly UserManager<ApplicationUser> _userManager; 
    private readonly SignInManager<ApplicationUser> _signInManager; 
    private readonly IEmailSender _emailSender; 
    private readonly ILogger _logger; 
    private readonly UrlEncoder _urlEncoder; 

    private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; 

    public ManageController(
     UserManager<ApplicationUser> userManager, 
     SignInManager<ApplicationUser> signInManager, 
     IEmailSender emailSender, 
     ILogger<ManageController> logger, 
     UrlEncoder urlEncoder) 
    { 
     _userManager = userManager; 
     _signInManager = signInManager; 
     _emailSender = emailSender; 
     _logger = logger; 
     _urlEncoder = urlEncoder; 
    } 
    // rest of code removed 
} 

(사용자 등록에 관한 모든 것을 처리) RegisterController (로그인과 로그 아웃을 처리) 인 LoginController 및 균형에 세번째. 나는 Manage Controller를 두 곳으로 나누었고, ManagePasswordController (모든 것은 패스워드와 관련 있음)와 UserManageController (다른 모든 것).

각각에 대한 DI 요구 사항에는 많은 공통점이 있으며이를 BaseController에 넣고 싶습니다. 이런 식으로 보이시겠습니까?

public abstract class BaseController : Controller 
{ 
    private readonly IConfiguration _config; 
    private readonly IEmailSender _emailSender; 
    private readonly ILogger _logger; 
    private readonly SignInManager<ApplicationUser> _signInManager; 
    private readonly UserManager<ApplicationUser> _userManager; 

    protected BaseController(IConfiguration iconfiguration, 
     UserManager<ApplicationUser> userManager, 
     SignInManager<ApplicationUser> signInManager, 
     IEmailSender emailSender, 
     ILogger<ManageController> logger) 
    { 
     _config = iconfiguration; 
     _userManager = userManager; 
     _signInManager = signInManager; 
     _emailSender = emailSender; 
     _logger = logger; 
    } 
    //rest of code removed 
} 

하지만 아무 것도 달성하지 못한 것 같습니다. 왜냐하면 나에게 보이는 것처럼 나는 모든 것을 주입해야한다. 내가 옳을 수는 없다. (나는 DI에 대해 전혀 새로운 단서가 없다.) 그러나 BaseController는 BaseController와 RegisterController 사이에 공통적 인 DI를 허용하지 않아야한다. 내가 잘못? 내가하려는 일을 어떻게 성취합니까?

public class RegisterController : BaseController 
{ 
    private const string ConfirmedRegistration = "User created a new account with password."; 

    private readonly UserManager<ApplicationUser> _userManager; 
    private readonly SignInManager<ApplicationUser> _signInManager; 
    private readonly IEmailSender _emailSender; 
    private readonly ILogger _logger; 
    private readonly IConfiguration _config; 

    public RegisterController(
     IConfiguration config, 
     UserManager<ApplicationUser> userManager, 
     SignInManager<ApplicationUser> signInManager, 
     IEmailSender emailSender, 
     ILogger<AccountController> logger) : base(config, userManager, signInManager, emailSender, logger) 

    { 
     _userManager = userManager; 
     _signInManager = signInManager; 
     _emailSender = emailSender; 
     _logger = logger; 
     _config = config; 
    } 
    //rest of code removed 
} 

업데이트 경 Rufo의 제안

public abstract class BaseController : Controller 
{ 
    protected UserManager<ApplicationUser> UserManager { get; } 
    protected SignInManager<ApplicationUser> SignInManager { get; } 
    protected IConfiguration Config { get; } 
    protected IEmailSender EmailSender { get; } 
    protected ILogger AppLogger { get; } 

    protected BaseController(IConfiguration iconfiguration, 
     UserManager<ApplicationUser> userManager, 
     SignInManager<ApplicationUser> signInManager, 
     IEmailSender emailSender, 
     ILogger<ManageController> logger) 
    { 
     AppLogger = logger; 
     EmailSender = emailSender; 
     Config = iconfiguration; 
     SignInManager = signInManager; 
     UserManager = userManager; 
    } 
} 

그리고이 작동하지 않습니다 상속 컨트롤러

public class TestBaseController : BaseController 
{ 

    public TestBaseController() : base() 
    { 

    } 
} 

. Resharper는 TestBaseController 생성자에서 기본 생성자 호출에 매개 변수를 추가해야한다고 말합니다.

또한 BaseController는 .Net Core 2.0의 Controller 또는 ControllerBase에서 상속해야합니까?

+1

'BaseController'가 생성자 호출에서 그것들 모두를 제공해야한다면, 그렇다. 하지만 당신은'BaseController'를 리펙토링하여 이들 모두를 필요로하지 않을 수 있습니다. 또한,'BaseController'와 그것의 하위 클래스 모두에서 참조를 저장하는 이유는 무엇입니까? – fredrik

+0

@ fredrik .. 내가 게시물에서 언급 한 5 명의 컨트롤러는 모두 DI가 필요합니다. 실제 컨트롤러에서해야 할 일은 모두 있습니까? 개인 참조를 제거하고 전달 된 매개 변수를 사용하십시오. 즉, _signInManager.IsSignedIn 대신 signInManager.IssignedIn? – dinotom

+1

BaseController의 보호 된 속성을 통해 삽입 된 참조를 게시하여 파생 클래스에서 액세스 할 수 있습니다. –

답변

3

very few good reasons to use a BaseController in MVC이 있습니다. 이 시나리오의 기본 컨트롤러는 더 많은 코드를 추가하여 실질적인 이점을 제공하지 않습니다.

사실 cross-cutting concerns의 경우 MVC에서 처리 할 수있는 가장 일반적인 방법은 global filters이지만 MVC 코어에서 고려해야 할 몇 가지 새로운 옵션이 있습니다.

그러나 문제는 교차하는 우려로 보이지 않으므로 Single Responsibility Principle을 위반하는 것입니다. 즉, 3 개 이상의 종속 된 종속성을 갖는 것은 컨트롤러가 너무 많이하는 코드 냄새입니다. 가장 실질적인 해결책은 Refactor to Aggregate Services입니다.

이 경우, 명시 적으로 지정해야하는 최소한 1 개의 암시 적 서비스가 있다고 주장합니다. 즉, UserManagerSignInManager은 자체 서비스로 래핑되어야합니다. 거기에서 잠재적으로 다른 3 가지 종속성을 해당 서비스에 삽입 할 수 있습니다 (물론 사용 방법에 따라 다름). 따라서 AccountControllerManageController에 대한 단일 종속성을 잠재적으로 줄일 수 있습니다. 컨트롤러가 너무 많이하고있다

일부 징후 : 행동간에 공유되는 비즈니스 로직을 포함하는 "도우미"방법이 많이 있습니다

  1. .
  2. 액션 메소드는 간단한 HTTP 요청/응답 이상의 것을 처리합니다. 액션 메소드는 일반적으로 입력을 처리하거나 출력을 생성하고 뷰 및 응답 코드를 반환하는 서비스를 호출해야합니다. 이와 같은 경우에

, 그것은 당신이 등 자신의 서비스와 그 서비스의 종속성에 공유 된 논리,

+1

@ NightOwl888 ... 아직 MVC 6의 기본 웹 응용 프로그램에는 AccountController가 있습니다. 4 개의 종속성을 주입하고 ManageController를 5 개의 주입 된 종속성으로 대체합니다. 이것은 공통적으로 주입 된 의존성을 하나의 기본 클래스로 줄이기 위해이 연습의 전체 목적 이었기 때문에 새로 만든 컨트롤러가 기본 클래스에서 가져올 수있었습니다. 저는 전문 프로그래머가 아니며 학습 메커니즘으로 내 웹 앱 템플릿을 빌드하기 위해 Core 2.0을 배우려고합니다. 예를 들어 "적어도 하나의 암시 적 서비스를 명시 적으로 작성해야합니다."가 도움이 될 것입니다 – dinotom

1

Calc와 Sir Rufo 모두 제안에 따라 작동합니다.

public abstract class BaseController : Controller 
{ 
    protected UserManager<ApplicationUser> UserManager { get; } 
    protected SignInManager<ApplicationUser> SignInManager { get; } 
    protected IConfiguration Config { get; } 
    protected IEmailSender EmailSender { get; } 
    protected ILogger AppLogger { get; } 

    protected BaseController(IConfiguration iconfiguration, 
     UserManager<ApplicationUser> userManager, 
     SignInManager<ApplicationUser> signInManager, 
     IEmailSender emailSender, 
     ILogger<ManageController> logger) 
    { 
     AppLogger = logger; 
     EmailSender = emailSender; 
     Config = iconfiguration; 
     SignInManager = signInManager; 
     UserManager = userManager; 
    } 

    protected BaseController() 
    { 
    } 
} 

파라미터들은 여전히베이스 생성자

public class TestBaseController : BaseController 
{ 
    public static IConfigurationRoot Configuration { get; set; } 

    public TestBaseController(IConfiguration config, 
     UserManager<ApplicationUser> userManager, 
     SignInManager<ApplicationUser> signInManager, 
     IEmailSender emailSender, 
     ILogger<ManageController> logger) : base(config,userManager,signInManager,emailSender,logger) 
    { 
    } 

    public string TestConfigGetter() 
    { 

     var t = Config["ConnectionStrings:DefaultConnection"]; 
     return t; 
    } 

    public class TestViewModel 
    { 
     public string ConnString { get; set; } 
    } 
    public IActionResult Index() 
    { 
     var tm = new TestViewModel { ConnString = TestConfigGetter() }; 
     return View(tm); 
    } 
} 

이제 모든 분사 객체 인스턴스를 뜻 상속 제어기 주입 및 전달 될 수있다.

최종 솔루션은 일반적으로 필요한 인스턴스를 각각의 상속 된 컨트롤러에 주입 할 필요가 없기를 바랄뿐입니다. 특정 컨트롤러에 필요한 추가 인스턴스 개체 만 필요합니다. 애스펙트를 반복하는 코드에서 실제로 해결 된 것은 각 컨트롤러에서 비공개 필드를 제거한 것이 었습니다.

BaseController가 Controller 또는 ControllerBase에서 상속해야하는지 궁금한가요?

0

마이크로 소프트에 그 논리를 이동할 수 있는지 살펴 가치가있다. F라고하면 AspNetCore.MVC.Controller 클래스 (HttpContext를 파이프 라인에서 사용할 때마다 사용할 수있는 확장 메서드

HttpContext.RequestServices.GetService<T>

와 함께 제공 예를 들어 HttpContext를 속성은 NULL이됩니다 롬 컨트롤러의 생성자)

보십시오이 패턴

자료 컨트롤러

public abstract class BaseController<T> : Controller where T: BaseController<T> 
{ 

    private ILogger<T> _logger; 

    protected ILogger<T> Logger => _logger ?? (_logger = HttpContext.RequestServices.GetService<ILogger<T>>()); 

아동 컨트롤러

[Route("api/authors")] 
public class AuthorsController : BaseController<AuthorsController> 
{ 

    public AuthorsController(IAuthorRepository authorRepository) 
    { 
     _authorRepository = authorRepository; 
    } 

    [HttpGet("LogMessage")] 
    public IActionResult LogMessage(string message) 
    { 
     Logger.LogInformation(message); 

     return Ok($"The following message has been logged: '{message}'"); 
    } 

없이 시작에 서비스를 등록 해, 말을 .cs -> ConfingureServices 메소드