ASP.NET MVC Boilerplate:从AutoFac迁移到Unity,无法再使服务DI工作

时间:2018-01-26 07:13:04

标签: dependency-injection asp.net-mvc-5 autofac

当前项目:

当我将DI从AutoFac切换到Unity时,我无法将内置在样板(robots.txt,sitemap.xml)中的服务备份并运行。特别是,我无法将这些服务的Autofac条目转换为相应的Unity条目。

我的HomeController默认构造函数与默认值相同,至少对于robots.txt,我正在进行石蕊测试:

private readonly IRobotsService _robotsService;
public HomeController(IRobotsService robotsService) {
    _robotsService = robotsService;
}

我的HomeController中的robots.txt方法同样是样板的默认方法:

[NoTrailingSlash]
[OutputCache(CacheProfile = CacheProfileName.RobotsText)]
[Route("robots.txt", Name = HomeControllerRoute.GetRobotsText)]
public ContentResult RobotsText() {
    Trace.WriteLine($"robots.txt requested. User Agent:<{Request.Headers.Get("User-Agent")}>.");
    var content = _robotsService.GetRobotsText();
    return Content(content, ContentType.Text, Encoding.UTF8);
}

IRobotsServiceRobotsService文件也是样板的默认文件 - 它们完全未经修改(除了为简洁起见删除注释):

namespace Project.Website.Services {
    public interface IRobotsService {
        string GetRobotsText();
    }
}

namespace Project.Website.Services {
    using Boilerplate.Web.Mvc;
    using Constants;
    using System.Text;
    using System.Web.Mvc;

    public sealed class RobotsService : IRobotsService {
        private readonly UrlHelper _urlHelper;
        public RobotsService(UrlHelper urlHelper) => _urlHelper = urlHelper;

        public string GetRobotsText() {
            var stringBuilder = new StringBuilder();
            stringBuilder.AppendLine("user-agent: *");
            stringBuilder.AppendLine("disallow: /error/");
            stringBuilder.Append("sitemap: ");
            // Commented out so it wouldn't trigger the sitemap, which is not active:
            //stringBuilder.AppendLine(_urlHelper.AbsoluteRouteUrl(HomeControllerRoute.GetSitemapXml).TrimEnd('/'));
            return stringBuilder.ToString();
        }
    }
}

Autofac的原始Startup.Container.cs非常广泛,但robots.txt服务是通过以下方式注入的:

builder.RegisterType<RobotsService>().As<IRobotsService>().InstancePerRequest();

当我的UnityConfig.cs文件包含以下内容时:

container.RegisterType<RobotsService>(new TransientLifetimeManager());

我得到了

  

当前类型JCI_Vernon.Website.Services.IRobotsService是一个接口,无法构造。你错过了类型映射吗?

其中很好地告诉我,我必须包含IRobotsService,但是当我的UnityConfig文件包含以下内容时:

container.RegisterType<IRobotsService, RobotsService>(new TransientLifetimeManager());

我得到了

  

当前类型System.Web.HttpContextBase是一个抽象类,无法构造。你错过了类型映射吗?

我不确定我的错误在哪里,因为我项目中的所有其他Unity DI都是使用这两种变体之一配置的。

非常感谢任何协助。

编辑:包括我的主项目(可见网站)中的Unity文件。

UnityMvcActivator.cs:

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(JCI_Vernon.Website.UnityMvcActivator), nameof(JCI_Vernon.Website.UnityMvcActivator.Start))]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(JCI_Vernon.Website.UnityMvcActivator), nameof(JCI_Vernon.Website.UnityMvcActivator.Shutdown))]

namespace JCI_Vernon.Website {
  using System.Linq;
  using System.Web.Mvc;
  using Unity.AspNet.Mvc;

  /// <summary>
  /// Provides the bootstrapping for integrating Unity with ASP.NET MVC.
  /// </summary>
  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();
    }
  }
}

UnityConfig.cs:

namespace JCI_Vernon.Website {
  using Data;
  using Domain;
  using Identity;
  using Microsoft.AspNet.Identity;
  using Services;
  using Store;
  using System;
  using System.Web;
  using System.Web.Mvc;
  using Unity;
  using Unity.Injection;
  using Unity.Lifetime;
  using Unity.Mvc5;

  public static class UnityConfig {
    public static IUnityContainer Container { get; internal set; }

    public static void RegisterComponents() {
      var container = new UnityContainer();

      container.RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager(), new InjectionConstructor("DefaultConnection"));
      container.RegisterType<IUserStore<IdentityUser, Guid>, UserStore>(new TransientLifetimeManager());
      container.RegisterType<RoleStore>(new TransientLifetimeManager());

      container.RegisterInstance<HttpContextBase>(new HttpContextWrapper(HttpContext.Current), new TransientLifetimeManager());

      container.RegisterType<IRobotsService, RobotsService>(new Unity.AspNet.Mvc.PerRequestLifetimeManager());
      //container.RegisterType<ISitemapService, SitemapService>(new InjectionConstructor());
      //container.RegisterType<ISitemapPingerService, SitemapPingerService>(new InjectionConstructor());

      DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    }
  }
}

在我的UnityMvcActivator.cs中,我有一条PerRequestLifetimeManager行评论和取消评论每次更改,没有观察到任何差异。在没有Unity.Mvc(作为PerRequestLifetimeManager)的UnityConfig.cs内使用using Unity.AspNet.Mvc;的任何尝试均失败。

更改UnityConfig.cs以包含Unity.AspNet.Mvc会导致大量开启:虽然我能够在没有明显智能感知错误的情况下接受PerRequestLifetimeManager,但UnityMvcActivator.cs突然无法解决UnityConfig.Container顶部没有奇怪条目的UnityConfig.cs个条目:

public static IUnityContainer Container { get; internal set; }

UnityConfig.cs中的SetResolver需要明确说明new Unity.Mvc5.UnityDependencyResolver(container),以免触发智能感知混淆。

另外,运行时,发生以下错误:

  

无法加载文件或程序集'Unity.Abstractions,Version = 3.1.3.0,Culture = neutral,PublicKeyToken = 6d32ff45e0ccc69f'或其中一个依赖项。定位的程序集的清单定义与程序集引用不匹配。 (HRESULT异常:0x80131040)

尽管整个解决方案都是在v5.x下创建的,但为什么它试图瞄准Unity的v3.x会导致我的灰色事件无法完全崩溃。是的,我完全清理并重建了整个解决方案,以及个别项目。

编辑2:

可能会遇到一个有趣的皱纹。在云雀,我决定完全重新安装所有NuGet包,刷新各种各样的。当然,当您升级或重新安装Unity时,它会尝试覆盖您的统一文件,这就是为什么您总是需要备份UnityConfig.cs,否则您的注册将会消失。每一个都发生在我身上。单。 ˚F##他妈的。项目。太烦人了。

所以无论如何,我做了一个完整的刷新,我的UnityConfig.cs突然发生了重大变化。在上面之前,包括 v5中的所有升级,但刷新为我提供了以下内容(为简洁起见删除了注释):

namespace JCI_Vernon.Website {
  using System;
  using Unity;

  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) {
      // TODO: Register your type's mappings here.
      // container.RegisterType<IProductRepository, ProductRepository>();
    }
  }
}
雅,很奇怪。重大变化,不知道为什么。旧版本工作得很好,它只是在这个帖子的特定类型映射中吹出它的cookie。

另外,我必须知道如何更改Global.cs条目以加载我的类型映射,只需使用明显的(更改UnityConfig.RegisterComponents(),无法找到){{1 }}没有任何意义 - 我如何传入容器?

1 个答案:

答案 0 :(得分:0)

这里有几个问题。首先,这一行:

container.RegisterType<RobotsService>(new TransientLifetimeManager());

不等于:

builder.RegisterType<RobotsService>().As<IRobotsService>().InstancePerRequest();

它应该而不是

container.RegisterType<IRobotsService, RobotsService>(new TransientLifetimeManager());
  

请记住,Autofac类型映射首先使用具体类型,然后使用接口类型。这是大多数其他DI容器的落后。

最后一条错误消息表明您需要向Unity注册HttpContextBase。您可以将HttpContext.CurrentHttpContextWrapper一起打包。

container.RegisterInstance<HttpContextBase>(new HttpContextWrapper(HttpContext.Current), new TransientLifetimeManager());