gpt4 book ai didi

unity-container - 使用接口(interface)拦截器按约定注册 Unity 导致 "[type] is not interceptable"异常

转载 作者:行者123 更新时间:2023-12-04 06:56:12 25 4
gpt4 key购买 nike

我想用 WithMappings.FromMatchingInterface 将所有实现特定接口(interface)的类注册到 Unity 中。惯例。此外,我希望使用接口(interface)拦截行为来拦截所有已注册的对象。问题是 Unity 还注册了具体类之间的映射,当这些类被解析时,会抛出异常并显示以下消息:

"[type] is not interceptable"



我意识到使用具体类类型解析对象不是最佳实践,但我想知道为什么 Unity 在按约定注册时会自动为接口(interface) -> 具体类以及具体类 -> 具体类添加映射?这意味着如果您添加接口(interface)拦截器并使用具体类型进行解析,它将永远无法工作。

我想要的结果是Unity在按照约定注册并给它一个接口(interface)拦截器时没有添加具体类型->具体类型映射,这样我们就可以使用它的具体类型来解析类,如果我们愿意,我们只需不被拦截。

我不想使用 VirtualMethodInterceptor因为我不想为了拦截工作而对类进行更改,这包括从 MarshalByRef 继承.我还想避免单独注册所有对象。

因此,我的问题是,按惯例注册时如何仅注册接口(interface)映射?

更新 :单独注册类会产生同样的问题,因此假设一旦一个对象注册了一个接口(interface)拦截器,那么它就不能通过使用具体类型来解决。

新注册码:

container.RegisterType<ISomeService, SomeService>(new InjectionMember[]
{
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<TraceInterceptor>()
});
container.RegisterType<ISomeRepository, SomeRepository>(new InjectionMember[]
{
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<TraceInterceptor>()
});

更新 2 为所有接口(interface)添加默认拦截器似乎可行,尽管此解决方案相当hacky。此解决方案在按惯例进行标准注册之前需要一些代码,并删除 InterfaceInterceptor在基于公约的注册中。

预注册码

foreach (var type in types)
{
container
.Configure<Interception>()
.SetDefaultInterceptorFor(type.GetInterface("I" + type.Name), new InterfaceInterceptor());
}

一些解释困境的代码:

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>();

var types = AllClasses.FromAssemblies(typeof(ISomeService).Assembly).Where(type => type == typeof(SomeService) || type == typeof(SomeRepository));

container.RegisterTypes(
types,
WithMappings.FromMatchingInterface,
getLifetimeManager: WithLifetime.ContainerControlled,
getInjectionMembers: c => new InjectionMember[]
{
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<TraceInterceptor>()
});

// this works fine, the interceptor does what it is supposed to.
var someService1 = container.Resolve<ISomeService>();
someService1.SomeServiceMethod("Hello from main method");

// should I by any chance resolve using the concrete service directly, it has a meltdown due to the interface interceptor.
var someService2 = container.Resolve<SomeService>();
someService2.SomeServiceMethod("This will never be shown due to a hissy fit thrown by the container about the concrete SomeService is not interceptable.");
}
}

public class TraceInterceptor : IInterceptionBehavior
{
public System.Collections.Generic.IEnumerable<System.Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}

public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Trace.WriteLine(string.Format("Hello from trace interception behavior for type [{0}]!", input.Target.GetType().FullName));

return getNext().Invoke(input, getNext);
}

public bool WillExecute
{
get { return true; }
}
}

public interface ISomeService
{
string SomeServiceMethod(string someParameter);
}

public class SomeService : ISomeService
{
private ISomeRepository _someRepository;

public SomeService(ISomeRepository someRepository)
{
_someRepository = someRepository;
}

public string SomeServiceMethod(string someParameter)
{
return _someRepository.SomeRepositoryMethod(someParameter);
}
}

public interface ISomeRepository
{
string SomeRepositoryMethod(string someParameter);
}

public class SomeRepository : ISomeRepository
{
public string SomeRepositoryMethod(string someParameter)
{
Trace.WriteLine(someParameter);

return "Hello from repository!";
}
}
}

最佳答案

我相信我可以对您的问题有所了解。我也想知道为什么要注册具体类型以及将相同具体类型映射到其接口(interface)的注册。

我进行了搜索以发现是否有其他人遇到同样的问题,认为我可能做错了什么。但我最终进入了这个 Codeplex 讨论线程:Registration convention generates two registrations for each mapping .在这个线程中,randylevy(在第 3 篇到最后一篇文章中)指出这是 LifetimeManager 时的默认行为。和/或注入(inject)成员被指定为公约的一部分:

In terms of the behavior you are seeing, I believe this is by design. If you specify a LifetimeManager or injectionMembers then Unity will register any types passed in with the supplied values. This makes sense in general because the user has specified the various configuration they desire for the types passed in.

For example you can also use registration by convention to just register types:

    container.RegisterTypes(
AllClasses.FromLoadedAssemblies(false, false, false, false),
WithMappings.None,
WithName.Default,
t => new ContainerControlledLifetimeManager(),
null,
true);

So in this case all classes (lets say Class1) will be registered as singletons (ContainerControlledLifetimeManager) with no interface mapping (WithMappings.None). If the lifetime manager was not specified then Class1 would not have been registered. But because the lifetime manager is specified the registration needs to be setup to use the correct user specified lifetime manager.



我相信这回答了您的问题,即为什么在按照约定使用注册时,除了给定类型的接口(interface)类型映射之外,还有一个具体的类型映射。正如该讨论线程上的 OP 所述,我也希望您可以通过约定类在注册中设置一些选项以禁用具体类型映射。

但是,最后,我不确定它是否会产生巨大的差异。如果您正在针对合约进行编程(例如,使用接口(interface)类型作为构造函数/方法/属性参数),那么容器将始终使用接口(interface)映射注册进行解析;并且如果在该接口(interface)注册上设置了任何注入(inject)/拦截,则在解析时,该类型应该注入(inject)适当的对象并且应该发生配置的拦截。

在我工作的地方,我们需要进行几种不同类型的常见注册,即服务、存储库和其他类别的类/接口(interface)。举个例子,假设我有一大堆 Service 类需要注册到它们的接口(interface),并且还有与之关联的验证器。命名约定是 MyService和对应的接口(interface) IMyService和相应的验证器 MyServiceValidator .我创建了一个 ServiceRegistrationConvention类来完成此操作,如下所示:

public class ServiceRegistrationConvention : RegistrationConvention
{
/// <summary>
/// Gets a function to get the types that will be requested for
/// each type to configure.
/// </summary>
public override Func<Type, IEnumerable<Type>> GetFromTypes()
{
return WithMappings.FromMatchingInterface;
}

/// <summary>
/// Gets a function to get the additional
/// <see cref="T:Microsoft.Practices.Unity.InjectionMember" />
/// objects for the registration of each type. Defaults to no injection members.
/// </summary>
public override Func<Type, IEnumerable<InjectionMember>> GetInjectionMembers()
{
return GetServiceValidator;
}

/// <summary>
/// Gets a function to get the
/// <see cref="T:Microsoft.Practices.Unity.LifetimeManager" />
/// for the registration of each type. Defaults to no
/// lifetime management (e.g. transient).
/// </summary>
public override Func<Type, LifetimeManager> GetLifetimeManager()
{
// Where I work, we use this lifetime manager for everyting.
// I wouldn't recommend this right off the bat--I'm looking
// into changing this, personally, but right now, this is
// what we use and it works for our MVC application.
return WithLifetime.Custom<PerRequestLifetimeManager>;
}

/// <summary>
/// Gets a function to get the name to use for the registration of each type.
/// </summary>
public override Func<Type, string> GetName()
{
return WithName.Default;
}

/// <summary>
/// Gets types to register.
/// </summary>
public override IEnumerable<Type> GetTypes()
{
// You may want to further restrict the search for classes to register
// by doing some sort of namespace matching:
//
// t.Namespace.StartsWith(
// "MyCompanyNamespacePrefix", StringComparison.Ordinal
// )
//
// for example.
return AllClasses.FromLoadedAssemblies()
.Where(t => t.Name.EndsWith("Service", StringComparison.Ordinal));
}

/// <summary>
/// Given a type, get the type's corresponding validator, if any.
/// </summary>
private IEnumerable<InjectionMember> GetServiceValidator(Type type)
{
if (type == null)
throw new ArgumentNullException("type");

// In our case, the validators live in the same assembly
// as the class its validating...
string matchingValidatorName = string.Concat(type.Name, "Validator");
Type vType = AllClasses.FromAssemblies(new[] { type.Assembly })
.FirstOrDefault(t =>
string.Equals(t.Name, matchingValidatorName, StringComparison.Ordinal)
);

return (vType != null) ?
new List<InjectionMember>
{
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior(vType)
}
:
null;
}
}

同样,只要您始终从其接口(interface)解析类型,一切都应该正常工作。

更新:好吧,不幸的是,拦截无法正常工作。当我发现这个问题时,我一定会更新我的答案。

更新:这确实像这里写的那样工作。我在另一个配置中出现错误,导致整个应用程序失败。一旦我修复了该错误,拦截就会按预期发生。

关于unity-container - 使用接口(interface)拦截器按约定注册 Unity 导致 "[type] is not interceptable"异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22869002/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com