【问题标题】:ASP.NET MVC Boilerplate: Migrating from AutoFac to Unity, cannot get Services DI working againASP.NET MVC 样板:从 AutoFac 迁移到 Unity,无法让服务 DI 再次工作
【发布时间】:2018-01-31 05:38:29
【问题描述】:

当前项目:

当我将 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 文件也是样板的默认文件 - 它们完全未修改(为简洁起见删除了 cmets):

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(如 using Unity.AspNet.Mvc;)的情况下,在 UnityConfig.cs 中使用 PerRequestLifetimeManager 的任何尝试都失败了。

UnityConfig.cs 更改为包含Unity.AspNet.Mvc 会导致大量垃圾:虽然我能够在没有明显Intellisense 错误的情况下让PerRequestLifetimeManager 被接受,但UnityMvcActivator.cs 突然无法解析其UnityConfig.Container 条目而没有一个非常奇怪的条目UnityConfig.cs顶部的入口:

public static IUnityContainer Container { get; internal set; }

UnityConfig.cs 中的 SetResolver 需要显式声明 new Unity.Mvc5.UnityDependencyResolver(container) 以免引发 Intellisense 混淆。

另外,运行时出现以下错误:

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

尽管整个解决方案都是在 v5.x 下创建的,但为什么它仍试图针对 Unity 的 v3.x,这导致我的灰质无休止的崩溃。是的,我对整个解决方案以及各个项目进行了全面清理和重建。


编辑 2:

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

所以无论如何,我进行了一次全面刷新,我的UnityConfig.cs 突然发生了重大变化。之前如上,包括 v5 内的所有升级,但刷新为我提供了以下内容(为简洁起见,删除了 cmets):

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() 更改为UnityConfig.RegisterTypes())不会使任何意义——我如何通过容器?

【问题讨论】:

  • when you do an upgrade or reinstall of Unity, it tries to overwrite your unity files - 这是我从不将“适配器包”用于任何 DI 容器的原因之一。通过自己连接实现来保持直截了当要简单得多。我的建议是manually go through the project 并取出任何“旧”Unity 东西并升级到相应的“新”Unity 东西。然后进行“新”配置。
  • 仅供参考 - 我找到了“新”PerRequestLifetimeManager here
  • @NightOwl888 - 是的,我上面提到的Unity.Mvc,以及下面答案的cmets。还是个问题。

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


【解决方案1】:

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

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

不等同于:

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

应该改为

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

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

最后一条错误消息表明您需要在 Unity 中注册 HttpContextBase。你可以通过用HttpContextWrapper 包裹HttpContext.Current 来做到这一点。

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

【讨论】:

  • 呃……。 “当前类型 System.Web.HttpContextBase 是一个抽象类,无法构造。你是否缺少类型映射?”是的,我将你的行复制粘贴到我的 UnityConfig.cs 中。
  • 显然,您还有其他未在问题中发布的问题。如果你将它添加到一个容器并且它不起作用,那么你必须在你的项目中配置多个容器实例(你通常不应该这样做)。
  • “多个容器实例”是什么意思?我所做的只是运行标准存储库模式,解决方案中有多个项目(标准 DDD)。样板文件完全包含在一个解决方案中。
  • 我的意思是你在你的项目中更新了多个UnityContainer,与this example相同。
  • 不,不是这样。用细齿梳子梳理所有东西,任何地方都没有。这不是一个大项目。
猜你喜欢
  • 2020-06-09
  • 1970-01-01
  • 2018-06-08
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
相关资源
最近更新 更多