简体   繁体   中英

SimpleInjector and SDammann WebAPI versioning

I try to configer the SimpleInjector container to use it with the SDammann WebAPI Versioning

I have this in my WebAPI config..

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 }
        );
     }
}

My controllers are available in the following folders....

在此处输入图片说明

When i just test the test controller everything works fine --> http:localhost/VDB.Service.WebAPI/Api/Test

Code below

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

When i try it with some other stuff like below then i get an error.... See picture below this code

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();
    }
}

在此处输入图片说明

BTW my Global.asax looks like this...

 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);
    }
}

It could be that this are some basic things. But i am new to Webdevelopment and that stuff. In my daily job i am using BizTalk and that stuff. :) But i want to learn new stuff. :)

EDIT FROM THIS POINT

I now get the following error, but i don't know why...

{
"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()"
}
}
}

My WebApi looks now likes this... Controller is still the same...

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 }
        );
     }
}

SECOND EDIT

 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);
    }
}

EDIT: MVC Application I get now something back when i do a test --> 'PostMan a chrome app' Now i try to do that in an ASP.Net MVC application...

But i get the error...

    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));

The accountController is the default accountcontroller... My Global file looks like this in my MVC application.

Just like the guide on.... , no? 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));

} }

The exception is pretty clear:

The constructor of type FindOilTypesQueryHandler contains the parameter of type IVdbQueryContext with name 'vdbQueryContext' that is not registered. Please ensure IVdbQueryContext is registered in the container, or change the constructor of FindOilTypesQueryHandler.

This means that the IVdbQueryContext isn't registered. It is easy to see why, because you create two Container instances. The second one (in the Application_Start method) contains this IVdbQueryContext registration, but that instance is never used. The first one (in the Register method) is registered as DependencyResolver and therefore used by Web API for resolving your controllers, but this instance does not contain the IVdbQueryContext registration.

Best practice is to have one single container instance per application .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM