I have an issue with the static method in ASP.NET. I created the Singleton below. During the execution process I will call Context.getInstance() several times and I need the same context value. But, once I make another request (Get, Post, wherever) to the server I need a new context because my context is dependant from .NET HttpContext. But, unfortunately once I call getInstance for the first time the class never will be instantiated again.
Any ideas how I solve this problem?
public class Context
{
private static Context _context = null;
private Context()
{ ... }
public static Context getInstance()
{
if (Context._context == null)
_context = new Context();
return _context;
}
}
Get rid of your static variable and store it in HttpContext.Current.Items.
public static Context GetInstance()
{
if (HttpContext.Current.Items["MyContext"] == null)
{
HttpContext.Current.Items["MyContext"] = new Context();
}
return (Context)HttpContext.Current.Items["MyContext"];
}
If I understand you correctly, you need the context only in a single page request, correct?
If so, the method above certainly won't work - the static will live for the life of the app domain. This lifetime can vary depending on a number of factors.
I would start by creating a base page class (inheriting from the core ASP.NET Page class) and include a Context property on it. Since the page only "lives" for one request, that should work for you.
Another approach - I prefer using my own ThreadStatic variables (ala HttpContext.Current) rather than use the HttpContext Items collections simply because I think (an opinion) that it makes for cleaner code. YMMV.
public class Context
{
[ThreadStatic()]
private static Context _Context = null;
private HttpContext _HttpContext = null;
public Context()
{
_HttpContext = HttpContext.Current;
}
public static Context Current
{
if(_Context == null ||
_HttpContext != _HttpContext.Current)
{
_Context = new Context();
}
return _Context;
}
}
if your variable is static then all user will access that same variable and any change to that variable will effect all users in case of web-application only, the logic is when your are making a variable static then a memory location is allocated in server when this location is allocated all users share that location only because it is static.
Related
I am having MVC web application in which i have written some code to get forms cookie..Some times when i log out automatically some other users name were gets displays in text-box for which i have never logged in.I believe its due to private static variable but not sure.I have below code that i have implemented can anyone help me for this.Thanks
////This is code i am using to set from cookie
private static string _formsCookieName;
private static string FormsCookieName
{
get
{
if (string.IsNullOrWhiteSpace(_formsCookieName))
{
_formsCookieName = FormsAuthentication.FormsCookieName;
}
return _formsCookieName;
}
}
private static string _formsCookiePath;
private static string FormsCookiePath
{
get
{
if (string.IsNullOrWhiteSpace(_formsCookiePath))
{
_formsCookiePath = FormsAuthentication.FormsCookiePath;
}
return _formsCookiePath;
}
}
public static UserSession LogoutAuthentication(HttpContextBase context)
{
UserSession session = null;
string cookieName = FormsCookieName;
try
{
HttpCookie httpCookie = context.Request.Cookies[cookieName];
httpCookie.Expires = DateTime.Now;
FormsAuthentication.SignOut();
}
catch
{
}
return session;
}
Yes, a static variable are shared amongst all threads.
Don't use static properties for values that should live only in the lifespam of your request. You can't even use [ThreadStatic] in asp.net because you don't control the thread pool, and the same thread can be reused to handle different requests .
And even when you DO want a static value that is mutated by different threads, you need to have locks in place to avoid race conditions.
Your FormCookieName class is request dependent, therefore it should only exist during the life spam of it. The poor man way of doing it would be to instantiate it in Application_BeginRequest and disposing it on Application_EndRequest of Global.aspx.cs, assuming .NET Framework 4.5.
The correct way of doing it, though, is using a DI container. They not only inject dependency, but manages the objects lifecycles. All major DI Containers have an HttpContext lifecycle manager of sorts, and .NET Core comes with a DI Container built in. In it, your code would become:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IFormsCookieName, FormsCookieName>();
}
And your controller:
public class FooController : ControllerBase
{
public FooController(IFormsCookieName formsCookieName)
{
// receives a FormsCookieName instance that can safely use it's non-static properties
}
}
EDIT: Full configuration of Unity would be too long and off-topic for stack overflow. But the basic idea is that the Dependency Injector Container will create an instance of a non-static FormsCookieName in the scope of your HttpContext and then dispose that and the end of the request. This ensures that every HttpContext gets it's own copy of FormsCookieNameand no data will mess up.
I recommend unity as DI Container. It's maintained by Microsoft, and it's performance has seen a lot of improvements in latest versions.
Configuring a DI Container isn't hard, and provides lots of benefits.
I'm working on a Web App where I instantiated my a Singleton class below in Startup.cs in order to be reused (more like making a programmable session):
app.CreatePerOwinContext<XYZManager>(XYZManager.Create);
But I'm encountering a problem, as soon as UserA logs in on the app, the information inside XYZManager class gets overwritten when UserB enters and vice versa when they perform some action.
The problem I think is, they're sharing the same application pool, how can this be resolve, any hack?
An meanwhile the whole essence of this approach, I want to be able to call any getter / setter of method inside XYZManager for the current logged user for example:
HttpContext.GetOwinContext().Get<XYZManager>().GetFullDetails();
But sometimes throw details for another logged on user based on operations.
public class XYZManager : IDisposable
{
private static XYZManager instance { get; set; }
public static XYZManager Create()
{
var xyzManager = instance ?? (instance = new XYZManager());
xyzManager.ApplicationDbContext = new ApplicationDbContext();
return xyzManager;
}
public string GetFullDetails () {
return "blah blah";
}
}
As described in msdn, the CreatePerOwinContext method will accept a factory method to create an instance of your class (in this cas XYZManager), and it will keep it for all same context requests with.
HttpContext.GetOwinContext().Get<XYZManager>()
So each time a new Owin Context is created (a new http request received) XYZManager.Create will be invoked. In your case this method returns the same instance, so all contexts will share that instance.
Depending if you want to share that instance for all contexts or not you should return new or the same instances. Also note, that for singleton shared instances there is a different Owin extension method that will keep the singleton for you.
Check out this answer as it explains what is the purpose of the CreatePerOwinContext method, as well as provide some examples how to create a inter context shared instance.
This is how you create Context shared service
public class XYZManager : IDisposable
{
public static XYZManager Create()
{
return new XYZManager(new ApplicationDbContext());
}
private readonly ApplicationDbContext DbContext;
public XYZManager(ApplicationDbContext dbContext)
{
DbContext = dbContext;
}
public string SomeInfo {get;set;}
public string GetFullDetails ()
{
return dbContext.getFullDetails();
}
// dispose
}
Note: Since you will be creating instances each time a new owin context is creates it is advisable, to make sure any unmanaged objects are disposed.
I have a Lazy Singleton that is used as a reference object for a web application.
It stores basic properties that are refrenenced throughout the application:
public class Context
{
public string UserName;
public Guid TenantId;
public static Context Current { get { return lazy.Value; } }
private static readonly Lazy<Context> lazy =
new Lazy<Context>(() => new Context());
}
//In Action
public static Something GetSomethingForUser()
{
return DataAccess.GetSomethingForCurrentUser(Context.Current.UserName);
}
The problem is, if a user logs in and out my Context is persisting across sessions.
This isn't unexpected, I'm not disposing it.
Where should I do that? What's the best method?
Should I do it on sign out?
public void SignOut()
{
Context.Dispose();
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
}
In my opinion, signout should be an event other classes can register to and act properly in response to it.
The way you are using the singleton seems okay to me if you only have 1 active user at a time.
Another solution which would removed the signout problem, such has the data inside the singleton should be put inside the session itself.
I'm struggling with Entityframework in a MVC 4 app, making use of Unity for Dependency injection and Automapper for automapping object to DTO. I run from one issue to the other, EF is sometimes returning old data, so I think my design is not good enough.
What do I have:
To configure Unity I have in my Application_Start:
var UnityContainer = UnityConfig.GetConfiguredContainer();
Mapper.Initialize(cfg => cfg.ConstructServicesUsing(type => UnityContainer.Resolve(type)));
UnityContainer.Resolve<AutoMapperConfig>().MapAutoMapper();
...
In UnityConfig.RegisterTypes:
container.RegisterType<IMyContext, MyContext>(new ContainerControlledLifetimeManager())
...
My respositories use constructor depencency injection:
public class MSSQLTenantRepository : IDalTenantRepository
{
private readonly IMyContext _Db;
public MSSQLTenantRepository(IMyContext db)
{
Db = db;
}
...
And my controller use constructor dependency injection too:
public class TenantController : Controller
{
private readonly ITenantRepository _TenantRepository;
public TenantController(ITenantRepository tenantRepository,
{
_TenantRepository = tenantRepository;
}
...
Automapper config:
public class AutoMapperConfig
{
private readonly ITenantRepository _TenantRepository;
public AutoMapperConfig(ITenantRepository tenantRepository)
{
_TenantRepository = tenantRepository;
}
...
Issues:
I sometimes get old data, from the first request.
When I manually update the data in de SQL server, EF's returning object don't reflect the changes
When I tried different options I also got error about multiple context (due to Automapper)
My questions:
What is best practice using Unity, MVC4, EF 6, repositories and Automapper?
Where to put the code (e.g. in global.asax.c or in UnitiConfig.cs of UnityWebApiActivator?
Do I need to explicit dispose the dbcontext, and if so: Where to do this?
There is a lot said about this subject, but nothing covers all.
container.RegisterType<IMyContext, MyContext>(
new ContainerControlledLifetimeManager())
This is rather bad, it makes a singleton out of your context. This way not only multiple requests share the same context and you risk concurrency issues but also the memory consumption of such shared context grows without control.
Rather, you would like to have a "per-request" life time, where a new context is established for each separate request:
http://www.wiktorzychla.com/2013/03/unity-and-http-per-request-lifetime.html
public class PerRequestLifetimeManager : LifetimeManager
{
private readonly object key = new object();
public override object GetValue()
{
if (HttpContext.Current != null &&
HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public override void RemoveValue()
{
if (HttpContext.Current != null)
HttpContext.Current.Items.Remove(key);
}
public override void SetValue(object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
and
container.RegisterType<IMyContext, MyContext>(new PerRequestLifetimeManager())
I am not sure what your AutoMapperConfig class does and why a repository is injected into it. This is a possible another lifetime issue but I need a clarification on that.
I figured it out with some help of Wiktor.
First: I must just a PerRequestLifeTimeManager (as stated by Wiktor Zychla, thank you for that), which is available in de Unity for MVC bootstrapper.
Second: The line:
UnityContainer.Resolve<AutoMapperConfig>().MapAutoMapper();
must be in Application_BeginRequest (Globas.asax.cs). I've put it in Application_Start, so this was only resolved once at startup. An incoming request was creating a new context, so it differs from the one that Automapper uses. When putting it in BeginRequest the resolve is done on every request, with the same context as the repositories.
We create one Linq2Sql DataContext for every request using the following class:
public static class DataContextManager
{
private const string HTTPCONTEXT_KEY = "DataContextManagerKey";
private static CompanyDataContext _staticContext; //when there's no HttpContext (in test/debug situations).
public static CompanyDataContext Context
{
get
{
if (_Context == null)
{
_Context = NewContext();
}
return _Context;
}
}
private static CompanyDataContext _Context
{
get
{
return (CompanyDataContext)(HttpContext.Current != null ? HttpContext.Current.Items[HTTPCONTEXT_KEY] : _staticContext);
}
set
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items[HTTPCONTEXT_KEY] = value;
}
else
{
DataContextManager._staticContext = value;
}
}
}
public static void Dispose()
{
CompanyDataContext context = _Context;
if (context != null)
{
if (Config.Instance.TestMode) context.Log.WriteLine("--- DISPOSING DATACONTEXT ---");
context.Dispose();
_Context = null;
}
}
public static CompanyDataContext NewContext()
{
CompanyDataContext db = new CompanyDataContext();
db.CommandTimeout = Config.SQL_COMMAND_TIMEOUT;
if (Config.Instance.TestMode)
{
db.Log = new ConsoleTextWriter();
db.Log.WriteLine("--- CREATING NEW DATACONTEXT ---");
}
return db;
}
}
And in Global.asax:
protected void Application_EndRequest(Object sender, EventArgs e)
{
DataContextManager.Dispose();
}
The reason why I'm asking is that we're suddenly getting random "SqlException: Server failed to resume the transaction" exceptions once or twice every day with code that used to work perfectly. After the exception we get a lot of other exceptions until we restart the web application. Anyone seen this behaviour before?
We're running ASP .Net 2.0 with SQL server 2005 on IIS 6.
UPDATE:
Just so no one else does the same horrible mistake as we did:
It turned out that some worker threads also used the DataContext but with no HttpContext they of course got the _staticContext (a feature in DataContextManager only to be used when testing). We rewrote the code in the worker threads to make sure one DataContext per thread and disposing it when done. And so far everything has worked for 2 weeks :)
This is a bad pattern. First you should never have a static data context it implements IDisposable so one thread could try to use the context while another is disposing of it plus many other potential problems. One data context per http request is no good either, data contexts are designed to be used for a single transaction then disposed of. You get problems if you retrieve update/insert/delete and retrieve using the same context, the second retrieve does not reflect the changes of the update/insert/delete. Remove the static context and just have the Context property return a new context every time. You could still dispose of the all at the end of a request by sticking them all in a List property and iterating through it.