How to execute a service in Global's Session_Start() - c#

I currently have a asp.net mvc application with ABP implementation. I currently want to execute a service method inside the Session_Start(), may I ask how abouts would I do that.
The service can be executed anywhere I have access to the IOC resolve but I'm in the global file and I'm not entirely sure how to do that from there.
protected void Session_Start()
{
// starting a session and already authenticated means we have an old cookie
var existingUser = System.Web.HttpContext.Current.User;
if (existingUser != null && existingUser.Identity.Name != "")
{
// execute app service here.
// if I'm exposed to IOCresolver I would do the following below
var srv = _iocResolver.Resolve<SettingsAppService>();
srv.UpdateItems();
}
}
May I ask how do I access IOC resolver on global.asax.cs file, if even possible. My goal is to execute the service when the user has re-established his session.

From the documentation on Dependency Injection:
The IIocResolver (and IIocManager) also have the CreateScope extension method (defined in the Abp.Dependency namespace) to safely release all resolved dependencies.
At the end of using block, all resolved dependencies are automatically removed.
If you are in a static context or can not inject IIocManager, as a last resort, you can use a singleton object IocManager.Instance everywhere.
So, use a scope with IocManager.Instance:
using (var scope = IocManager.Instance.CreateScope()) { ... }
→ IocManager.Instance.UsingScope(scope => { ... })
protected void Session_Start()
{
// Starting a session and already authenticated means we have an old cookie
var existingUser = System.Web.HttpContext.Current.User;
if (existingUser != null && existingUser.Identity.Name != "")
{
IocManager.Instance.UsingScope(scope => // Here
{
// Execute app service here.
var srv = scope.Resolve<SettingsAppService>();
srv.UpdateItems();
});
}
}

You can create a static link to your IoC resolver and use it in Global.asax. You even can add it to Global.asax.cs. Set this property after container registration and use it from anywhere.
public static YourIocResolver IocResolver { get; set; }

Related

Can Cosmos SDK3 Container be a singleton?

I would like to reduce load on Azure Cosmos DB SQL-API, which is called from a .NET Core Web API with dependency injection.
In App Insights, I have noticed that every call to the Web API results in GetDatabase and GetCollection calls to Cosmos which can take 5s to run when Cosmos is under heavy load.
I have made CosmosClient a singleton (e.g advice here - https://learn.microsoft.com/en-us/azure/cosmos-db/performance-tips-dotnet-sdk-v3-sql)
However I could not find any advice for whether the Database or Container objects could also be singletons so these are created for each request to the Web API.
I check for the existence of the database and collection (e.g. following advice here - https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.cosmos.cosmosclient.getdatabase?view=azure-dotnet#remarks and https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.cosmos.cosmosclient.getcontainer?view=azure-dotnet#remarks)
This means that for every request to the Web API, the following code is run
var databaseResponse = await this.cosmosClient.CreateDatabaseIfNotExistsAsync(
this.databaseConfiguration.DatabaseName,
throughput: this.databaseConfiguration.DatabaseLevelThroughput);
var database = databaseResponse.Database;
var containerResponse = await database.CreateContainerIfNotExistsAsync(containerId, partitionKey);
var container = containerResponse.Container;
Can I make Database and Container singletons and add them to the DI to be injected like CosmosClient in order to reduce the number of calls to GetDatabase and GetCollection seen in App Insights?
According to the latest Microsoft documentation, you create a CosmosClient Service singleton, which owns the Containers you will be working with. In affect making the Containers singletons as well.
First, make your interface contract:
public interface ICosmosDbService
{
// identify the database CRUD operations you need
}
Second, define your Service based on the contract:
public class CosmosDbService : ICosmosDbService
{
private Container _container;
public CosmosDbService(CosmosClient dbClient, string databaseName, string containerName)
{
this._container = dbClient.GetContainer(databaseName, containerName);
}
// your database CRUD operations go here using the Container field(s)
}
Third, create a method in your Startup class to return a CosmosClient:
private static async Task<CosmosDbService> InitializeCosmosClientAsync(IConfigurationSection cosmosConfig)
{
var databaseName = cosmosConfig.GetSection("DatabaseName").Value;
var containerName = cosmosConfig.GetSection("ContainerName").Value;
var account = cosmosConfig.GetSection("Account").Value;
var key = cosmosConfig.GetSection("Key").Value;
var client = new Microsoft.Azure.Cosmos.CosmosClient(account, key);
var database = await client.CreateDatabaseIfNotExistsAsync(databaseName);
await database.Database.CreateContainerIfNotExistsAsync(containerName, "/id");
return new CosmosDbService(client, databaseName, containerName);
}
Finally, add your CosmosClient to the ServiceCollection:
public void ConfigureServices(IServiceCollection services)
{
var cosmosConfig = this.Configuration.GetSection("CosmosDb");
var cosmosClient = InitializeCosmosClientAsync(cosmosConfig).GetAwaiter().GetResult();
services.AddSingleton<ICosmosDbService>(cosmosClient);
}
Now your CosmosClient has only been created once (with all the Containers), and it will be reused each time you get it through Dependency Injection.
You don't need to call CreateIfNotExistsAsync every time, if you know that they are available, you can use CosmosClient.GetContainer(dbName, containerName) which is a lightweight proxy class.
Unless you are expecting the database and containers to be deleted dynamically at some point?
CreateDatabaseIfNotExistsAsync should only be called once as it is just a setup step for DB configuration.
You'd better create a DbService to persist the container object. And inject the DbService into each services instead of the DB client

Why does DI return the same DbContext for two different scopes?

I'm trying to add a test to an ASP.NET Core project where an object is created in one scope and then read in another scope. This is to simulate a user creating an object in one POST request and then reading it in another GET Request. However, I'm having trouble properly simulating this scenario.
I have this in my test code
SomeDbContext firstContext;
bool isSame;
using (var scope = someServiceProvider.CreateScope()) {
firstContext = someServiceProvider.GetService<SomeDbContext>();
}
using (var scope = someServiceProvider.CreateScope()) {
var secondContext = someServiceProvider.GetService<SomeDbContext>();
isSame = firstContext == secondContext; //should be false, right?
}
I expect isSame to have a value of false when the code above executes but it's actually true. Why is that? SomeDbContext has a lifetime of scoped when registering it with AddDbContext() so it should be destroyed when its scope is disposed and recreated in the second scope.
Your test is incorrect. Although you are creating two separate scopes, you're not actually using them. Here's a working version:
SomeDbContext firstContext;
bool isSame;
using (var scope = someServiceProvider.CreateScope()) {
firstContext = scope.ServiceProvider.GetService<SomeDbContext>();
}
using (var scope = someServiceProvider.CreateScope()) {
var secondContext = scope.ServiceProvider.GetService<SomeDbContext>();
isSame = firstContext == secondContext; //should be false, right?
}
Note how scope.ServiceProvider is used instead of someServiceProvider when resolving dependencies.
The closest thing I can find in the docs is Call services from main. Although the example shows the Main method, it does also demonstrate how the IServiceProvider that gets used comes from the scope itself.

Testing WCF Service that Uses Impersonation

I am converting some existing integration tests of a legacy WCF service to be automated via NUnit. The current tests call a deployed version of the WCF service; what I would like to do is have the tests hit the service class (MyService.svc.cs) directly/internally.
The problem I am having is that the service uses impersonation:
//this is a method in MyService.svc.cs
public SomeObject GetSomeObject()
{
using (GetWindowsIdentity().Impersonate())
{
//do some stuff
}
return null;
}
private WindowsIdentity GetWindowsIdentity()
{
var callerWinIdentity = ServiceSecurityContext.Current.WindowsIdentity;
var cf = new ChannelFactory<IMyService>();
cf.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
return callerWinIdentity;
}
The problem is that ServiceSecurityContext.Current is always null when I call it from a unit test.
The impersonation is important in downstream operations, so I can't just bypass this code and just call what is within the using block. It might be possible to wrap my test code in WindowsIdentity.GetCurrent().Impersonate() and then call what is within the using block (bypassing MyService.svc.cs code), but this would be less than ideal, as it would not be a complete end-to-end test.
I do not need to fake different users to impersonate--I just need the runner's user context to be available in ServiceSecurityContext.Current.
Is this possible?
I'd still be interested in a better and less invasive way of doing this, but this seems to work for now.
I created a second constructor for MyService to allow the use of WindowsIdentity.GetCurrent().
private readonly bool _useLocalIdentity;
public MyService(bool useLocalIdentity) :this()
{
_useLocalIdentity = useLocalIdentity;
}
private WindowsIdentity GetWindowsIdentity()
{
if (_useLocalIdentity)
{
return WindowsIdentity.GetCurrent();
}
var callerWinIdentity = ServiceSecurityContext.Current.WindowsIdentity;
if (callerWinIdentity == null)
{
throw new InvalidOperationException("Caller not authenticated");
}
var cf = new ChannelFactory<IMyService>();
cf.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
return callerWinIdentity;
}

Autofac global.asax errors with using Tasks in MVC 5.1

I've setup a series of tasks using the Application events in a global.asax.cs file like so:
// Note: For instructions on enabling IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=301868
public class MvcApplication : System.Web.HttpApplication
{
private static IContainer ContainerGlobal;
private static ILogger Logger;
public ILifetimeScope Container
{
get { return (ILifetimeScope)HttpContext.Current.Items["_Container"]; }
set { HttpContext.Current.Items["_Container"] = value; }
}
protected void Application_Start()
{
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//configure the Autofac IoC container
var container = AutofacBuilder.Configure(Assembly.GetExecutingAssembly(),
new MvcModule(), new TaskModule());
// startup the logging
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase)
.Replace("file:\\", string.Empty);
var file = new FileInfo(path + #"\log4net.ui.config");
Logger = new Logger(MethodBase.GetCurrentMethod().DeclaringType, file);
var runAtInits = container.Resolve<IEnumerable<IRunAtInit>>();
if (runAtInits != null)
{
Logger.LogFormat(LogType.Debug, "Found {0} IRunAtInit instances",
runAtInits.Count());
foreach (var task in runAtInits)
{
task.Execute();
}
}
var runAtStartups = container.Resolve<IEnumerable<IRunAtStartup>>();
if (runAtStartups != null)
{
Logger.LogFormat(LogType.Debug, "Found {0} IRunAtStartup instances",
runAtStartups.Count());
foreach (var task in runAtStartups)
{
task.Execute();
}
}
ContainerGlobal = container;
}
public void Application_BeginRequest()
{
try
{
Container = ContainerGlobal.BeginLifetimeScope();
var runOnEachRequests =
Container.Resolve<IEnumerable<IRunOnEachRequest>>();
if (runOnEachRequests == null)
return;
Logger.LogFormat(LogType.Debug, "Found {0} IRunOnEachRequest instances",
runOnEachRequests.Count());
foreach (var task in runOnEachRequests)
{
task.Execute();
}
}
catch(Exception ex)
{
Logger.Log(LogType.Error, ex);
}
}
public void Application_Error()
{
try
{
var runOnErrors = Container.Resolve<IEnumerable<IRunOnError>>();
if (runOnErrors == null)
return;
Logger.LogFormat(LogType.Debug, "Found {0} IRunOnError instances",
runOnErrors.Count());
foreach (var task in runOnErrors)
{
task.Execute();
}
}
catch (Exception ex)
{
Logger.Log(LogType.Error, ex);
}
}
public void Application_EndRequest()
{
try
{
var runAfterEachRequests =
Container.Resolve<IEnumerable<IRunAfterEachRequest>>();
if (runAfterEachRequests == null)
return;
Logger.LogFormat(LogType.Debug, "Found {0} IRunAfterEachRequest instances",
runAfterEachRequests.Count());
foreach (var task in runAfterEachRequests)
{
task.Execute();
}
}
catch (Exception ex)
{
Logger.Log(LogType.Error, ex);
}
finally
{
if (Container != null)
{
Container.Dispose();
Container = null;
}
}
}
}
This is discussed by Matt Honeycut here:
https://github.com/MattHoneycutt/Fail-Tracker
As we make extensive use of Autofac throughout our application, we've implemented it using Autofac rather than Structuremap.
The problem is, we get exceptions raised for the following events: Application_EndRequest and sometimes for Application_BeginRequest. The exception raised in both cases is:
"No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself"
We've ensured that we use the ILifeScope for our Container object, rather than the root Container object. However, this doesn't fix the errors. Does anyone else have any suggestions as to what else we need to do?
For information, the call to AutofacBuilder.Configure above registers all necessary library types and modules and returns the root Container from autofac by calling the Build method.
thanks
PS We're using:
Visual Studio 2013,
MVC 5.1
Autofac 3.3
The error message pretty much says it all, but to be more specific, one or more of the following is occurring:
One or more of your begin/end request handlers is being registered as InstancePerHttpRequest
One or more of the dependencies required by your begin/end request handlers is being registered as InstancePerHttpRequest
One or more of the request handlers (or the dependencies for the request handlers) is trying to use DependencyResolver.Current during app start/end.
The Autofac dependency resolver requires a web request context to work. You can read more about that in the answer here: Autofac - The request lifetime scope cannot be created because the HttpContext is not available - due to async code?
App start/end don't have a web request running. They're outside the request pipeline.
Go through your registrations (which appear to be in AutofacBuilder.Configure) and check to see which ones are InstancePerHttpRequest. Something in there that is required during your handlers' execution is getting incorrectly registered that way, so when the web request scope isn't found - boom. And, again, it may not be just one thing - it could be that all your handlers are registered correctly, but one of the dependencies for your handlers are registered InstancePerHttpRequest.
If you find the issues and you don't want to switch to registering them as SingleInstance, consider switching the registration to InstancePerLifetimeScope. Chances are, unless your app is creating a bunch of lifetime scopes for units of work or something, that InstancePerLifetimeScope will behave just like InstancePerHttpRequest but will properly resolve without a web request.
That said, I would recommend wrapping the task execution in a lifetime scope so memory gets cleaned up:
using(var scope = container.BeginLifetimeScope())
{
// Resolve from a scope.
var runAtStartups = scope.Resolve<IEnumerable<IRunAtStartup>>();
// Do the run, etc.
}
Finally, something to be aware of: I see you're manually generating some sort of request lifetime scope yourself in the BeginRequest event. I'm not sure if it's important, but the scope you're creating there will not be the request lifetime scope that Autofac actually uses. It will only be used for your components, and it won't work with InstancePerHttpRequest registered stuff.

Workaround for HttpContext.HideRequestResponse being internal? Detect if HttpContext.Request is really available?

We're migrating an application to use IIS7 integrated mode. In library code that is designed to work either within the context of an HTTP request or not, we commonly have code like this:
if (HttpContext.Current != null &&
HttpContext.Current.Request != null) {
// do something with HttpContext.Current.Request
} else {
// do equivalent thing without HttpContext..
}
But in IIS7 integrated mode the check for HttpContext.Current.Request throws an exception whenever this code is called from Application_Start.
protected void Application_Start(object sender, EventArgs e)
{
SomeLibrary.DoSomethingWithHttpContextCurrentDetection();
}
Results in:
System.Web.HttpException: Request is not available in this context
How can I detect whether the request is really available without wrapping these calls in an exception handler and taking action based on whether an exception is generated or not.
Looking at HttpContext in Reflector I see it has an internal bool HideRequestResponse field but it's internal so I can only get to it with reflection and that's fragile. Is there a more official/approved way to determine if it's ok to call HttpContext.Request?
This blog post about the subject says not to use HttpContext, but how, in generic library code, can you determine if it's ok to use HttpContext?
http://mvolo.com/iis7-integrated-mode-request-is-not-available-in-this-context-exception-in-applicationstart/
I'm using the work-around mentioned there which is to use Application_BeginRequest and an initialized field to only initialize once as part of BeginRequest, but that has to be done in every calling application whereas I'd prefer to make the library code more robust and handle this situation regardless of where it's called from.
I would refactor your code to this:
if (IsRequestAvailable())
{
// do something with HttpContext.Current.Request...
}
else
{
// do equivalent thing without HttpContext...
}
public Boolean IsRequestAvailable()
{
if (HttpContext.Current == null)
return false;
try
{
if (HttpContext.Current.Request == null)
return false;
}
catch (System.Web.HttpException ex)
{
#if DEBUG
// Testing exception to a magic string not the best practice but
// it works for this demo.
if (ex.Message == "Request is not available in this context")
return false;
throw;
#else
return false;
#endif
}
return true;
}
Your question asked not to use exception handling (I assume for performance reasons) and my answer does. However, by changing your code from using "If (HttpContext.Current != null && HttpContext.Current.Request != null)" to "If (IsRequestAvailable())" you only have one place to change the code when you find an answer how not to use exception handling.
I'm afraid the answer is that you can't get what you want - Microsoft sees this case as an 'exceptional circumstance' and so it will throw an exception.
You can use reflection as you describe in your answer but you don't want to and so are limited by the API that Microsoft have provided, for better or for worse.
If you do decide to use reflection, of note is the HttpApplication.InitInternal method which is what sets the HideRequestResponse flag.
Hope that helps. I would suggest you file a report with Microsoft Connect.
You should not even use Request (or Response) in the Application_Start since application could be started without a request. So in the future your application won't even run when other parts of framework stop providing the Request object.
If you want to just hack it temporarily, you could use Reflection (if you have above-medium trust) or catching an exception (even though you don't want to) and store the result in a static variable or possibly use a static HttpContext wrapper:
Also you could use HttpRuntime.UsingIntegratedPipeline.
So the best approach is remove the dependance of your classes on HttpContext when they are being initialized or not initalize them in appstart.
What is your reasoning to use Request in the app start anyway? For statistics? Or just telling the user he woke the application?
Edited with code to explain better:
public static class ContextWrapper
{
public static HttpRequest Request
{
get
{
HttpContext context = HttpContext.Current;
if (context == null) return null;
if (HttpRuntime.UsingIntegratedPipeline)
{
try { return context.Request; }
catch (HttpException e) { /* Consume or log e*/ return null; }
// Do not use message comparison - .NET translates messages for multi-culture environments.
}
return context.Request;
}
}
}
And in code:
if (ContextWrapper.Request != null) //...
Or a user-controlled faster way:
public static class ContextWrapper2
{
public static bool IsIis7IntegratedAppStart { get; set; }
public static HttpRequest Request
{
get
{
if (ContextWrapper2.IsIis7IntegratedAppStart) return null;
HttpContext context = HttpContext.Current;
if (context == null) return null;
return context.Request;
}
}
}
And in app start:
protected void Application_Start(object sender, EventArgs e)
{
yourLibraryNamespace.ContextWrapper2.IsIis7IntegratedAppStart = true;
//...
yourLibraryNamespace.yourClass.Init();
//...
yourLibraryNamespace.ContextWrapper2.IsIis7IntegratedAppStart = false;
}
You could note this behaviour in your documentation and all should be well. AppStart-like context should be the only place where you get such an exception.
You could also implement IDisposable on a member and use it in appStart with the using statement so you do not forget to set IsIis7IntegratedAppStart = false.
I think I have the solution for you. I maintain a logging library and have the same issue as you. If it is a web request I am grabbing some data from the HttpContext. But depending on how the logging library is used this same scenario can happen. So here is my solution. The key fix for me was checking if the Handler was null or not.
if (System.Web.Hosting.HostingEnvironment.IsHosted
&& System.Web.HttpContext.Current != null
&& System.Web.HttpContext.Current.Handler != null
&& System.Web.HttpContext.Current.Request != null)
{
//access the Request object here
}
Depending on what you are trying to accomplish, you may be able to get some of the properties and settings around the web app from System.Web.Hosting.HostingEnvironment
I added a comment, but it gets auto-hidden.
I think it's more important to have an idea of what it is that you need from the request.
For instance, the link you provided which provides a workaround is looking for Request.ApplicationPath.
If that's actually what you're looking for (for, say, loading the web.config vs the app.config), you could do this:
if (HttpRuntime.AppDomainAppId != null)
return WebConfigurationManager.OpenWebConfiguration(HttpRuntime.AppDomainAppVirtualPath);
else
return ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
If this (or HttpRuntime.ApplicationPath) isn't what you're actually looking for, it would be helpful to know which properties of the Request you are actually looking for. Maybe there's a better, safer way to get there.

Categories

Resources