【问题标题】:SimpleInjector and SDammann WebAPI versioningSimpleInjector 和 SDamann WebAPI 版本控制
【发布时间】:2015-01-18 21:54:39
【问题描述】:

我尝试配置 SimpleInjector 容器以将其与 SDammann WebAPI 版本控制一起使用

我的 WebAPI 配置中有这个..

public static class WebApiConfig
{
    public sealed class AcceptHeaderRequestVersionDetector : SDammann.WebApi.Versioning.Request.AcceptHeaderRequestVersionDetector 
    {
        protected override ApiVersion GetVersionFromSingleHeader(MediaTypeWithQualityHeaderValue headerValue)
        {
            string rawVersion = headerValue.Parameters.First(x => x.Name == "version").Value;
            int version = Convert.ToInt32(rawVersion);

            return new SemVerApiVersion(
                new Version(version, 0)
            );
        }
    }

    public static void Register(HttpConfiguration config)
    {
        Container container = new Container();
        container.Register<DefaultControllerIdentificationDetector>();
        container.Register<DefaultRequestControllerIdentificationDetector>();
        container.Register<HttpConfiguration>(() => config);

        config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

        container.Verify();

        config.Services.Replace(typeof(IHttpControllerSelector), new VersionedApiControllerSelector(config));

        ApiVersioning.Configure()
                     .ConfigureRequestVersionDetector<AcceptHeaderRequestVersionDetector>();

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
     }
}

我的控制器在以下文件夹中可用....

当我只是测试测试控制器时,一切正常 --> http:localhost/VDB.Service.WebAPI/Api/Test

代码如下

public class TestController : ApiController
{
    [HttpGet]
    public string Version()
    {
        return "Version 1";
    }
}

当我用下面的其他东西尝试它时,我得到一个错误.... 请参阅此代码下方的图片

public class OilTypesController : ApiController
{
    private readonly IRequestHandler<FindOilTypesQuery,FindOilTypesQueryResult>  _findOilTypesQueryHandler;
    private readonly IRequestHandler<CreateOilTypeCommand, CreateOilTypeCommandResult> _createOilTypeCommandHandler;

    public OilTypesController(FindOilTypesQueryHandler findOilTypesQueryHandler, CreateOilTypeCommandHandler createOilTypeCommandHandler)
    {
        _findOilTypesQueryHandler = findOilTypesQueryHandler;
        _createOilTypeCommandHandler = createOilTypeCommandHandler;
    }

    [HttpPost]
    public CreateOilTypeCommandResult CreateOilType(CreateOilTypeCommand command)
    {
        var result = _createOilTypeCommandHandler.Execute(command);
        return result;
    }

    [HttpGet]
    public IQueryable<OilTypeSummaryModel> GetOilTypes(ODataQueryOptions<OilType> oDataQuery)
    {
        var query = new FindOilTypesQuery(oDataQuery);
        return _findOilTypesQueryHandler.Execute(query).OilTypes.ToOilTypeSummaryModel();
    }
}

顺便说一句,我的 Global.asax 看起来像这样......

 public class WebApiApplication : HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        Container container = new Container();
        container.RegisterManyForOpenGeneric(typeof(IRequestHandler<,>), Assembly.GetExecutingAssembly());
        container.Register<IVdbCommandContext, VdbCommandContext>(Lifestyle.Transient);
        container.Register<IVdbQueryContext, VdbQueryContext>(Lifestyle.Transient);

        GlobalConfiguration.Configuration.Filters.Add(new ExceptionHandlerFilter());

        // Force code first migrations to check database and migrate if required
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<VdbCommandContext, Configuration>());

        VdbCommandContext vdbCommandContext = new VdbCommandContext();
        vdbCommandContext.Database.Initialize(true);
    }
}

这可能是一些基本的东西。但我是 Web 开发和那些东西的新手。 在我的日常工作中,我使用 BizTalk 之类的东西。 :) 但我想学习新的东西。 :)

从这里开始编辑

我现在收到以下错误,但我不知道为什么...

{
"Message": "An error has occurred.",
"ExceptionMessage": "An error occurred when trying to create a controller of type 'OilTypesController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\ \ at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\ \ at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "No registration for type OilTypesController could be found and an implicit registration could not be made. The constructor of type OilTypesController contains the parameter of type IRequestHandler<FindOilTypesQuery, FindOilTypesQueryResult> with name 'findOilTypesQueryHandler' that is not registered. Please ensure IRequestHandler<FindOilTypesQuery, FindOilTypesQueryResult> is registered in the container, or change the constructor of OilTypesController.",
"ExceptionType": "SimpleInjector.ActivationException",
"StackTrace": " at SimpleInjector.InstanceProducer.GetInstance()\ \ at SimpleInjector.Container.GetInstance(Type serviceType)\ \ at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "The constructor of type OilTypesController contains the parameter of type IRequestHandler<FindOilTypesQuery, FindOilTypesQueryResult> with name 'findOilTypesQueryHandler' that is not registered. Please ensure IRequestHandler<FindOilTypesQuery, FindOilTypesQueryResult> is registered in the container, or change the constructor of OilTypesController.",
"ExceptionType": "SimpleInjector.ActivationException",
"StackTrace": " at SimpleInjector.Advanced.DefaultConstructorInjectionBehavior.BuildParameterExpression(ParameterInfo parameter)\ \ at SimpleInjector.ContainerOptions.BuildParameterExpression(ParameterInfo parameter)\ \ at SimpleInjector.Registration.BuildConstructorParameters(ConstructorInfo constructor)\ \ at SimpleInjector.Registration.BuildNewExpression(Type serviceType, Type implementationType)\ \ at SimpleInjector.Registration.BuildTransientExpression[TService,TImplementation]()\ \ at SimpleInjector.Registration.BuildExpression(InstanceProducer producer)\ \ at SimpleInjector.InstanceProducer.BuildExpressionInternal()\ \ at System.Lazy`1.CreateValue()\ \ --- End of stack trace from previous location where exception was thrown ---\ \ at SimpleInjector.InstanceProducer.BuildInstanceCreator()\ \ at SimpleInjector.InstanceProducer.GetInstance()"
}
}
}

我的 WebApi 现在看起来像这样... 控制器还是一样的...

public static class WebApiConfig
{
    public sealed class AcceptHeaderRequestVersionDetector : SDammann.WebApi.Versioning.Request.AcceptHeaderRequestVersionDetector 
    {
        protected override ApiVersion GetVersionFromSingleHeader(MediaTypeWithQualityHeaderValue headerValue)
        {
            string rawVersion = headerValue.Parameters.First(x => x.Name == "version").Value;
            int version = Convert.ToInt32(rawVersion);

            return new SemVerApiVersion(
                new Version(version, 0)
            );
        }
    }

    public static void Register(HttpConfiguration config)
    {
        Container container = new Container();
        container.Register<DefaultControllerIdentificationDetector>();
        container.Register<DefaultRequestControllerIdentificationDetector>();
        container.Register(() => config);

        container.RegisterManyForOpenGeneric(typeof(IRequestHandler<,>), Assembly.GetExecutingAssembly());
        container.Register<IVdbCommandContext, VdbCommandContext>();
        container.Register<IVdbQueryContext, VdbQueryContext>();

        GlobalConfiguration.Configuration.Filters.Add(new ExceptionHandlerFilter());

        config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

        container.Verify();

        config.Services.Replace(typeof(IHttpControllerSelector), new VersionedApiControllerSelector(config));

        ApiVersioning.Configure()
                     .ConfigureRequestVersionDetector<AcceptHeaderRequestVersionDetector>();

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
     }
}

第二次编辑

 public class FindOilTypesQueryHandler: IRequestHandler<FindOilTypesQuery,FindOilTypesQueryResult>
{
    private readonly IVdbQueryContext _vdbQueryContext;

    public FindOilTypesQueryHandler(IVdbQueryContext vdbQueryContext)
    {
        _vdbQueryContext = vdbQueryContext;
    }

    public FindOilTypesQueryResult Execute(FindOilTypesQuery request)
    {
        var oilTypes = request.ODataQuery.ApplyTo(_vdbQueryContext.OilTypes).Cast<OilType>();
        return new FindOilTypesQueryResult(oilTypes);
    }
}

编辑:MVC 应用程序 当我进行测试时,我现在得到了一些回报——>'PostMan 一个 chrome 应用程序' 现在我尝试在 ASP.Net MVC 应用程序中执行此操作...

但我得到了错误...

    For the container to be able to create AccountController, it should contain exactly one public constructor, but it has 2.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: SimpleInjector.ActivationException: For the container to be able to create AccountController, it should contain exactly one public constructor, but it has 2.
Source Error: 


Line 27:             container.RegisterMvcIntegratedFilterProvider();
Line 28: 
Line 29:             container.Verify();
Line 30: 
Line 31:             DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

accountController 是默认的 accountcontroller... 我的全局文件在我的 MVC 应用程序中如下所示。

就像...的指南一样,不是吗?https://simpleinjector.readthedocs.org/en/latest/mvcintegration.html

public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            Container container = new Container();
            container.Register<IVdbService, VdbService>();
            container.Register<IRequestExecutor,RequestExecutor>();

            container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
            container.RegisterMvcIntegratedFilterProvider();

            container.Verify();

            DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

} }

【问题讨论】:

  • 请不要在您的问题中添加带有堆栈跟踪的屏幕截图。只需将完整的异常信息(堆栈跟踪、异常消息、类型和内部异常)作为文本粘贴到您的问题中。
  • 在调用验证之前请致电container.RegisterWebApiControllers()。这让您的生活变得更加轻松,因为这将允许 Simple Injector 也可以验证您的控制器,从而允许应用程序在调用 Verify 时崩溃,而不是稍后在应用程序运行时崩溃。跨度>
  • @Steven 我从昨天晚上开始就这样做了......在 StackOverflow 上进行了编辑之后。我在“ExceptionHandlerFilter add”之前进行了调用,当我在验证之前尝试时,我得到一个类似这样的错误“在第一次调用 GetInstance、GetAllInstances 和验证后无法更改容器。”
  • 要了解为什么会出现该异常,请阅读this part of the documentation

标签: c# asp.net-mvc-5 asp.net-web-api simple-injector


【解决方案1】:

异常很明显:

FindOilTypesQueryHandler 类型的构造函数包含 IVdbQueryContext 类型的参数,名称为“vdbQueryContext”,即 未注册。请确保 IVdbQueryContext 已在 容器,或者更改 FindOilTypesQueryHandler 的构造函数。

这意味着IVdbQueryContext 未注册。原因很容易理解,因为您创建了两个 Container 实例。第二个(在Application_Start 方法中)包含此IVdbQueryContext 注册,但从未使用过该实例。第一个(在Register 方法中)注册为DependencyResolver,因此被Web API 用于解析您的控制器,但此实例不包含IVdbQueryContext 注册。

最佳做法是使用one single container instance per application

【讨论】:

  • simpleinjector.readthedocs.org/en/latest/using.html 中,第一个警告块中存在一个指向simpleinjector.codeplex.com/discussions/349908 的链接,但可惜,该链接不再起作用。
  • @Maarten:感谢您报告此事。我会马上解决这个问题。
  • 在 Globals.asax 或 WebAPI 寄存器中添加所有内容更好吗?但是在 Global.asax 我没有“HttpConfiguration”....
  • @Sven:我想你已经回答了自己的问题。
  • @Steven 我又遇到了一些错误。为什么要添加无参数构造函数?
猜你喜欢
  • 2016-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-10
  • 1970-01-01
  • 1970-01-01
  • 2020-06-27
  • 2014-09-24
相关资源
最近更新 更多