首页 > 解决方案 > 使用统一拦截时 UnityConfig.Resolve 组件失败

问题描述

我在 .NET MVC 5 应用程序中使用 Unity 解析组件时遇到问题。在我的 App_Start 文件夹中,我生成了 UnityConfig.cs 和 UnityMvcActivator.cs,如下所示:

public static class UnityConfig
{
    #region Unity Container
    private static Lazy<IUnityContainer> container =
      new Lazy<IUnityContainer>(() =>
      {
          var container = new UnityContainer();
          RegisterTypes(container);
          return container;
      });
    public static IUnityContainer Container => container.Value;
    #endregion

    public static void RegisterTypes(IUnityContainer container)
    {
        // NOTE: To load from web.config uncomment the line below.
        // Make sure to add a Unity.Configuration to the using statements.
        // container.LoadConfiguration();

        container.AddNewExtension<Interception>();

        container.RegisterType<ILogger, EntLibFileLogger>(new ContainerControlledLifetimeManager(),
            new InjectionConstructor(ConfigurationManager.AppSettings["logTrace"],
                int.Parse(ConfigurationManager.AppSettings["logSize"])));
        container.RegisterType<IUserRepository, UserRepository>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<LoggingInterceptionBehavior>());
        container.RegisterType<IUserProvider, UserProvider>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<LoggingInterceptionBehavior>());
        ...
    }
}

public static class UnityMvcActivator
{
    /// <summary>
    /// Integrates Unity when the application starts.
    /// </summary>
    public static void Start() 
    {
        FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
        FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(UnityConfig.Container));

        DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Container));

        // TODO: Uncomment if you want to use PerRequestLifetimeManager
        // Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
    }

    /// <summary>
    /// Disposes the Unity container when the application is shut down.
    /// </summary>
    public static void Shutdown()
    {
        UnityConfig.Container.Dispose();
    }
}

拦截看起来像这样:

class LoggingInterceptionBehavior : IInterceptionBehavior
{
    private readonly ILogger _logger;

    public LoggingInterceptionBehavior(ILogger logger)
    {
        _logger = logger;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        var methodDesc = $"{input.MethodBase.DeclaringType.Name}.{input.MethodBase.Name}";
        var args = input.Arguments == null || input.Arguments.Count == 0
            ? "null"
            : JsonConvert.SerializeObject(input.Arguments);
        // Before invoking the method on the original target.
        _logger.Log($"{methodDesc} start. Arguments: {args}", LogLevel.Info);

        // Invoke the next behavior in the chain.
        var result = getNext()(input, getNext);

        // After invoking the method on the original target.
        if (result.Exception != null)
        {
            _logger.Log($"{methodDesc} threw exception. {result.Exception.GetBaseException()}", LogLevel.Error);
        }
        else
        {
            _logger.Log($"{methodDesc} end", LogLevel.Info);
        }

        return result;
    }

    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

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

我还自定义了授权过滤器,以下代码在哪里:

public class SayDoAuthorizeAttribute : AuthorizeAttribute
{
    private readonly string[] allowedRoles;

    public SayDoAuthorizeAttribute() : base()
    {
    }

    public SayDoAuthorizeAttribute(params string[] roles)
    {
        allowedRoles = roles;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var userRoles = SayDoAuthorizeHelper.GetRolesAndCacheThem(httpContext);
        bool authorize = false;
        foreach (var role in allowedRoles)
        {
            if (userRoles.Contains(role))
            {
                authorize = true;
                break;
            }
        }
        return authorize;
    }

    ...
}

问题是 UnityConfig.Resolve 在遵循 SayDoAuthorizeHelper.GetRolesAndCacheThem 方法时无法解析 IUserProvider 组件。(我希望这个类是静态的,因为 IsUserInRole 和 IsUserInRoles 方法也用于剃刀视图。

public static class SayDoAuthorizeHelper
{
    public static IEnumerable<string> GetRolesAndCacheThem(HttpContextBase httpContext)
    {
        var nameFromSession = httpContext.Session[Constants.SessionKeyUserPreselectSession];
        var userName = (nameFromSession != null) ? (string)nameFromSession : httpContext.User.Identity.Name;

        var rolesString = (string)httpContext.Cache.Get(userName);
        if (string.IsNullOrEmpty(rolesString))
        {
            var userProvider = UnityConfig.Container.Resolve<IUserProvider>();
            var roles = userProvider.GetUserRolesNames(userName);
            rolesString = string.Join(",", roles);
            //set absolute cache
            var minutesToCacheRoles = double.Parse(ConfigurationManager.AppSettings[Constants.KeyCacheUserRolesInMinutes]);
            httpContext.Cache.Insert(userName, rolesString, null, DateTime.Now.AddMinutes(minutesToCacheRoles), Cache.NoSlidingExpiration);
            return roles;
        }
        return rolesString.Split(',').ToList();
    }

    public static bool IsUserInRole(System.Security.Principal.IPrincipal userPrincipal, string roleName)
    {
        var userRoles = GetRolesAndCacheThem(new HttpContextWrapper(HttpContext.Current));
        return userRoles.Contains(roleName);
    }

    public static bool IsUserInRoles(System.Security.Principal.IPrincipal userPrincipal, string[] roleNames)
    {
        var userRoles = GetRolesAndCacheThem(new HttpContextWrapper(HttpContext.Current));
        foreach (var roleName in roleNames)
        {
            if (userRoles.Contains(roleName))
            {
                return true;
            }
        }
        return false;
    }
}

当我在线上设置断点时 var userProvider = UnityConfig.Container.Resolve(); 然后没有抛出异常,代码仍然继续,但我在局部变量中看到 userProvider 抛出了类型为 'System.IO.FileNotFoundException' Abb.Czopc.SayDo.BusinessLogic.Interface.IUserProvider {System.IO.FileNotFoundException} 的异常消息“无法加载程序集 '15bd37ce9f8a41d9b9916a1ebadc536a'。”。但在输出窗口中,我看到程序集已加载。“iisexpress.exe”(CLR v4.0.30319:/LM/W3SVC/2/ROOT-1-131699797337577440):已加载“15bd37ce9f8a41d9b9916a1ebadc536a”。

当我在没有拦截的情况下在 UnityConfig 中注册时,一切正常。

    container.RegisterType<IUserRepository, UserRepository>();
    container.RegisterType<IUserProvider, UserProvider>();

我也尝试用 VirtualMethodInterceptor 替换 InterfaceInterceptor,但结果相同。任何人都可以帮忙吗?我不知道出了什么问题。感谢帮助。解决方案是用 VS 2015 编写的,nuget 包列表在这里:

  <package id="Abb.Czopc.Common.Log" version="1.0.1" targetFramework="net451" />
  <package id="Abb.Czopc.Common.Log.EntLibLogger" version="1.0.1" targetFramework="net451" />
  <package id="Antlr" version="3.5.0.2" targetFramework="net451" />
  <package id="AutoMapper" version="6.2.2" targetFramework="net451" />
  <package id="EnterpriseLibrary.Common" version="6.0.1304.0" targetFramework="net451" />
  <package id="EnterpriseLibrary.Logging" version="6.0.1304.0" targetFramework="net451" />
  <package id="jQuery" version="3.3.1" targetFramework="net451" />
  <package id="jQuery.Validation" version="1.17.0" targetFramework="net451" />
  <package id="Microsoft.AspNet.Mvc" version="5.2.4" targetFramework="net451" />
  <package id="Microsoft.AspNet.Razor" version="3.2.4" targetFramework="net451" />
  <package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net451" />
  <package id="Microsoft.AspNet.WebPages" version="3.2.4" targetFramework="net451" />
  <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.8" targetFramework="net451" />
  <package id="Microsoft.jQuery.Unobtrusive.Ajax" version="3.2.5" targetFramework="net451" />
  <package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.9" targetFramework="net451" />
  <package id="Microsoft.Net.Compilers" version="2.7.0" targetFramework="net451" developmentDependency="true" />
  <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net451" />
  <package id="Modernizr" version="2.8.3" targetFramework="net451" />
  <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net451" />
  <package id="Respond" version="1.4.2" targetFramework="net451" />
  <package id="Unity.Abstractions" version="3.3.0" targetFramework="net451" />
  <package id="Unity.Container" version="5.8.5" targetFramework="net451" />
  <package id="Unity.Interception" version="5.5.1" targetFramework="net451" />
  <package id="Unity.Mvc" version="5.0.13" targetFramework="net451" />
  <package id="WebActivatorEx" version="2.2.0" targetFramework="net451" />
  <package id="WebGrease" version="1.6.0" targetFramework="net451" />

标签: dependency-injectionunity-containerfilenotfoundexceptionunity-interception

解决方案


推荐阅读