Howto get injection working with Topshelf, Autofac and self hosted Owin - asp.net-web-api2

No matter what i do i cannot get injection working with Topshelf, Autofac and self hosted Owin.
I have followed the documentation in http://autofac.readthedocs.org/en/latest/integration/webapi.html#owin-integration and read Autofac WebApi 2 OWIN Not Working, but I am still unable to inject a simple class into my apicontroller.
The 'almost' complete app is posted here.
No matter what i do I cannot get an IEmail instance injected into the EmailController. Can anyone suggest a solution
// topshelf startup code
class Program
{
static void Main(string[] args)
{
HostFactory.Run(c =>
{
//c.UseAutofacContainer(container);
c.RunAsNetworkService();
c.Service<SampleService>(s =>
{
s.ConstructUsing(name => new SampleService());
s.WhenStarted((service, control) => service.Start());
s.WhenStopped((service, control) => service.Stop());
});
});
}
}
// lifted from http://autofac.readthedocs.org/en/latest/integration/webapi.html#owin-integration
public class StartupConfig
{
public void Configure(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes(); // using attribute based routing because I prefer it
var builder = new Autofac.ContainerBuilder(); // Create the container builder.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); // Register the Web API controllers.
builder.RegisterWebApiFilterProvider(config); // optional
builder.RegisterType<Email>().As<IEmail>().InstancePerRequest();
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(config); // Make sure the Autofac lifetime scope is passed to Web API.
appBuilder.UseWebApi(config); // enable web-api
string filedir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "../../web");
appBuilder.UseFileServer(new FileServerOptions
{
EnableDefaultFiles = true,
DefaultFilesOptions =
{
DefaultFileNames = new[] { "Index.html" }
},
EnableDirectoryBrowsing = true,
FileSystem = new PhysicalFileSystem(filedir),
});
}
}
// topshelf hosted service to start
public class SampleService
{
public bool Start()
{
if (WebApplication == null)
{
WebApplication = WebApp.Start
(
new StartOptions
{
Port = 1234
},
appBuilder =>
{
new StartupConfig().Configure(appBuilder);
}
);
}
return true;
}
public bool Stop()
{
return true;
}
protected IDisposable WebApplication
{
get;
set;
}
}
// sample controller
public class EmailController : ApiController
{
public IEmail MyModel; /** always NULL **/
[HttpGet]
[Route("api/emails/{id}")]
public IHttpActionResult get(int id)
{
}
}
Nuget packages
<packages>
<package id="Autofac" version="3.5.0" targetFramework="net452" />
<package id="Autofac.Owin" version="3.1.0" targetFramework="net452" />
<package id="Autofac.WebApi" version="3.1.0" targetFramework="net452" />
<package id="Autofac.WebApi2" version="3.4.0" targetFramework="net452" />
<package id="Autofac.WebApi2.Owin" version="3.3.0" targetFramework="net452" />
<package id="EntityFramework" version="6.0.0" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.2" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.2" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.2" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.FileSystems" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Hosting" version="3.0.0" targetFramework="net45" />
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="Topshelf" version="3.1.4" targetFramework="net45" />
<package id="Topshelf.Autofac" version="1.0.0" targetFramework="net452" />
</packages>

In order to inject into the apicontroller here
public class EmailController : ApiController
{
public IEmail MyModel; // <---
[HttpGet]
[Route("api/emails/{id}")]
public IHttpActionResult get(int id)
{
}
}
I was missing [PropertiesAutowired] when registering the api controller builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();

You also dont have a constructor for your Api Controller. Once you have constructor, the service will resolve itself if it is in the Container.
public class EmailController : ApiController
{
public IEmail MyModel;
public EmailController(IEmail MyModel){
this.MyModel = MyModel;
}
[HttpGet]
[Route("api/emails/{id}")]
public IHttpActionResult get(int id)
{
}
}

Related

Unresolved DI in aspnet core with lightinject

Apologies for the long text!
I'm trying to build a message listener for RabbitMQ in aspnet core 2.1. As soon as I post a message, I get this error in the log:
2018-09-29 12:35:35.459 INFO NServiceBus.RecoverabilityExecutor
Immediate Retry is going to retry message 'ab43' because of an
exception:
System.InvalidOperationException: Unable to resolve type:
Event.Processor.Listener.CustomerReceivedHandler, service name: --->
System.InvalidOperationException: Unresolved dependency
csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>Event.Processor</AssemblyName>
<RootNamespace>Event.Processor</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Customer.Models" Version="1.0.21875" />
<PackageReference Include="lightinject" Version="5.2.0" />
<PackageReference Include="LightInject.Microsoft.DependencyInjection" Version="2.0.8" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="nservicebus" Version="7.1.4" />
<PackageReference Include="NServiceBus.RabbitMQ" Version="5.0.1" />
<PackageReference Include="odp.netcore" Version="2.0.12" />
<PackageReference Include="serilog" Version="2.7.1" />
<PackageReference Include="Serilog.aspnetcore" Version="2.1.1" />
<PackageReference Include="serilog.settings.configuration" Version="2.6.1" />
<PackageReference Include="serilog.sinks.console" Version="3.1.1" />
<PackageReference Include="serilog.sinks.file" Version="4.0.0" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
app.Run(async context =>
{
await context.Response.WriteAsync("Hello Processor");
});
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
ServiceContainer container = new ServiceContainer(new ContainerOptions
{
EnablePropertyInjection = false
});
//The below didn't work either!
//services.AddSingleton<IDbProvider, DbProvider>();
//services.AddSingleton<IConfigSettings, ConfigSettings>();
//services.AddSingleton<IEncryptor, Encryptor>();
container.Register<IDbProvider, DbProvider>();
container.Register<IConfigSettings, ConfigSettings>();
container.Register<IEncryptor, Encryptor>();
return container.CreateServiceProvider(services);
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
try
{
var endpointConfiguration = new EndpointConfiguration("MSG_QUEUE");
var transport = endpointConfiguration.UseTransport<RabbitMQTransport>();
transport.UseConventionalRoutingTopology();
transport.ConnectionString("ConxnString");
endpointConfiguration.EnableInstallers();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.AutoSubscribe();
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.UseSerialization<XmlSerializer>();
Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
//IEndpointInstance endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
//endpointInstance.Stop().ConfigureAwait(false);
//Log.Information("Starting web host");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
//Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
//Log.CloseAndFlush();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
MessageHandler.cs
public class CustomerReceivedHandler : IHandleMessages<PubSubObject>
{
private readonly IDbProvider _DbProvider;
private static readonly ILog Log = LogManager.GetLogger<CustomerReceivedHandler>();
public CustomerReceivedHandler(IDbProvider DbProvider)
{
_DbProvider = DbProvider;
// If I don't inject and initialize as below, it works fine
//_DbProvider = new DbProvider(new ConfigSettings(new Encryptor());
}
public Task Handle(PubSubObject message, IMessageHandlerContext context)
{
Log.Info($"Received message with id {context.MessageId}");
}
}
Apparently I should've used the below code:
endpointConfiguration.UseContainer<ServicesBuilder>(
customizations: customizations =>
{
customizations.ExistingServices(services);
});
Working after I followed: https://docs.particular.net/samples/dependency-injection/aspnetcore/

OData v4 error using filter with groupby

I'm getting an error when using $filter with $apply=groupby. It appears to happen only when the filter field is not in the groupby expression. Here is the error message:
Instance property 'DRG_Definition' is not defined for type 'DynamicTypeWrapper'
This works fine:
http://localhost:9810/odata/PAYMENTS?$apply=groupby((Provider_Id,DRG_Definition),aggregate(Total_Payments with sum as Total_Payments))&$filter=(DRG_Definition eq '069 - TRANSIENT ISCHEMIA')
This throws the error (only difference is no DRG_Definition field in the groupby):
http://localhost:9810/odata/PAYMENTS?$apply=groupby((Provider_Id),aggregate(Total_Payments with sum as Total_Payments))&$filter=(DRG_Definition eq '069 - TRANSIENT ISCHEMIA')
Updated with my packages and code samples below:
<packages>
<package id="EntityFramework" version="6.1.3" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="1.2.1" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights.JavaScript" version="0.15.0-build58334" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights.Web" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights.WindowsServer" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.1.0" targetFramework="net452" />
<package id="Microsoft.AspNet.OData" version="5.9.1" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.1" targetFramework="net452" />
<package id="Microsoft.Net.Compilers" version="1.3.2" targetFramework="net452" developmentDependency="true" />
<package id="Microsoft.OData.Core" version="6.15.0" targetFramework="net452" />
<package id="Microsoft.OData.Edm" version="6.15.0" targetFramework="net452" />
<package id="Microsoft.Spatial" version="6.15.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="System.Spatial" version="5.7.0" targetFramework="net452" />
</packages>
Here's the WebApiConfig.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using HealthcareWebApp;
namespace HealthcareWebApp
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//Custom code
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<PAYMENTS>("PAYMENTS"); //.EntityType.HasKey(p => p.PAYMENT_KEY);
builder.EntitySet<DATE_DIM>("DATE_DIM"); //.EntityType.HasKey(p => p.Year);
builder.EntitySet<PROVIDERS>("PROVIDERS"); //.EntityType.HasKey(p => p.Provider_Id);
config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
}
}
}
PAYMENTSController.cs:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.ModelBinding;
using System.Web.OData;
using System.Web.OData.Query;
using System.Web.OData.Routing;
using HealthcareWebApp;
namespace HealthcareWebApp.Controllers
{
/*
The WebApiConfig class may require additional changes to add a route for this controller. Merge these statements into the Register method of the WebApiConfig class as applicable. Note that OData URLs are case sensitive.
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using HealthcareWebApp;
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<PAYMENTS>("PAYMENTS");
builder.EntitySet<DATE_DIM>("DATE_DIM");
builder.EntitySet<PROVIDERS>("PROVIDERS");
config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
*/
public class PAYMENTSController : ODataController
{
private FlexIT_HealthcareEntities db = new FlexIT_HealthcareEntities();
// GET: odata/PAYMENTS
[EnableQuery]
public IQueryable<PAYMENTS> GetPAYMENTS()
{
return db.PAYMENTS;
}
// GET: odata/PAYMENTS(5)
[EnableQuery]
public SingleResult<PAYMENTS> GetPAYMENTS([FromODataUri] int key)
{
return SingleResult.Create(db.PAYMENTS.Where(pAYMENTS => pAYMENTS.PAYMENT_KEY == key));
}
// PUT: odata/PAYMENTS(5)
public IHttpActionResult Put([FromODataUri] int key, Delta<PAYMENTS> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
PAYMENTS pAYMENTS = db.PAYMENTS.Find(key);
if (pAYMENTS == null)
{
return NotFound();
}
patch.Put(pAYMENTS);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!PAYMENTSExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(pAYMENTS);
}
// POST: odata/PAYMENTS
public IHttpActionResult Post(PAYMENTS pAYMENTS)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.PAYMENTS.Add(pAYMENTS);
db.SaveChanges();
return Created(pAYMENTS);
}
// PATCH: odata/PAYMENTS(5)
[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] int key, Delta<PAYMENTS> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
PAYMENTS pAYMENTS = db.PAYMENTS.Find(key);
if (pAYMENTS == null)
{
return NotFound();
}
patch.Patch(pAYMENTS);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!PAYMENTSExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(pAYMENTS);
}
// DELETE: odata/PAYMENTS(5)
public IHttpActionResult Delete([FromODataUri] int key)
{
PAYMENTS pAYMENTS = db.PAYMENTS.Find(key);
if (pAYMENTS == null)
{
return NotFound();
}
db.PAYMENTS.Remove(pAYMENTS);
db.SaveChanges();
return StatusCode(HttpStatusCode.NoContent);
}
// GET: odata/PAYMENTS(5)/DATE_DIM
[EnableQuery]
public SingleResult<DATE_DIM> GetDATE_DIM([FromODataUri] int key)
{
return SingleResult.Create(db.PAYMENTS.Where(m => m.PAYMENT_KEY == key).Select(m => m.DATE_DIM));
}
// GET: odata/PAYMENTS(5)/PROVIDERS
[EnableQuery]
public SingleResult<PROVIDERS> GetPROVIDERS([FromODataUri] int key)
{
return SingleResult.Create(db.PAYMENTS.Where(m => m.PAYMENT_KEY == key).Select(m => m.PROVIDERS));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool PAYMENTSExists(int key)
{
return db.PAYMENTS.Count(e => e.PAYMENT_KEY == key) > 0;
}
}
}
Lastly, the PAYMENTS.cs model:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace HealthcareWebApp
{
using System;
using System.Collections.Generic;
public partial class PAYMENTS
{
[System.ComponentModel.DataAnnotations.Key] //manually added by ataft
public int PAYMENT_KEY { get; set; }
public string DRG_Definition { get; set; }
public string Provider_Id { get; set; }
public string Hospital_Referral_Region_Description { get; set; }
public Nullable<decimal> Total_Discharges_ { get; set; }
public Nullable<decimal> Covered_Charges { get; set; }
public Nullable<decimal> Total_Payments { get; set; }
public Nullable<decimal> Medicare_Payments { get; set; }
public int Year { get; set; }
public virtual DATE_DIM DATE_DIM { get; set; }
public virtual PROVIDERS PROVIDERS { get; set; }
}
}
It's an issue about filter and groupby is filter can't apply to groupby or aggregated property and resolve in WebAPI/OData 5.9.1.
https://www.nuget.org/packages/Microsoft.AspNet.OData/5.9.1
And in your scenario, apply will always executed first and then filter get executed, so when $apply=groupby((Provider_Id),aggregate(Total_Payments with sum as Total_Payments)), the result won't contain DRG_Definition, so the filter failed, if you want to filter first, you should use filter in apply, like $apply=filter(Name eq 'Lowest')/groupby((Name))
FYI the spec http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/odata-data-aggregation-ext-v4.0.html

Web API 2 HTTP module EndRequest event being fired first?

I'm trying to implement the NHibernate session management/repository pattern code found here related to the implementation I originally read on the NHibernate Forge page about effective session management.
I am using ASP.NET Web API 2 and am having issues with the HTTPModule events. When I run the app with a simple Home/Index action, I get a System.Collections.Generic.KeyNotFoundException error in the UnBind method.
When I debug, BeginRequest is never called, and somehow EndRequest is falling through. I believe this is where the error is coming from.
Am I missing something obvious? Why is EndRequest being called before anything else?
LazySessionContext and HTTPModule:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate;
using NHibernate.Context;
using NHibernate.Engine;
namespace MyService.Service.Infrastructure.SessionManagement
{
//Is up to you to:
//-set the currentsessioncontextclass in nhibernate as follows:
// configuration.Properties[Environment.CurrentSessionContextClass]
// = typeof (LazySessionContext).AssemblyQualifiedName;
//-implement ISessionFactoryProvider or use Castle Typed Factories:
// container.Register(Component.For<ISessionFactoryProvider>().AsFactory());
//-load the SessionFactoryProvider in the HttpApplication as follows:
// HttpContext.Current.Application[SessionFactoryProvider.Key]
// = your instance of ISessionFactoryProvider;
//-inject ISessionFactory in Daos and use GetCurrentSessionContext()
public class LazySessionContext : ICurrentSessionContext
{
private readonly ISessionFactoryImplementor factory;
private const string CurrentSessionContextKey = "NHibernateCurrentSession";
public LazySessionContext(ISessionFactoryImplementor factory)
{
this.factory = factory;
}
/// <summary>
/// Retrieve the current session for the session factory.
/// </summary>
/// <returns></returns>
public ISession CurrentSession()
{
Lazy<ISession> initializer;
var currentSessionFactoryMap = GetCurrentFactoryMap();
if (currentSessionFactoryMap == null ||
!currentSessionFactoryMap.TryGetValue(factory, out initializer))
{
return null;
}
return initializer.Value;
}
/// <summary>
/// Bind a new sessionInitializer to the context of the sessionFactory.
/// </summary>
/// <param name="sessionInitializer"></param>
/// <param name="sessionFactory"></param>
public static void Bind(Lazy<ISession> sessionInitializer, ISessionFactory sessionFactory)
{
var map = GetCurrentFactoryMap();
map[sessionFactory] = sessionInitializer;
}
/// <summary>
/// Unbind the current session of the session factory.
/// </summary>
/// <param name="sessionFactory"></param>
/// <returns></returns>
public static ISession UnBind(ISessionFactory sessionFactory)
{
var map = GetCurrentFactoryMap();
var sessionInitializer = map[sessionFactory];
map[sessionFactory] = null;
if (sessionInitializer == null || !sessionInitializer.IsValueCreated) return null;
return sessionInitializer.Value;
}
/// <summary>
/// Provides the CurrentMap of SessionFactories.
/// If there is no map create/store and return a new one.
/// </summary>
/// <returns></returns>
private static IDictionary<ISessionFactory, Lazy<ISession>> GetCurrentFactoryMap()
{
var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<ISession>>)
HttpContext.Current.Items[CurrentSessionContextKey];
if (currentFactoryMap == null)
{
currentFactoryMap = new Dictionary<ISessionFactory, Lazy<ISession>>();
HttpContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap;
}
return currentFactoryMap;
}
}
public interface ISessionFactoryProvider
{
IEnumerable<ISessionFactory> GetSessionFactories();
}
public class SessionFactoryProvider
{
public const string Key = "NHibernateSessionFactoryProvider";
}
public class NHibernateSessionModule : IHttpModule
{
private HttpApplication app;
public void Init(HttpApplication context)
{
app = context;
context.BeginRequest += ContextBeginRequest;
context.EndRequest += ContextEndRequest;
context.Error += ContextError;
}
private void ContextBeginRequest(object sender, EventArgs e)
{
var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key];
foreach (var sf in sfp.GetSessionFactories())
{
var localFactory = sf;
LazySessionContext.Bind(
new Lazy<ISession>(() => BeginSession(localFactory)),
sf);
}
}
private static ISession BeginSession(ISessionFactory sf)
{
var session = sf.OpenSession();
session.BeginTransaction();
return session;
}
private void ContextEndRequest(object sender, EventArgs e)
{
var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key];
var sessionsToEnd = sfp.GetSessionFactories()
.Select(LazySessionContext.UnBind)
.Where(session => session != null);
foreach (var session in sessionsToEnd)
{
EndSession(session);
}
}
private void ContextError(object sender, EventArgs e)
{
var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key];
var sessionstoAbort = sfp.GetSessionFactories()
.Select(LazySessionContext.UnBind)
.Where(session => session != null);
foreach (var session in sessionstoAbort)
{
EndSession(session, true);
}
}
private static void EndSession(ISession session, bool abort = false)
{
if (session.Transaction != null && session.Transaction.IsActive)
{
if (abort)
{
session.Transaction.Rollback();
}
else
{
session.Transaction.Commit();
}
}
session.Dispose();
}
public void Dispose()
{
app.BeginRequest -= ContextBeginRequest;
app.EndRequest -= ContextEndRequest;
app.Error -= ContextError;
}
}
}
Web.config with HttpModule registered:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<httpModules>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
<add name="SessionPerRequest" type="MyService.Service.Infrastructure.SessionManagement.NHibernateSessionModule, MyService.Service" />
</httpModules>
</system.web>
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<modules runAllManagedModulesForAllRequests="true">
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
<add name="SessionPerRequest" type="MyService.Service.Infrastructure.SessionManagement.NHibernateSessionModule, MyService.Service" />
</modules>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Turns out I only needed to register the HttpModules in the system.webServer node and not system.Web.

ServiceStack service metadata shows no operations

I am using ServiceStack for the first time on a brand-new project that started off as a ASP.NET MVC. I am hosting ServiceStack API at the root, so my web.config looks like this:
<system.web>
<httpHandlers>
<add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<add path="*" name="ServiceStack.Factory" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />
</handlers>
</system.webServer>
and my App_Start/RouteConfig.cs:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("*");
}
Here's my service:
[Route("/catalogs/{ID}")]
[DataContract]
public class CatalogRequest : IReturn<Catalog>
{
[DataMember]
public int ID { get; set; }
}
[DefaultView("Catalogs")]
public class CatalogService : Service
{
public object Get(CatalogRequest request)
{
return (request.ID == 9999) ? new Catalog() { ID = 9999 } : null;
}
}
I use the following for testing:
public class TestAppHost : AppHostHttpListenerBase
{
public TestAppHost() : base("TestService", typeof(TestAppHost).Assembly) { }
public override void Configure(Funq.Container container)
{
IoC.Configure(container); // IoC is where all Funq configuration is done
}
}
In my VisualStudio unit test I start up the AppHost like this:
[TestClass]
public class TestHelper
{
public const string TEST_HOST_URL = "http://127.0.0.1:8888/";
private static TestAppHost __AppHost;
[AssemblyInitialize]
public static void Initialize(TestContext context)
{
// Start the test app host.
__AppHost = new TestAppHost();
__AppHost.Init();
__AppHost.Start(TEST_HOST_URL);
}
[AssemblyCleanup]
public static void Cleanup()
{
__AppHost.Dispose();
__AppHost = null;
}
}
When I run my test:
[TestMethod]
public void RequestCatalogByID()
{
var client = new JsonServiceClient(TestHelper.TEST_HOST_URL);
var request = new CatalogRequest() { ID = 9999 };
var response = client.Get(request);
Assert.IsNotNull(response);
}
I get a "Not Found" exception even though the URL seems to be correct: http://127.0.0.1:8888/catalogs/9999.
Pointing the browser to http://127.0.0.1:8888/metadata shows the metadata page with no operations.
What am I doing wrong?
Note the assembly you pass in your AppHost Base constructor should be where all your service implementations are (i.e. not your AppHost), so try instead:
public class TestAppHost : AppHostHttpListenerBase
{
public TestAppHost() : base("TestService", typeof(CatalogService).Assembly){}
...
}

Handlers not auto-subscribing in latest NSB 3.0

My event handlers are not auto-subscribing. I'm using NServiceBus-CI 3.0.2044. I had the same issue with NServiceBus-CI 3.0.2027.
<MsmqTransportConfig ErrorQueue="dwh.projectmanagement.documents.notifications.error" NumberOfWorkerThreads="1" MaxRetries="5" />
<UnicastBusConfig ForwardReceivedMessagesTo="dwh.admin.auditor">
<MessageEndpointMappings>
<add Messages="DWH.Login.EmployeeLoggedInToTIPS, DWH.Events" Endpoint="dwh.webeventpublisher" />
<add Messages="DWH.Login.BuyerLoggedIn, DWH.Events" Endpoint="dwh.webeventpublisher" />
<add Messages="DWH.ProjectManagement.Events" Endpoint="dwh.projectmanagement.commandhandlers" />
<add Messages="DWH.ProjectManagement.Documents.DocumentDistributor.Events" Endpoint="dwh.projectmanagement.documents.documentdistributor" />
</MessageEndpointMappings>
</UnicastBusConfig>
class EndpointConfig : IConfigureThisEndpoint, AsA_Server , IWantCustomInitialization
{
public void Init()
{
log4net.Config.XmlConfigurator.Configure();
var kernel = new StandardKernel();
Configure.With()
.NinjectBuilder(kernel)
.CustomJsonSerializer();
.MsmqTransport()
.IsTransactional(true)
.UnicastBus()
.LoadMessageHandlers()
.CreateBus()
.Start();
// Other DI bindings
}
}
DocumentVersionSignedByBuyer is in the DWH.ProjectManagement.Events assembly.
public class SalesConsultantNotification :
IHandleMessages<DocumentVersionSignedByBuyer>
{
public IBus Bus { get; set; }
private readonly ISalesQueries _salesQueries;
public SalesConsultantNotification(ISalesQueries salesQueries)
{
_salesQueries = salesQueries;
}
public void Handle(DocumentVersionSignedByBuyer message)
{
var salesConsultants = _salesQueries.GetSalesConsultants(message.SaleId);
foreach (var salesConsultant in salesConsultants)
{
var cmd = new NotifySalesConsultantBuyerSigned(salesConsultant, message);
Bus.Send(cmd);
}
}
}