我们正在使用域来自定义应用程序的行为方式。我将在示例中说明它:
// default behavior
public class CoreService : IService {
public virtual string Hello { get { return "Hello"; } }
public virtual string FavouriteDrink { get { return "Water"; } }
}
// german.site.com
public class GermanService : CoreService {
public override string Hello { get { return "Gutten tag"; } }
public override string FavouriteDrink { get { return "Beer"; } }
}
// usa.site.com
public class UsaService : CoreService {
public override string FavouriteDrink { get { return "Cofee"; } }
}
服务如下引导:
var container = new UnityContainer();
container.RegisterType<IService, CoreService>();
container.RegisterType<IService, GermanService>("german.site.com");
container.RegisterType<IService, UsaService>("usa.site.com");
我使用Unity来引导mvc控制器。 IE:
public class HomeController : Controller {
private IService m_Service;
// contructor dependency injection magic - this resolves into "CoreService"
public HomeController([Dependency]IService service) {
if (service == null) {
throw new ArgumentNullException("service");
}
m_Service = service;
}
}
有没有办法如何改变统一分辨率,以便将域考虑在内?现在我最终得到了
public class HomeController : Controller {
private IService m_Service;
// contructor dependency injection magic - a lot less magical
public HomeController() {
m_Service = DomainServiceLocator.Retrieve<IService>();
}
}
支持课程:
public static class DomainServiceLocator {
private static UnityContainerAdapter adapter;
public static T Retrieve<T>() {
string domain = HttpContext.Current.Request.Url.Host;
if (adapter.IsServiceRegistered(typeof(T), domain)) {
return adapter.Resolve<T>(domain);
}
return adapter.Resolve<T>();
}
}
public class QueryableContainerExtension : UnityContainerExtension {
private List<RegisterInstanceEventArgs> registeredInstances = new List<RegisterInstanceEventArgs>();
private List<RegisterEventArgs> registeredTypes = new List<RegisterEventArgs>();
protected override void Initialize() {
this.Context.Registering += (sender, e) => { this.registeredTypes.Add(e); };
this.Context.RegisteringInstance += (sender, e) => { this.registeredInstances.Add(e); };
}
public bool IsServiceRegistered(Type service, string name) {
return registeredTypes.FirstOrDefault(e => e.TypeFrom == service && e.Name == name) != null
|| registeredInstances.FirstOrDefault(e => e.RegisteredType == service && e.Name == name) != null;
}
}
public class UnityContainerAdapter {
private readonly QueryableContainerExtension queryableContainerExtension;
private readonly IUnityContainer unityContainer;
public UnityContainerAdapter()
: this(new UnityContainer()) {
}
public UnityContainerAdapter(IUnityContainer unityContainer) {
this.unityContainer = unityContainer;
// adding extensions to unity container
this.queryableContainerExtension = new QueryableContainerExtension();
unityContainer.AddExtension(this.queryableContainerExtension);
}
public T Resolve<T>(string name) {
return unityContainer.Resolve<T>(name);
}
public T Resolve<T>() {
return unityContainer.Resolve<T>();
}
public bool IsServiceRegistered(Type service, string name) {
return this.queryableContainerExtension.IsServiceRegistered(service, name);
}
}
答案 0 :(得分:1)
在运行时解析某些内容时,我喜欢在这些场景中使用注入工厂。基本上,您通过域名解析您的类型:
所以在你的作文根你可以像这样注册:
container.RegisterType<Func<string, IService>>
(
new InjectionFactory(c => new Func<string, IService>(name => c.Resolve<IService>(name)))
);
然后在你的HomeController中你可以注入委托
public class HomeController
{
private readonly Func<string,IService> _serviceFactory;
public HomeController(Func<string, IService> serviceFactory)
{
if(serviceFactory==null)
throw new ArgumentNullException("serviceFactory");
this._serviceFactory= serviceFactory;
}
public void DoSomethingWithTheService()
{
var domain = this.HttpContext.Uri.Host;
var service = this._serviceFactory(domain);
var greeting = service.Hello;
}
}
```
然后,这仍然是单元可测试的,并且你没有泄露DI包含&#34;组合root&#34;之外的实现。
另外.. CoreService应该是抽象的,以避免直接实例化它吗?
答案 1 :(得分:0)
以下是我最终得到的解决方案 - 它基于@Spencer的想法。我已经创建了一个工厂,工厂的默认实现引用了DI容器本身(在我的情况下为IUnityContainer
),因此它可以在被要求时根据域执行解析。它也更加现代友好&#34;因为在当前的ASP.NET(ASP.NET CORE)中,没有提供当前HttpContext
的魔术单例,并且DI被硬编码到框架中。
public interface IFactory<T>
{
T Retrieve(string domain);
}
internal sealed class Factory<T> : IFactory<T>
{
private readonly IUnityContainer _container;
public Factory(IUnityContainer container)
{
_container = container;
}
public T Resolve(string domain)
{
// this is actually more complex - we have chain inheritance here
// for simplicity assume service is either registered for given
// domain or it throws an error
return _container.Resolve<T>(domain);
}
}
// bootstrapper
var container = new UnityContainer();
container.RegisterType<IService, CoreService>();
container.RegisterType<IService, GermanService>("german.site.com");
container.RegisterType<IService, UsaService>("usa.site.com");
container.RegisterInstance<IFactory<IService>>(new Factory<IService>(container));
家庭控制器看起来像
public class HomeController : Controller {
private IFactory<IService> m_Factory;
public HomeController(IFactory<IService> factory) {
m_Factory = factory;
}
private void FooBar() {
var service = m_Factory.Retrieve(this.HttpContext.Uri.Host);
var hello = service.Hello;
}
}
还值得一提的是 - 因为我懒惰 - 我构建了一个装饰属性系统,如
[Domain("german.site.com")]
public class GermanService : IService { ... }
[DomainRoot]
public class CoreService : IService { ... }
[Domain("usa.site.com")]
public class UsaService : CoreService { ... }
因此,在给定程序集中的所有类型上自动完成引导。但是那部分有点冗长 - 如果有人有兴趣我可以在github上发布。