ASP.NET 5&MVC 6 시리즈 튜 토리 얼 해독(7):주입 의존
이전 버 전의 의존 주입 기능 에서 주입 에 의존 하 는 입 구 는 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.csAutofac 통합 에 관 한 또 다른 사례: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