Can't find OwinContext in NancyContext - c#

I have a self hosted Owin application that uses Nancy. In one of the NancyModules I need to get an instance of IOwinContext.
This question touches on the subject, but there's no solution in it: Get current owin context in self host mode
It says that for Nancy, you have to use NancyContext to get to the Items dictionary and look for the value corresponding to the key "OWIN_REQUEST_ENVIRONMENT".
I do have access to the NancyContext and I can see the Items dictionary and that it contains a key called "OWIN_REQUEST_ENVIRONMENT". (I could also call the NancyContext.GetOwinEnvironment() extension, which gives the same result
However, when I get that key it doesn't contain an actual IOwinContext.
It contains a lot of keys with information about Owin (some of the keys are owin.RequestPath, owin.RequestMethod, owin.CallCancelled, and more), but not an actual context object. And it is only really a dictionary with various keys, so I can't cast it to an IOwinContext either.
How can I get from a NancyContext to an IOwinContext object?
public class MyStartup
{
public void Start()
{
var options = new StartOptions()
options.Urls.Add(new Uri("http://*:8084"));
options.AppStartup(this.GetType().AssemblyQualifiedName;
var host = WebApp.Start(options, Configuration);
}
public void Configuration(IAppBuilder app)
{
app.UseNancy();
}
}
public class MyModule : NancyModule
{
Get["/", true] = async(x, ct) =>
{
var owinEnvironment = Context.GetOwinEnvironment();
// Now what?
}
}

var owinContext = new OwinContext(Context.GetOwinEnvironment());
example:
public class SecurityApi : NancyModule
{
public SecurityApi()
{
Post["api/admin/register", true] = async (_, ct) =>
{
var body = this.Bind<RegisterUserBody>();
var owinContext = new OwinContext(Context.GetOwinEnvironment());
var userManager = owinContext.GetUserManager<ApplicationUserManager>();
var user = new User {Id = Guid.NewGuid().ToString(), UserName = body.UserName, Email = body.Email};
var result = await userManager.CreateAsync(user, body.Password);
if (!result.Succeeded)
{
return this.BadRequest(string.Join(Environment.NewLine, result.Errors));
}
return HttpStatusCode.OK;
};
}
}

Actually, question that you mentioned has some tips that you probably missed.
For Nancy, you have to use NancyContext to get to the Items dictionary
and look for the value corresponding to the key
"OWIN_REQUEST_ENVIRONMENT". For SignalR, Environment property of
IRequest gives you access to OWIN environment. Once you have the OWIN
environment, you can create a new OwinContext using the environment.
So, once you called var owinEnvironment = Context.GetOwinEnvironment() and got the dictionary then you can create OwinContext (which is just wrapper for these dictionary values)
It has a constructor OwinContext(IDictionary<String, Object>) which, i guess, is what you need.
Also, you can get OwinContext from HttpContext:
// get owin context
var owinContext = HttpContext.Current.GetOwinContext();
// get user manager
var userManager = owinContext.GetUserManager<YourUserManager>();

I ended up solving this by creating new Owin middleware. In the middleware you have access to the current Owin context, which gives you access to the Owin environment.
When you have access to the Owin environment it's simply a case of adding the Owin context to the environment. When the context is in the environment you can retrieve it in the NancyModule.
After retrieving it like this I also had access to the GetUserManager() method on the context so that I could get my AspNetIdentity manager (as I mentioned in a comment to another answer). Just remember that the middleware must be added before Nancy to the Owin pipeline.
Startup
public class Startup
{
public void Start()
{
var options = new StartOptions()
options.Urls.Add(new Uri("http://*:8084"));
options.AppStartup(this.GetType().AssemblyQualifiedName;
var host = WebApp.Start(options, Configuration);
}
public void Configuration(IAppBuilder app)
{
app.Use(typeof(OwinContextMiddleware));
app.UseNancy();
}
}
Middleware
public class OwinContextMiddleware : OwinMiddleware
{
public OwinContextMiddleware(OwinMiddleware next)
: base(next)
{
}
public async override Task Invoke(IOwinContext context)
{
context.Environment.Add("Context", context);
await Next.Invoke(context);
}
}
NancyModule
public class MyModule : NancyModule
{
public MyModule()
{
Get["/", true] = async(x, ct) =>
{
IDictionary<string, object> environment = Context.GetOwinEnvironment();
IOwinContext context = (IOwinContext)environment["Context"]; // The same "Context" as added in the Middleware
}
}
Caveat
The middleware listed above is untested as the middleware I have is more complex and I haven't had the time to create a working example. I found a simple overview on how to create Owin middleware on this page.

Related

Chicken and egg issue implementing Autofac with NServicebus

Background
I'm trying to set up a Web API 2 which needs to communicate to a NServicebus Endpoint.
I will need to implement IoC, which will be done using Autofac.
What I have
A controller defined like so:
[RoutePrefix("api")]
public class Controller : ApiController
{
private IEndpointInstance EndpointInstance { get; set; }
public public MyController(IEndpointInstance endpointInstance)
{
this.EndpointInstance = endpointInstance;
}
[HttpGet]
[Route("dostuff")]
public async Task DoStuff()
{
var command = new MyCommand
{
...
};
await this.EndpointInstance.SendLocal(command);
}
}
And in global.asax
Application_Start
protected async void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
await RegisterNServiceBusWithAutofac();
}
RegisterNServiceBusWithAutofac
private async Task RegisterNServiceBusWithAutofac()
{
var builder = new ContainerBuilder();
var endpointConfiguration = await GetEndpointConfiguration("My.Service");
var endpointInstance = await Endpoint.Start(endpointConfiguration);
builder.RegisterInstance(endpointInstance);
var container = builder.Build();
endpointConfiguration.UseContainer<AutofacBuilder>(c => c.ExistingLifetimeScope(container));
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
}
GetEndpointConfiguration
private static async Task<EndpointConfiguration> GetEndpointConfiguration(string name)
{
var endpointConfiguration = new EndpointConfiguration(name);
// Set transport.
var routing = endpointConfiguration.UseTransport<MsmqTransport>().Routing();
// Register publish to self
routing.RegisterPublisher(typeof(EventHasFinished), name);
endpointConfiguration.UseSerialization<JsonSerializer>();
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.EnableInstallers();
return endpointConfiguration;
}
The result
I get the following error on the UseContainer line:
Unable to set the value for key:
NServiceBus.AutofacBuilder+LifetimeScopeHolder. The settings has been
locked for modifications. Move any configuration code earlier in the
configuration pipeline
What I think this means
I think I need to do all Autofac registrations for the NServicebus when creating the endpointConfiguration. The above manipulates the builder instance after that.
But
I can't do the above, because I need to register the endpointinstance to the IoC, because I need that in my controller to send messages. And that doesn't exist yet, because I need the endpointConfiguration first, for that.
So I have a chicken and egg situation ...
Question
Do I understand the issue correctly and how can I solve it while
making sure that IoC works correctly for the Controller?
I.e.: this.EndpointInstance has been correctly instantiated through IoC.
Instead of registering the actual instance, you could register it with a lambda expression that is going to be executed the first time the container will be asked to resolve IEndpointInstance.
builder
.Register(x =>
{
var endpointConfiguration = GetEndpointConfiguration("My.Service").GetAwaiter().GetResult();
var endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
return endpointInstance
})
.As<IEndpointInstance>()
.SingleInstance();

Why is AppTenant null only while seeding data?

I'm currently fiddeling around with Ben Fosters Saaskit.
I have extended the ApplicationUser with a AppTenantId property and created a custom UserStore, which uses the AppTenant to identify the user:
public class TenantEnabledUserStore : IUserStore<ApplicationUser>, IUserLoginStore<ApplicationUser>,
IUserPasswordStore<ApplicationUser>, IUserSecurityStampStore<ApplicationUser>
{
private bool _disposed;
private AppTenant _tenant;
private readonly ApplicationDbContext _context;
public TenantEnabledUserStore(ApplicationDbContext context, AppTenant tenant)
{
_context = context;
_tenant = tenant;
}
/*... implementation omitted for brevity*/
}
If a user registers or logs in, this works fine. The AppTenant is set correctly. The problem occurs, when SeedData.Initialize(app.ApplicationServices); is called at the end of my Statup.Configure() method:
public static class SeedData
{
public async static void Initialize(IServiceProvider provider)
{
using (var context = new ApplicationDbContext(
provider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
var admin = new ApplicationUser
{
AppTenantId = 1,
Email = "foo#bar.com",
UserName = "Administrator",
EmailConfirmed = true
};
if(!context.Users.Any(u => u.Email == admin.Email))
{
var userManager = provider.GetRequiredService<UserManager<ApplicationUser>>();
await userManager.CreateAsync(admin, "Penis123#");
}
context.SaveChanges();
}
}
}
The usermanager is is calling the custom userstore, but now AppTenant is null.
When the code finally reaches
public Task<ApplicationUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
return _context.Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName && u.AppTenantId == _tenant.AppTenantId, cancellationToken);
}
I am facing a System.InvalidoperationException, because AppTenant is passed as null in the constructor of above mentioned userstore.
What am I doing wrong? Am I seeding the wrong way or do I forget something fundamental here?
Update:
For now I have taken the crowbar-approach, avoided the usermanager and created my own instance of a userstore with a mock AppTenant:
if (!context.Users.Any(u => u.Email == admin.Email))
{
var userStore = new TenantEnabledUserStore(context, new AppTenant
{
AppTenantId = 1
});
await userStore.SetPasswordHashAsync(admin, new PasswordHasher<ApplicationUser>().HashPassword(admin, "VeryStrongPassword123#"), default(CancellationToken));
await userStore.SetSecurityStampAsync(admin, Guid.NewGuid().ToString("D"), default(CancellationToken));
await userStore.CreateAsync(admin, default(CancellationToken));
}
Nontheless, I'm still interested in a more clean approach, that doesn't feel that hacky.
When using Saaskit, you configure an AppTenantResolver that determines how to set the TenantContext<T> based on the provided HttpContext. It then stores the retrieved TenantContext<T> in the Items property of the HttpContext. This is a Scope level cache, so the tenant is only stored there for the duration of the request.
When you inject an AppTenant into a class it attempts to resolve it from HttpContext.Items. If no tenant is found, then it injects null instead.
When you call SeedData.Initialize(app.ApplicationServices), you are not in the context of a request, and so the AppTenantResolver middleware has not run, and will not have resolved an AppTenant.
Unfortunately, not having the full details of your code, it's hard to say exactly how to fix your issue. You would need to make sure you create a new Scope in your SeedData method and resolve an AppTenant within that scope so that subsequent calls to the IoC will allow it to be inserted.

When can I get an Application Insights operation id?

I have a AspNetCore web app that writes to EventHub and a webjob that reads from it. I'd like the telemetry from both parts of this transaction to have the same operation id in Application Insights.
So, when I'm about to send the data to EventHub I try to pull the operation id out of the TelemetryClient, e.g.
var myOperationId = MyTelemetryClient.Context.Operation.Id;
But this always gives me null. I found this article and tried using
var request.HttpContext.Items["Microsoft.ApplicationInsights.RequestTelemetry"] as RequestTelemetry;
But again null. Any pointers on a way I can extract this value when I need it?
My code looks like this:
public class Startup
{
public void ConfigureServices( IServiceCollection IServices )
{
var builder = TelemetryConfiguration.Active.TelemetryProcessorChainBuilder;
builder.Use((next) => new MyTelemetryProcessor(next));
builder.Build();
var aiOptions = new Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions();
aiOptions.EnableQuickPulseMetricStream = true;
IServices.AddApplicationInsightsTelemetry( Configuration, aiOptions);
IServices.AddMvc();
IServices.AddOptions();
TelemetryClient AppTelemetry = new TelemetryClient();
AppTelemetry.InstrumentationKey = InsightsInstrumentationKey;
IServices.AddSingleton(typeof(TelemetryClient), AppTelemetry);
}
public void Configure( IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory )
{
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseMvc();
var configuration = app.ApplicationServices.GetService<TelemetryConfiguration>();
configuration.TelemetryInitializers.Add(new MyTelemetryInitializer());
}
}
[Route("[controller]")]
public class MyController
{
private readonly TelemetryClient mTelemetryClient;
public MyController(
TelemetryClient TelemetryClientArg)
{
mTelemetryClient = TelemetryClientArg;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody]MyPostDataClass MyPostData)
{
string telemetryId = mTelemetryClient.Context.Operation.Id; // this is null
return Ok();
}
}
I did not have OperationIdTelemetryInitializer in my TelemetryConfiguration .Active.TelemetryInitializers.
But this provides me with the current operation id:
System.Diagnostics.Activity.Current.RootId
https://github.com/Microsoft/ApplicationInsights-aspnetcore/issues/504
Think I finally cracked this without creating unwanted telemetry. The following is for AspNetCore, but should translate as long as the operation id initializer is available:
var operationId = default(string);
try
{
var telemetry = new RequestTelemetry();
TelemetryConfiguration
.Active
.TelemetryInitializers
.OfType<OperationIdTelemetryInitializer>()
.Single()
.Initialize(telemetry);
operationId = telemetry.Context.Operation.Id;
}
catch { }
Asp.Net core package has an OperationIdTelemetryInitializer (see github) which tries to set this from a request. Hypothetically if you have request tracking and this telemetry initializer turned on, everything that happens in that request should have an Operation.Id set.
if that isn't working for you, you could create your own custom TelemetryInitializer to set it however you need, or, in the place where you do find it to be null, you could just set it there, and all things in that context should then have that id.
You can get this value by sending a piece of telemetry and then reading back the operation id from the telemetry object. Not elegant, but it works.
[HttpPost]
public async Task<IActionResult> Post([FromBody]MyPostDataClass MyPostData)
{
var dummyTrace = new TraceTelemetry("hello", SeverityLevel.Verbose);
mTelemetryClient.TrackTrace(dummyTrace);
string opId = dummyTrace.Context.Operation.Id;
return Ok();
}

Resolving an Autofac SingleInstance still results in "The request lifetime scope cannot be created because the HttpContext is not available" exception

Note: I have already read this article: Autofac - The request lifetime scope cannot be created because the HttpContext is not available - due to async code?, and it's not duplicate.
As of the recently update, I register a Autofac module for AutoMapper:
protected override void Load(A.ContainerBuilder builder)
{
var mapper = this.Config.CreateMapper();
builder.RegisterInstance(this.Config).As<IConfigurationProvider>().SingleInstance();
builder.RegisterInstance(mapper).SingleInstance();
}
EDIT: According to Travis's comment, I update my current code. All codes are within a Controller Action. This code works well:
var config = DependencyResolver.Current.GetService<IConfigurationProvider>();
However, this does not work:
var mapperConfig = this.MapperConfig;
Here are the definitions:
public class BaseController : Controller
{
public IConfigurationProvider MapperConfig
{
get
{
return this.Service<IConfigurationProvider>();
}
}
}
public static class ControllerExtension
{
public static T Service<T>(this Controller controller) where T : class
{
return DependencyUtils.Resolve<T>();
}
}
public static class DependencyUtils
{
public static T Resolve<T>() where T : class
{
return DependencyResolver.Current.GetService<T>();
}
}
OLD CONTENT:
And I have the following code (I have tried both async/non-async methods):
var modelProducts = await service.GetActive()
.ProjectTo<ProductViewModel>(this.MapperConfig)
.ToListAsync();
model.AppendLine("Select new products and project into ViewModel async successfully:");
MapperConfig definition:
public MapperConfiguration MapperConfig
{
get
{
return this.Service<MapperConfiguration>();
}
}
and the Service method:
public static T Service<T>(this Controller controller) where T : class
{
return DependencyResolver.Current.GetService<T>();
}
This exception is thrown:
I am sure that DependencyResolver.Current works well (I also changed to AutofacDependencyResolver.Current, it is the same object), because on the upper lines, I got the IProductService instance successfully with the same resolver (I am using Repository pattern). I am also used the newest package of AutoMapper, Autofac, Entity Framework 6.1 (to support SQL Server 2008). These code works fine, and if I comment out the ProjectTo line, there is no more exception:
var model = new StringBuilder();
var service = this.Service<IProductService>();
model.AppendLine("Service resolved successfully");
var products = service.Get().ToList();
model.AppendLine("Get all products successfully");
products = await service.Get().ToListAsync();
model.AppendLine("Get all products async successfully");
What did I do wrong?
EDIT: I seperated the calls, so it can be clearer:
var mapperConfig = this.MapperConfig;
var modelProducts = await service.GetActive()
.ProjectTo<ProductViewModel>(mapperConfig)
.ToListAsync();
model.AppendLine("Select new products and project into ViewModel async successfully:");
The exception occurs right at the first line, var mapperConfig = this.MapperConfig;, it is not related to the service call.

Getting Current OwinContext without using HttpContext

With
HttpContext.Current.GetOwinContext()
I can recieve the current OwinContext in web applications.
With OwinContext.Set<T> and OwinContext.Get<T> I store values which should be present for a whole request.
Now I've a component which should be used inside web and console owin applications. In this component I currently don't have access to http context.
In the application I'm using threading and async features.
I also tried using the CallContext but this seems to loose data in some scenarios.
How could I access the current OwinContext? Or is there an other context where I may play my values?
I do the below with a WebApi AuthorizationFilter, also you should also be able to do this on an MVC controller and WebApi controller context if you have middleware to support it for example app.UseWebApi(app) for WebApi.
The component must support the Owin pipeline, otherwise not sure how you will get the context from for the correct thread.
So maybe you can create your own custom
OwinMiddleware
to wireup this component using the app.Use() in your Owin startup.
More Info here
My Properties Middleware
public class PropertiesMiddleware : OwinMiddleware
{
Dictionary<string, object> _properties = null;
public PropertiesMiddleware(OwinMiddleware next, Dictionary<string, object> properties)
: base(next)
{
_properties = properties;
}
public async override Task Invoke(IOwinContext context)
{
if (_properties != null)
{
foreach (var prop in _properties)
if (context.Get<object>(prop.Key) == null)
{
context.Set<object>(prop.Key, prop.Value);
}
}
await Next.Invoke(context);
}
}
Owin StartUp configuration
public void Configuration(IAppBuilder app)
{
var properties = new Dictionary<string, object>();
properties.Add("AppName", AppName);
//pass any properties through the Owin context Environment
app.Use(typeof(PropertiesMiddleware), new object[] { properties });
}
WebApi Filter
public async Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext context, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
var owinContext = context.Request.GetOwinContext();
var owinEnvVars = owinContext.Environment;
var appName = owinEnvVars["AppName"];
}
Happy coding!

Categories

Resources