在解析服务时,使用自动生成的工厂与否之间存在差异。例如,如果我有这些类:
public class A {
public delegate A Factory();
public A(B someDependency) {}
}
public class B {
public B(String s) {}
}
public class AFactory {
private readonly Func<B> _b;
public AFactory(Func<B> b) {
_b = b;
}
public A Create() {
return new A(_b());
}
}
然后注册他们:
var cb = new ContainerBuilder();
cb.RegisterType<A>();
cb.RegisterType<AFactory>();
var c = cb.Build();
致电c.Resolve<AFactory>()
会立即生成DependencyResolutionException
。调用c.Resolve<A.Factory>()
将返回一个委托,该委托将在调用时抛出DependencyResolutionException
。
在我看来,c.Resolve<AFactory>()
提供的行为更为可取,因为在解析服务时会抛出异常,而不是在将来实际使用服务的某个时刻抛出异常。我正在寻找可以应用于我项目中所有注册的通用解决方案。
是否有某种方法可以改变我的示例中c.Resolve<A.Factory>()
的行为,使其立即抛出异常?
答案 0 :(得分:1)
在我看来,c.Resolve()提供的行为更可取,因为在解析服务时会抛出异常
解析委托将导致延迟操作。这意味着具体的解析操作可以在初始解决之后进行。在初始解析和最终解决之间,容器可能会发生变化。
请看以下示例:
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().AsSelf();
builder.RegisterType<Bar1>().As<IBar>();
IContainer container = builder.Build();
Foo foo = container.Resolve<Foo>();
foo.Do(); // ==> DependencyResolutionExtension
// update the container with the Pouet type
builder = new ContainerBuilder();
builder.RegisterType<Pouet>().AsSelf();
builder.Update(container);
foo.Do(); // OK
// update the container with another IBar
builder = new ContainerBuilder();
builder.RegisterType<Bar2>().As<IBar>();
builder.Update(container);
foo.Do(); // OK
}
}
public class Foo
{
public Foo(Func<IBar> barFactory)
{
this._barFactory = barFactory;
}
private readonly Func<IBar> _barFactory;
public void Do()
{
IBar bar = this._barFactory();
}
}
public interface IBar { }
public class Bar1 : IBar
{
public Bar1(Pouet p) { }
}
public class Bar2 : IBar
{
}
public class Pouet { }
如果您真的想在初始解析操作中抛出异常,我可以看到2个解决方案:
RegistrationSource
,为Func<T>
(或您想要的代理人)提供新的实施。不太困难,但您必须在初始解析操作期间解决该类型。下面的代码是如何做到这一点的示例。我没有用不同的生命周期注册类型测试它,我也不知道这些对象的处理在某些情况下是如何工作的。此代码适用于简单的情况
/*
* This code was not fully tested and it is not optimized
* It doesn't fully managed the lifetimescope of the object and memory leak may appear
*/
internal class FixedFactoryRegistrationSource : IRegistrationSource
{
internal class FixedFactory<T>
{
public FixedFactory(T instance)
{
this._instance = instance;
}
private readonly T _instance;
public T GetInstance()
{
return this._instance;
}
}
public FixedFactoryRegistrationSource(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(FixedFactory<>)).As(typeof(FixedFactory<>));
}
public Boolean IsAdapterForIndividualComponents
{
get { return true; }
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
IServiceWithType serviceWithType = service as IServiceWithType;
if (serviceWithType == null || !serviceWithType.ServiceType.IsGenericType)
{
yield break;
}
if (serviceWithType.ServiceType.GetGenericTypeDefinition() != typeof(Func<>))
{
yield break;
}
Type elementType = serviceWithType.ServiceType.GetGenericArguments()[0];
Type fixedFactoryType = typeof(FixedFactory<>).MakeGenericType(elementType);
Service fixedFactoryService = serviceWithType.ChangeType(fixedFactoryType);
MethodInfo getInstanceMethod = typeof(FixedFactory<>).MakeGenericType(elementType).GetMethod("GetInstance");
foreach (IComponentRegistration registration in registrationAccessor(fixedFactoryService))
{
yield return RegistrationBuilder.ForDelegate(typeof(Func<>).MakeGenericType(elementType), (c, p) =>
{
// /!\ disposal of this object is not managed
Object fixedFactory = c.ResolveComponent(registration, p);
return getInstanceMethod.CreateDelegate(typeof(Func<>)
.MakeGenericType(elementType), fixedFactory);
})
.As(service)
.Targeting(registration)
.CreateRegistration();
}
}
}
我不推荐这样做,因为所有解决方案都非常困难,并且 Autofac 的设计并非如此。