ASP.NET 5&MVC 6 시리즈 튜 토리 얼 해독(7):주입 의존

앞의 장(Middleware 장)에서 우 리 는 주입 기능(Dependency Injection)에 의존 하 는 것 을 언급 했다.ASP.NET 5 는 본 격 적 으로 주입 에 의존 하여 전 기능 을 실현 하여 개발 자가 더욱 탄력 있 는 구성 요소 프로그램 을 개발 할 수 있 도록 하 였 고 MVC 6 도 주입 에 의존 하 는 기능 을 이용 하여 Controller 와 View 의 서비스 주입 기능 을 재 설계 하 였 다.미래의 의존 주입 기능 은 더 많은 API 를 제공 할 수 있 습 니 다.모든 것 이 아직 접촉 의존 주입 을 시작 하지 않 았 다 면 잘 배 워 야 합 니 다.
이전 버 전의 의존 주입 기능 에서 주입 에 의존 하 는 입 구 는 MVC 중의IControllerFactory과 웹 API 중의IHttpControllerActivator에서 새로운 ASP.NET 5 에서 의존 주입 이 최 하층 의 기초 지지 로 바 뀌 었 고 MVC,Routing,Signalr,Entity Framrwork 등 은 모두 이 라이 가 주입 한IServiceProvider인터페이스 에 의존 하여 이 인터페이스 마이크로소프트 에 대해 기본 적 인 실현ServiceProvider을 제시 했다.그리고 Ninject 와 AutoFac 버 전의 포장 은 물론 다른 제3자 의 의존 주입 용기,예 를 들 어 Castle Windsor 등 을 사용 할 수 있 습 니 다.제3자 용 기 를 사용 하면 모든 의존 분석 은 제3자 용기 에 의 해 이 루어 진다.
일반적인 의존 유형 에 대한 분석 과 생 성,마이크로소프트 는 기본적으로 4 가지 유형의 생명 주 기 를 정의 합 니 다.각각 다음 과 같 습 니 다.
유형
묘사 하 다.
Instance
언제든지 특정한 인 스 턴 스 대상 만 사용 할 수 있 고 개발 자 는 이 대상 의 초기 화 작업 을 책임 져 야 한다.
Transient
매번 인 스 턴 스 를 다시 만 듭 니 다.
Singleton
하나의 예 를 만 들 고 호출 할 때마다 이 예 대상 을 되 돌려 줍 니 다.
Scoped
현재 역할 영역 에서 몇 번 을 호출 하 든 하나의 인 스 턴 스 입 니 다.역할 영역 을 바 꾸 면 다시 인 스 턴 스 를 만 듭 니 다.특정한 역할 내의 단일 예 와 유사 합 니 다.
형식 등록 및 예시
주입 형식 에 의존 하 는 등록 은 일반적으로 프로그램 이 시작 하 는 입구 에 있 습 니 다.예 를 들 어 Startup.cs 의 Configure Services 에서 이러한 주요 목적 은 주입 에 의존 하 는 유형 을 등록 하 는 것 입 니 다.주입 에 의존 하 는 주요 표현 은 인터페이스 프로 그래 밍 이기 때문에 본 사례 에서 저 는 인터페이스 와 실현 류 의 방식 으로 예 를 들 겠 습 니 다.
먼저 인터페이스 ITodoRepository 와 구현 클래스 TodoRepository 1 을 설명 합 니 다.코드 는 다음 과 같 습 니 다.

public interface ITodoRepository
{
 IEnumerable<TodoItem> AllItems { get; }
 void Add(TodoItem item);
 TodoItem GetById(int id);
 bool TryDelete(int id);
}

public class TodoItem
{
 public int Id { get; set; }
 public string Name { get; set; }
}

public class TodoRepository : ITodoRepository
{
 readonly List<TodoItem> _items = new List<TodoItem>();

 public IEnumerable<TodoItem> AllItems
 {
 get { return _items; }
 }

 public TodoItem GetById(int id)
 {
 return _items.FirstOrDefault(x => x.Id == id);
 }

 public void Add(TodoItem item)
 {
 item.Id = 1 + _items.Max(x => (int?)x.Id) ?? 0;
 _items.Add(item);
 }

 public bool TryDelete(int id)
 {
 var item = GetById(id);

 if (item == null) { return false; }

 _items.Remove(item);

 return true;
 }
}
서로 다른 성명 주기 유형 을 보 여주 기 위해 서 는 Todo Repository 2,Todo Repository 3,Todo Repository 4 등 몇 가지 유형 을 더 실현 하여 프레젠테이션 을 할 수 있 도록 하 는 것 을 권장 합 니 다.
그 다음 에 Configure Services 방법 에 인터페이스 ITodoRepository 유형 과 해당 하 는 실현 류 를 등록 합 니 다.본 사례 에서 서로 다른 생명 주기 에 따라 서로 다른 실현 류 를 등 록 했 습 니 다.구체 적 인 예 는 다음 과 같 습 니 다.

//      ,         ITodoRepository       TodoRepository1       
services.AddSingleton<ITodoRepository, TodoRepository1>();
services.AddSingleton(typeof(ITodoRepository), typeof(TodoRepository1)); //     

//        ,         ITodoRepository                    

TodoRepository2
services.AddInstance<ITodoRepository>(new TodoRepository2());
services.AddInstance(typeof(ITodoRepository), new TodoRepository2()); //     

//         ,       ITodoRepository    TodoRepository3
services.AddScoped<ITodoRepository, TodoRepository3>();
services.AddScoped(typeof(ITodoRepository), typeof(TodoRepository3));//     

//   ITodoRepository   ,         TodoRepository4 
services.AddTransient<ITodoRepository, TodoRepository4>();
services.AddTransient(typeof(ITodoRepository), typeof(TodoRepository));//     

//           ,            ,  :
services.AddTransient<LoggingHelper>();
주입 에 의존 하 는 MVC 에서 의 사용 방식 은 현재 세 가지 가 있 는데 그것 이 바로 Controller 의 구조 함수,속성 과 View 의 Inject 형식 이다.그 중에서 구조 함수 주입 은 이전의 MVC 와 같 습 니 다.예제 코드 는 다음 과 같 습 니 다.

public class TodoController : Controller
{
 private readonly ITodoRepository _repository;

 ///            ITodoRepository      ,        
 public TodoController(ITodoRepository repository)
 {
 _repository = repository;
 }

 [HttpGet]
 public IEnumerable<TodoItem> GetAll()
 {
 return _repository.AllItems; //           
 }
}
속성 주입 은 속성 에 하나의[FromServices]속성 을 추가 하면 자동 으로 인 스 턴 스 를 얻 을 수 있 습 니 다.

public class TodoController : Controller
{
 //            ITodoRepository      ,      
 [FromServices]
 public ITodoRepository Repository { get; set; }

 [HttpGet]
 public IEnumerable<TodoItem> GetAll()
 {
 return Repository.AllItems;
 }
}
주의:이런 방식 은 현재 Controller 및 하위 클래스 에 만 적용 되 며 일반 클래스 에는 적용 되 지 않 습 니 다.
또한,이러한 방식 을 통 해ActionContext,HttpContext,HttpRequest,HttpResponse,ViewDataDictionary,ActionBindingContext,@inject등 더 많은 시스템 인 스 턴 스 대상 을 얻 을 수 있 습 니 다.
보기 에 서 는IServiceProvider키 워드 를 통 해 주입 형식의 인 스 턴 스 추출 을 실현 할 수 있 습 니 다.예 는 다음 과 같 습 니 다.

@using WebApplication1
@inject ITodoRepository repository
<div>
 @repository.AllItems.Count()
</div>
가장 일반적인 사용 방식 은IServiceProvider의 인 스 턴 스 를 얻 는 것 입 니 다.이IOptions<AppSettings>인 스 턴 스 를 얻 는 방식 은 현재 다음 과 같은 몇 가지 가 있 습 니 다(그러나 범위 가 다 릅 니 다).

var provider1 = this.Request.HttpContext.ApplicationServices;           Service
var provider2 = Context.RequestServices; // Controller ,           Service
var provider3 = Resolver; //Controller 
그 다음 에 GetService 와 GetRequiredService 방법 을 통 해 지정 한 유형의 인 스 턴 스 를 얻 습 니 다.예 를 들 어 다음 과 같 습 니 다.

var _repository1 = provider1.GetService(typeof(ITodoRepository));
var _repository2 = provider1.GetService<LoggingHelper>();//    
//  2       

var _repository3 = provider1.GetRequiredService(typeof(ITodoRepository));
var _repository4 = provider1.GetRequiredService<LoggingHelper>();//    
//  2        ,        ,        
일반 클래스 의존 주입
새로운 ASP.NET 5 에 서 는 위 에서 말 한 인터페이스 류 의 의존 주입 을 지원 할 뿐만 아니 라 일반적인 유형의 의존 주입 도 지원 합 니 다.예 를 들 어 우리 의 생명 은 일반 류 입 니 다.예 를 들 어 다음 과 같 습 니 다.

public class AppSettings
{
 public string SiteTitle { get; set; }
}
상기 일반 류 는 매개 변수 구조 함수 가 있 는 지 없 는 지 를 확보 하려 면 등 록 된 용법 은 다음 과 같이 해 야 한다.

services.Configure<AppSettings>(app =>
{
 app.SiteTitle = "111";
});
사용 할 때@inject형식의 인 스 턴 스 를 가 져 와 야 합 니 다.그리고 Options 속성 은 바로 AppSettings 의 인 스 턴 스 입 니 다.코드 는 다음 과 같 습 니 다.

var appSettings = app.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Options;
물론,우 리 는 보기에 서ContainerMiddleware문법 을 사용 하여 실례 를 얻 을 수 있다.예시 코드 는 다음 과 같다.

@inject IOptions<AppSettings> AppSettings

<title>@AppSettings.Options.SiteTitle</title>
Scope 라 이 프 사이클 기반 의존 주입
일반적인 Scope 의존 주입
Scope 역할 도 메 인 을 기반 으로 하 는 인 스 턴 스 는 생 성 할 때 먼저 역할 도 메 인 을 만 든 다음 에 이 역할 도 메 인 에서 특정한 인 스 턴 스 를 가 져 와 야 합 니 다.우 리 는 예 시 를 보고 이 를 검증 해 야 합 니 다.우선,등록 의존 주입 유형,코드 는 다음 과 같 습 니 다.

services.AddScoped<ITodoRepository, TodoRepository>();
그리고 역할 영역 을 만 들 고 이 역할 영역 에서 인 스 턴 스 를 가 져 옵 니 다:

var serviceProvider = Resolver;

var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>(); //  Scope   
using (var scope = scopeFactory.CreateScope()) //     Scope   
{
 var containerScopedService = serviceProvider.GetService<ITodoRepository>(); //       
 var scopedService1 = scope.ServiceProvider.GetService<ITodoRepository>(); //    Scope   
 Thread.Sleep(200);
 var scopedService2 = scope.ServiceProvider.GetService<ITodoRepository>(); //    Scope   

 Console.WriteLine(containerScopedService == scopedService1); //   :False
 Console.WriteLine(scopedService1 == scopedService2); //  :True
}
또한 Scope 도 끼 워 넣 을 수 있 습 니 다.끼 워 넣 은 내외 작용 도 메 인 에서 얻 은 인 스 턴 스 도 다 릅 니 다.인 스 턴 스 코드 는 다음 과 같 습 니 다.

var serviceProvider = Resolver;

var outerScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
using (var outerScope = outerScopeFactory.CreateScope()) //  Scope   
{
 var innerScopeFactory = outerScope.ServiceProvider.GetService<IServiceScopeFactory>();
 using (var innerScope = innerScopeFactory.CreateScope()) //  Scope   
 {
 var outerScopedService = outerScope.ServiceProvider.GetService<ITodoRepository>();
 var innerScopedService = innerScope.ServiceProvider.GetService<ITodoRepository>();

 Console.WriteLine(outerScopedService == innerScopedService); //   :False
 }
}
HTTP 기반 Scope 의존 주입
이전에 많이 유 행 했 던 DI 용기 에 서 는 모든 요청 에 대해 이 요청 필드 에 하나의 인 스 턴 스 대상 을 유지 하 는 것 이 유행 하 였 습 니 다.즉,요청 할 때마다 하나의 유형의 대상 인 스 턴 스 를 한 번 만 만 들 면 성능 을 크게 향상 시 킬 수 있 습 니 다.
ASP.NET 5 에 서 는 HTTP 요청 을 기반 으로 한 Scope 의존 주입 이 하나의ContainerMiddleware을 통 해 이 루어 집 니 다.이 Middleware 를 호출 할 때 현재 요청 에 있 는 기본 DI 용 기 를 교체 하기 위해 한 정 된 역할 영역 을 만 듭 니 다.이 라인 에서 모든 후속 Middleware 는 이 새로운 DI 용 기 를 사용 합 니 다.전체 Pipeline 라인 을 요청 한 후에 이ContainerMiddleware의 역할 이 끝 납 니 다.이때 역할 영역 은 소각 되 고 이 역할 영역 에서 만 든 인 스 턴 스 대상 도 모두 소각 되 어 방출 됩 니 다.TestService의 순서 도 는 다음 과 같다.

구체 적 인 사용 방식 은 다음 과 같다.

app.Use(new Func<RequestDelegate, RequestDelegate>(nextApp => new ContainerMiddleware(nextApp, app.ApplicationServices).Invoke));
일반 클래스 의존 주입 처리
현재 일반 클래스 의 의존 주입 은 구조 함수 만 지원 합 니 다.예 를 들 어 우 리 는 하나의ITodoRepository클래스 로 정 해 졌 습 니 다.코드 는 다음 과 같 습 니 다.

public class TestService
{
 private ITodoRepository _repository;
 public TestService(ITodoRepository r)
 {
 _repository = r;
 }

 public void Show()
 {
 Console.WriteLine(_repository.AllItems);
 }
}
구조 함수 에[FromServices]클래스 의 인 자 를 입력 하여 이 인 스 턴 스 를 사용 합 니 다.사용 할 때 이 종 류 를 DI 용기 에 등록 해 야 합 니 다.코드 는 다음 과 같 습 니 다.

services.AddScoped<ITodoRepository, TodoRepository>();
services.AddSingleton<TestService>();
그리고 다음 문장 을 호출 하면 사용 할 수 있 습 니 다.

var service = serviceProvider.GetRequiredService<TestService>();
또한 현재 상황 에서 의존 주입 기능 을 사용 할 수 없습니다.예 를 들 어 다음 코드 가TestService2인 스 턴 스 를 가 져 오 는 과정 에서 오류 가 발생 할 수 있 습 니 다.

public class TestService2
{
 [FromServices]
 public ITodoRepository Repository { get; set; }
 public void Show()
 {
 Console.WriteLine(Repository.AllItems);
 }
}
일반 클래스 에서 HttpContext 인 스 턴 스 가 져 오기
MVC 6 에 서 는 HttpContent.Current 를 통 해 컨 텍스트 대상 을 가 져 올 수 없 기 때문에 일반 클래스 에서 사용 할 때 문제 가 발생 할 수 있 습 니 다.일반 클래스 에서 이 컨 텍스트 대상 을 사용 하려 면 주입 에 의존 하여 HttpContext 인 스 턴 스 를 가 져 와 야 합 니 다.마이크로소프트 는 ASP.NET 5 에서IHttpContextAccessor인 터 페 이 스 를 제공 하여 컨 텍스트 대상 을 가 져 올 수 있 습 니 다.즉,우 리 는 이 유형의 매개 변 수 를 구조 함수 에 넣 어서 문맥 인 스 턴 스 를 얻 을 수 있 습 니 다.코드 는 다음 과 같 습 니 다.

public class TestService3
{
 private IHttpContextAccessor _httpContextAccessor;
 public TestService3(IHttpContextAccessor httpContextAccessor)
 {
 _httpContextAccessor = httpContextAccessor;
 }

 public void Show()
 {
 var httpContext = _httpContextAccessor.HttpContext;//         
 Console.WriteLine(httpContext.Request.Host.Value);
 }
}
사용 할 때 다음 과 같은 문 구 를 직접 통과 하면 됩 니 다.코드 는 다음 과 같 습 니 다.

var service = serviceProvider.GetRequiredService<TestService3>();
service.Show();
알림:일반 클래스 의 구조 함수 에 서 는 여러 개의 DI 용기 가 지원 하 는 데 이 터 를 매개 변수 로 전송 할 수 있 습 니 다.
제3자 DI 용기 사용
현재.NET core 는 지원 되 지 않 으 며,전체 기능 판.NET framework 에서 만 사용 할 수 있 으 므 로 사용 할 때 주의해 야 합 니 다.제3자 DI 용기 의 교 체 는 보통 Startup.cs 의 Configure 방법 에서 이 루어 지 며,후속 Middleware 가 관련 의존 주입 기능 을 사용 할 수 있 도록 방법의 시작 부분 에서 교 체 됩 니 다.
먼저 제3자 용 기 를 도입 하고 Autofac 를 예 로 들 어 Microsoft.Framework.Dependency Injection.Autofac 를 도입 한 다음 에 다음 예제 의 교체 코드 를 추가 하면 됩 니 다.

app.UseServices(services =>
{
 services.AddMvc();// AddMvc      
 var builder = new ContainerBuilder();//        
 builder.Populate(services);//    Services   Autofac      
 IContainer container = builder.Build();
 return container.Resolve<IServiceProvider>();//  AutoFac   IServiceProvider
});
상기 방법 을 사용 할 때 Mvc 의 등록 코드services.AddMvc();ConfigureServices에서 이 표현 식 으로 옮 겨 야 합 니 다.그렇지 않 으 면 이상 을 보고 하고 마이크로소프트 가 해결 할 때 까지 기 다 려 야 합 니 다.
또한 마이크로소프트 의 현재 사례 프로젝트 에서 아직 공개 되 지 않 았 다.일부 코드 를 분석 한 결과 우 리 는Microsoft.AspNet.Hosting프로그램 에서StartupLoader.cs프로그램의 입구 점 의 집행 을 담당 하 는 것 을 발견 할 수 있다.이 파일 에서 우 리 는 먼저 호출Startup.cs중의ConfigureServices방법 을 알 고 그 다음 에 호출Configure방법 을 알 수 있다.예 시 된ConfigureServices의 반환 값 은 void 형식 임 을 볼 수 있 지만,원본 분석 에서 약 정 된 해석ConfigureServices방법 에 따라 반환 유형 이 있 는 지 없 는 지 를 먼저 판단 하고,있 으 면 이 방법 을 실행 하고,이 반환 에서 되 돌아 오 는 새로운IServiceProvider실례 를 사용한다.없 으 면IServiceProvider유형의void방법 을 계속 찾 아 보 세 요.따라서 우 리 는 이러한 방식 을 통 해 제3자 의 DI 용 기 를 교체 할 수 있 습 니 다.인 스 턴 스 코드 는 다음 과 같 습 니 다.

//      void   ConfigureServices  
public IServiceProvider ConfigureServices(IServiceCollection services)
{
 var builder = new ContainerBuilder(); //        
 builder.Populate(services); //    Services   Autofac      
 IContainer container = builder.Build();
 return container.Resolve<IServiceProvider>(); //  AutoFac   IServiceProvider
}
이렇게 하면 예전 처럼 Autofac 방식 으로 의존 유형 관 리 를 할 수 있 습 니 다.예 를 들 어 다음 과 같 습 니 다.

public class AutofacModule : Module
{
 protected override void Load(ContainerBuilder builder)
 {
 builder.Register(c => new Logger())
  .As<ILogger>()
  .InstancePerLifetimeScope();

 builder.Register(c => new ValuesService(c.Resolve<ILogger>()))
  .As<IValuesService>()
  .InstancePerLifetimeScope();
 }
}
주소:https://github.com/aspnet/Hosting/blob/dev/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs
Autofac 통합 에 관 한 또 다른 사례:http://alexmg.com/autofac-4-0-alpha-1-for-asp-net-5-0-beta-3/
최선 의 실천
의존 주입 을 사용 할 때 우 리 는 다음 과 같은 최선 의 실천 을 지 켜 야 한다.
모든 일 을 하기 전에 프로그램 입구 에 모든 의존 유형 을 미리 등록 해 야 한다.IServiceProvider 인 터 페 이 스 를 직접 사용 하 는 것 을 피하 고 구조 함수 에 의존 해 야 할 유형 을 명시 적 으로 추가 하면 엔진 에 의존 하여 인 스 턴 스 를 분석 하고 의존 하기 어 려 우 면 추상 적 인 공장 을 사용 할 수 있다.인 터 페 이 스 를 기반 으로 프로 그래 밍 을 하 는 것 이지 실현 을 바탕 으로 프로 그래 밍 을 하 는 것 이 아니다.
참고 1:http://social.technet.microsoft.com/wiki/contents/articles/28875.dependency-injection-in-asp-net-vnext.aspx
참고 2:http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx

좋은 웹페이지 즐겨찾기