Entity Framework Context per request in ASP.NET and Multi Threading - c#

My application is in ASP.NET MVC 4.
I'm using BDContext per request, as recommended in many questions here.
I have:
public static class ContextPerRequest {
private const string myDbPerRequestContext = "dbGeTraining_";
public static DbGesForma_v2 db {
get {
if (!HttpContext.Current.Items.Contains(myDbPerRequestContext + HttpContext.Current.GetHashCode().ToString("x") + Thread.CurrentContext.ContextID.ToString())) {
HttpContext.Current.Items.Add(myDbPerRequestContext + HttpContext.Current.GetHashCode().ToString("x") + Thread.CurrentContext.ContextID.ToString(), new DbGesForma_v2());
}
return HttpContext.Current.Items[myDbPerRequestContext + HttpContext.Current.GetHashCode().ToString("x") + Thread.CurrentContext.ContextID.ToString()] as DbGesForma_v2;
}
}
/// <summary>
/// Called automatically on Application_EndRequest()
/// </summary>
public static void DisposeDbContextPerRequest() {
// Getting dbContext directly to avoid creating it in case it was not already created.
var entityContext = HttpContext.Current.Items[myDbPerRequestContext + HttpContext.Current.GetHashCode().ToString("x") + Thread.CurrentContext.ContextID.ToString()] as DbGesForma_v2;
if (entityContext != null) {
entityContext.Dispose();
HttpContext.Current.Items.Remove(myDbPerRequestContext + HttpContext.Current.GetHashCode().ToString("x") + Thread.CurrentContext.ContextID.ToString());
}
}
}
And I dispose it in Application_EndRequest() method. This approach worked well for a long time.
Now I'm trying to implement something with asynchronous tasks like this:
Task.Factory.StartNew(() => {
DoSomething();
});
This brings some problems.
HttpContext is null in subthreads, and is used in the key of the context.
Even if I would be able to pass httpcontext or null check it, if a subthread takes longer to run than the request itself, it would be disposed before the thread finishes, which would be problematic.
Any solutions?

I'm not sure what version of ASP.NET you use. Anyway, ASP.NET MVC (also WebAPI) has DependencyResolver which support those 'per-request' instance.
http://www.asp.net/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4-dependency-injection
Also, I recommend you to use DI framework with DependencyResolver, rather than implement per-request instance factory (or something like that). Most of well-known DI frameworks are support integration with ASP.NET.
For instance;
Unity
Autofac
SimpleInjector
and many others

I've found a less intrusive solution. (I know, Gongdo Gong solution is much better, but requires a lot of changes in an ongoing project)
When I call the async task I pass through the HttpContext, and at the end I dispose the Context.
Like this:
System.Web.HttpContext htcont = System.Web.HttpContext.Current;
Task.Factory.StartNew(() => {
System.Web.HttpContext.Current = htcont;
DoSomething();
ContextPerRequest.DisposeDbContextPerRequest();
});
This way HttpContext is usable inside the subthread and the context gets disposed at the end of the job.

Related

Block Controller Method while already running

I have a controller which returns a large json object. If this object does not exist, it will generate and return it afterwards. The generation takes about 5 seconds, and if the client sent the request multiple times, the object gets generated with x-times the children. So my question is: Is there a way to block the second request, until the first one finished, independent who sent the request?
Normally I would do it with a Singleton, but because I am having scoped services, singleton does not work here
Warning: this is very oppinionated and maybe not suitable for Stack Overflow, but here it is anyway
Although I'll provide no code... when things take a while to generate, you don't usually spend that time directly in controller code, but do something like "start a background task to generate the result, and provide a "task id", which can be queried on another different call).
So, my preferred course of action for this would be having two different controller actions:
Generate, which creates the background job, assigns it some id, and returns the id
GetResult, to which you pass the task id, and returns either different error codes for "job id doesn't exist", "job id isn't finished", or a 200 with the result.
This way, your clients will need to call both, however, in Generate, you can check if the job is already being created and return an existing job id.
This of course moves the need to "retry and check" to your client: in exchange, you don't leave the connection to the server opened during those 5 seconds (which could potentially be multiplied by a number of clients) and return fast.
Otherwise, if you don't care about having your clients wait for a response during those 5 seconds, you could do a simple:
if(resultDoesntExist) {
resultDoesntExist = false; // You can use locks for the boolean setters or Interlocked instead of just setting a member
resultIsBeingGenerated = true;
generateResult(); // <-- this is what takes 5 seconds
resultIsBeingGenerated = false;
}
while(resultIsBeingGenerated) { await Task.Delay(10); } // <-- other clients will wait here
var result = getResult(); // <-- this should be fast once the result is already created
return result;
note: those booleans and the actual loop could be on the controller, or on the service, or wherever you see fit: just be wary of making them thread-safe in however method you see appropriate
So you basically make other clients wait till the first one generates the result, with "almost" no CPU load on the server... however with a connection open and a thread from the threadpool used, so I just DO NOT recommend this :-)
PS: #Leaky solution above is also good, but it also shifts the responsability to retry to the client, and if you are going to do that, I'd probably go directly with a "background job id", instead of having the first (the one that generates the result) one take 5 seconds. IMO, if it can be avoided, no API action should ever take 5 seconds to return :-)
Do you have an example for Interlocked.CompareExchange?
Sure. I'm definitely not the most knowledgeable person when it comes to multi-threading stuff, but this is quite simple (as you might know, Interlocked has no support for bool, so it's customary to represent it with an integral type):
public class QueryStatus
{
private static int _flag;
// Returns false if the query has already started.
public bool TrySetStarted()
=> Interlocked.CompareExchange(ref _flag, 1, 0) == 0;
public void SetFinished()
=> Interlocked.Exchange(ref _flag, 0);
}
I think it's the safest if you use it like this, with a 'Try' method, which tries to set the value and tells you if it was already set, in an atomic way.
Besides simply adding this (I mean just the field and the methods) to your existing component, you can also use it as a separate component, injected from the IOC container as scoped. Or even injected as a singleton, and then you don't have to use a static field.
Storing state like this should be good for as long as the application is running, but if the hosted application is recycled due to inactivity, it's obviously lost. Though, that won't happen while a request is still processing, and definitely won't happen in 5 seconds.
(And if you wanted to synchronize between app service instances, you could 'quickly' save a flag to the database, in a transaction with proper isolation level set. Or use e.g. Azure Redis Cache.)
Example solution
As Kit noted, rightly so, I didn't provide a full solution above.
So, a crude implementation could go like this:
public class SomeQueryService : ISomeQueryService
{
private static int _hasStartedFlag;
private static bool TrySetStarted()
=> Interlocked.CompareExchange(ref _hasStartedFlag, 1, 0) == 0;
private static void SetFinished()
=> Interlocked.Exchange(ref _hasStartedFlag, 0);
public async Task<(bool couldExecute, object result)> TryExecute()
{
if (!TrySetStarted())
return (couldExecute: false, result: null);
// Safely execute long query.
SetFinished();
return (couldExecute: true, result: result);
}
}
// In the controller, obviously
[HttpGet()]
public async Task<IActionResult> DoLongQuery([FromServices] ISomeQueryService someQueryService)
{
var (couldExecute, result) = await someQueryService.TryExecute();
if (!couldExecute)
{
return new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = "Another request has already started. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
return Ok(result);
}
Of course possibly you'd want to extract the 'blocking' logic from the controller action into somewhere else, for example an action filter. In that case the flag should also go into a separate component that could be shared between the query service and the filter.
General use action filter
I felt bad about my inelegant solution above, and I realized that this problem can be generalized into basically a connection number limiter on an endpoint.
I wrote this small action filter that can be applied to any endpoint (multiple endpoints), and it accepts the number of allowed connections:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConcurrencyLimiterAttribute : ActionFilterAttribute
{
private readonly int _allowedConnections;
private static readonly ConcurrentDictionary<string, int> _connections = new ConcurrentDictionary<string, int>();
public ConcurrencyLimiterAttribute(int allowedConnections = 1)
=> _allowedConnections = allowedConnections;
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var key = context.HttpContext.Request.Path;
if (_connections.AddOrUpdate(key, 1, (k, v) => ++v) > _allowedConnections)
{
Close(withError: true);
return;
}
try
{
await next();
}
finally
{
Close();
}
void Close(bool withError = false)
{
if (withError)
{
context.Result = new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = $"Maximum {_allowedConnections} simultaneous connections are allowed. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
_connections.AddOrUpdate(key, 0, (k, v) => --v);
}
}
}

AutoFac Request-scope is not WHOLE request

I just set up AutoFac in my MVC project and set my EF DbContext to InstancePerRequest. Until now DbContext has been a static singleton. It seems that AutoFac's "request" scope is not really the entire request. I'm getting the error message:
The operation cannot be completed because the DbContext has been disposed.
Much like some other questions.
I would expect the context to be disposed after the entire request has been handled, but the exception is showing up when I try to access lazily loaded properties in views or return statements.
An example of the problem:
[HttpGet]
public JsonResult GetLotoLockNumbers(int permitId)
{
var lockNums = lockService.FindLotoLockNumbers(permitId);
// this line throws an exception because... (go to last line)
return Json(string.Join(", ", lockNums), JsonRequestBehavior.AllowGet);
}
public IEnumerable<string> FindLotoLockNumbers(int permitId)
{
// returns an attached entity
var permit = Repository.GetDBEntityWithId<PermPermit>(permitId);
return GetLotoLockNumsForPermit(permit).AsEnumerable();
}
private IEnumerable<string> GetLotoLockNumsForPermit(PermPermit permit, int? configLockItemId = null)
{
// lazily accesses virtual properties
var lotoLockItems = permit.permitSections.SelectMany(section => section.childs.Where(
(sectionItem) => IsLotoLockSectionItem(sectionItem, configLockItemId))).AsEnumerable();
return lotoLockItems.SelectMany(item => LotoLockHelper.ParseLockNums(item.value)).AsEnumerable();
}
private bool IsLotoLockSectionItem(PermPermitSectionItem item, int? configLockItemId = null)
{
// ...the context has been exposed when this line gets lazily executed
return lockItemIds.Contains(item.confSectionItem.id);
}
My favourite solution would be if I could simply extend the scope of the context so that it lasts for the whole request (a singleton is not ok). Otherwise, I'll have to make changes throughout the code to make sure that everything is loaded before controller actions return.
I was all wrong about AutoFac. The problem in the code here is not that the query's execution is deferred, it's that the lockItemIds object is an IEnumerable who's execution gets deferred. I loaded the list and stored it in a static variable using an old context and didn't realize that it wasn't executed immediately.
AutoFac's request scope is perfect.
The lesson is: use the debugger to make sure you know exactly which object is causing the problem and check your static variables.

Properly handling DbContexts in ASP.NET Core WebApi

Got a small confusion here.
I'm not sure if I am handling my DbContext throughout the WebApi properly.
I do have some controllers that do some operations on my DB (Inserts/Updates with EF) and after doing these actions I do trigger an event.
In my EventArgs (I have a custom class which inherits from EventArgs) I pass my DbContext and I use it in the event handler to log these operations (basically I just log authenticated user API requests).
In the event handler when I am trying to commit my changes (await SaveChangesAsync) I get an error : "Using a disposed object...etc" basically noticing me that at the first time I use await in my async void (fire and forget) I notify the caller to dispose the Dbcontext object.
Not using async works and the only workaround that I've mangaged to put out is by creating another instance of DbContext by getting the SQLConnectionString of the EventArgs passed DbContext.
Before posting I did made a small research based on my issue
Entity Framework disposing with async controllers in Web api/MVC
This is how I pass parameters to my OnRequestCompletedEvent
OnRequestCompleted(dbContext: dbContext,requestJson: JsonConvert.SerializeObject);
This is the OnRequestCompleted() declaration
protected virtual void OnRequestCompleted(int typeOfQuery,PartnerFiscalNumberContext dbContext,string requestJson,string appId)
{
RequestCompleted?.Invoke(this,new MiningResultEventArgs()
{
TypeOfQuery = typeOfQuery,
DbContext = dbContext,
RequestJson = requestJson,
AppId = appId
});
}
And this is how I process and use my dbContext
var appId = miningResultEventArgs.AppId;
var requestJson = miningResultEventArgs.RequestJson;
var typeOfQuery = miningResultEventArgs.TypeOfQuery;
var requestType = miningResultEventArgs.DbContext.RequestType.FirstAsync(x => x.Id == typeOfQuery).Result;
var apiUserRequester = miningResultEventArgs.DbContext.ApiUsers.FirstAsync(x => x.AppId == appId).Result;
var apiRequest = new ApiUserRequest()
{
ApiUser = apiUserRequester,
RequestJson = requestJson,
RequestType = requestType
};
miningResultEventArgs.DbContext.ApiUserRequests.Add(apiRequest);
await miningResultEventArgs.DbContext.SaveChangesAsync();
By using SaveChanges instead of SaveChangesAsync everything works.
My only idea is to create another dbContext by passing the previous DbContext's SQL connection string
var dbOptions = new DbContextOptionsBuilder<PartnerFiscalNumberContext>();
dbOptions.UseSqlServer(miningResultEventArgs.DbContext.Database.GetDbConnection().ConnectionString);
using (var dbContext = new PartnerFiscalNumberContext(dbOptions.Options))
{
var appId = miningResultEventArgs.AppId;
var requestJson = miningResultEventArgs.RequestJson;
var typeOfQuery = miningResultEventArgs.TypeOfQuery;
var requestType = await dbContext.RequestType.FirstAsync(x => x.Id == typeOfQuery);
var apiUserRequester = await dbContext.ApiUsers.FirstAsync(x => x.AppId == appId);
var apiRequest = new ApiUserRequest()
{
ApiUser = apiUserRequester,
RequestJson = requestJson,
RequestType = requestType
};
dbContext.ApiUserRequests.Add(apiRequest);
await dbContext.SaveChangesAsync();
}
The latter code excerpt is just a small test to check my supposition, basically I should pass the SQL connection string instead of the DbContext object.
I am not sure (in terms of best practice) if I should pass a connection string and create a new dbContext object (and dispose it by using a using clause) or if I should use/have another mindset for this issue.
From what I know, using a DbContext should be done for a limited set of operations and not for multiple purposes.
EDIT 01
I'm going to detail more thorough what I've been doing down below.
I think I got an idea of why this error happens.
I have 2 controllers
One that receives a JSON and after de-serializing it I return a JSON to the caller and another controller that gets a JSON that encapsulates a list of objects that I iterate in an async way, returning an Ok() status.
The controllers are declared as async Task<IActionResult> and both feature an async execution of 2 similar methods.
The first one that returns a JSON executes this method
await ProcessFiscalNo(requestFiscalView.FiscalNo, dbContext);
The second one (the one that triggers this error)
foreach (string t in requestFiscalBulkView.FiscalNoList)
await ProcessFiscalNo(t, dbContext);
Both methods (the ones defined previously) start an event OnOperationComplete()
Within that method I execute the code from my post's beginning.
Within the ProcessFiscalNo method I DO NOT use any using contexts nor do I dispose the dbContext variable.
Within this method I only commit 2 major actions either updating an existing sql row or inserting it.
For edit contexts I select the row and tag the row with the modified label by doing this
dbContext.Entry(partnerFiscalNumber).State = EntityState.Modified;
or by inserting the row
dbContext.FiscalNumbers.Add(partnerFiscalNumber);
and finally I execute an await dbContext.SaveChangesAsync();
The error always gets triggered within the EventHandler ( the one detailed # the beginning of the thread) during the await dbContext.SaveChangedAsync()
which is pretty weird since 2 lines before that I do await reads on my DB with EF.
var requestType = await dbContext.RequestType.FirstAsync(x => x.Id == typeOfQuery);
var apiUserRequester = await dbContext.ApiUsers.FirstAsync(x => x.AppId == appId);
dbContext.ApiUserRequests.Add(new ApiUserRequest() { ApiUser = apiUserRequester, RequestJson = requestJson, RequestType = requestType });
//this throws the error
await dbContext.SaveChangesAsync();
For some reason calling await within the Event Handler notifies the caller to dispose the DbContext object.
Also by re-creating the DbContext and not re-using the old one I see a huge improvement on access.
Somehow when I use the first controller and return the info the DbContext object appears to get flagged by the CLR for disposal but for some unknown reason it still functions.
EDIT 02
Sorry for the bulk-ish content that follows, but I've placed all of the areas where I do use dbContext.
This is how I'm propagating my dbContext to all my controllers that request it.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMemoryCache();
// Add framework services.
services.AddOptions();
var connection = #"Server=.;Database=CrawlerSbDb;Trusted_Connection=True;";
services.AddDbContext<PartnerFiscalNumberContext>(options => options.UseSqlServer(connection));
services.AddMvc();
services.AddAuthorization(options =>
{
options.AddPolicy("PowerUser",
policy => policy.Requirements.Add(new UserRequirement(isPowerUser: true)));
});
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthorizationHandler, UserTypeHandler>();
}
In Configure I'm using the dbContext for my custom MiddleWare
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
var context = app.ApplicationServices.GetService<PartnerFiscalNumberContext>();
app.UseHmacAuthentication(new HmacOptions(),context);
app.UseMvc();
}
In the custom MiddleWare I'm only using it for a query.
public HmacHandler(IHttpContextAccessor httpContextAccessor, IMemoryCache memoryCache, PartnerFiscalNumberContext partnerFiscalNumberContext)
{
_httpContextAccessor = httpContextAccessor;
_memoryCache = memoryCache;
_partnerFiscalNumberContext = partnerFiscalNumberContext;
AllowedApps.AddRange(
_partnerFiscalNumberContext.ApiUsers
.Where(x => x.Blocked == false)
.Where(x => !AllowedApps.ContainsKey(x.AppId))
.Select(x => new KeyValuePair<string, string>(x.AppId, x.ApiHash)));
}
In my controller's CTOR I'm passing the dbContext
public FiscalNumberController(PartnerFiscalNumberContext partnerContext)
{
_partnerContext = partnerContext;
}
This is my Post
[HttpPost]
[Produces("application/json", Type = typeof(PartnerFiscalNumber))]
[Consumes("application/json")]
public async Task<IActionResult> Post([FromBody]RequestFiscalView value)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var partnerFiscalNo = await _fiscalNoProcessor.ProcessFiscalNoSingle(value, _partnerContext);
}
Within the ProcessFiscalNoSingle method I have the following usage, If that partner exists then I'll grab him, if not, create and return him.
internal async Task<PartnerFiscalNumber> ProcessFiscalNoSingle(RequestFiscalView requestFiscalView, PartnerFiscalNumberContext dbContext)
{
var queriedFiscalNumber = await dbContext.FiscalNumbers.FirstOrDefaultAsync(x => x.FiscalNo == requestFiscalView.FiscalNo && requestFiscalView.ForceRefresh == false) ??
await ProcessFiscalNo(requestFiscalView.FiscalNo, dbContext, TypeOfQuery.Single);
OnRequestCompleted(typeOfQuery: (int)TypeOfQuery.Single, dbContextConnString: dbContext.Database.GetDbConnection().ConnectionString, requestJson: JsonConvert.SerializeObject(requestFiscalView), appId: requestFiscalView.RequesterAppId);
return queriedFiscalNumber;
}
Further down in the code, there's the ProcessFiscalNo method where I use the dbContext
var existingItem =
dbContext.FiscalNumbers.FirstOrDefault(x => x.FiscalNo == partnerFiscalNumber.FiscalNo);
if (existingItem != null)
{
var existingGuid = existingItem.Id;
partnerFiscalNumber = existingItem;
partnerFiscalNumber.Id = existingGuid;
partnerFiscalNumber.ChangeDate = DateTime.Now;
dbContext.Entry(partnerFiscalNumber).State = EntityState.Modified;
}
else
dbContext.FiscalNumbers.Add(partnerFiscalNumber);
//this gets always executed at the end of this method
await dbContext.SaveChangesAsync();
Also I've got an Event called OnRequestCompleted() where I pass my actual dbContext (after it ends up with SaveChangesAsync() if I update/create it)
The way I initiate the event args.
RequestCompleted?.Invoke(this, new MiningResultEventArgs()
{
TypeOfQuery = typeOfQuery,
DbContextConnStr = dbContextConnString,
RequestJson = requestJson,
AppId = appId
});
This is the notifier class (where the error occurs)
internal class RequestNotifier : ISbMineCompletionNotify
{
public async void UploadRequestStatus(object source, MiningResultEventArgs miningResultArgs)
{
await RequestUploader(miningResultArgs);
}
/// <summary>
/// API Request Results to DB
/// </summary>
/// <param name="miningResultEventArgs">EventArgs type of a class that contains requester info (check MiningResultEventArgs class)</param>
/// <returns></returns>
private async Task RequestUploader(MiningResultEventArgs miningResultEventArgs)
{
//ToDo - fix the following bug : Not being able to re-use the initial DbContext (that's being used in the pipeline middleware and controller area),
//ToDo - basically I am forced by the bug to re-create the DbContext object
var dbOptions = new DbContextOptionsBuilder<PartnerFiscalNumberContext>();
dbOptions.UseSqlServer(miningResultEventArgs.DbContextConnStr);
using (var dbContext = new PartnerFiscalNumberContext(dbOptions.Options))
{
var appId = miningResultEventArgs.AppId;
var requestJson = miningResultEventArgs.RequestJson;
var typeOfQuery = miningResultEventArgs.TypeOfQuery;
var requestType = await dbContext.RequestType.FirstAsync(x => x.Id == typeOfQuery);
var apiUserRequester = await dbContext.ApiUsers.FirstAsync(x => x.AppId == appId);
var apiRequest = new ApiUserRequest()
{
ApiUser = apiUserRequester,
RequestJson = requestJson,
RequestType = requestType
};
dbContext.ApiUserRequests.Add(apiRequest);
await dbContext.SaveChangesAsync();
}
}
}
Somehow when the dbContext reaches the Event Handler CLR gets notified to dispose the dbContext object (because I'm using await?)
Without recreating the object I was having huge lag when I wanted to use it.
While writing this I have an idea, I did upgrade my solution to 1.1.0 and I'm gonna try to see if it behaves similarly.
Concerning Why you get the error
As pointed out at the Comments by #set-fu DbContext is not thread safe.
In addition to that, since there is no explicit lifetime management of your DbContext your DbContext is going to get disposed when the garbage collector sees fit.
Judging from your context, and your mention about Request scoped DbContext
I suppose you DI your DbContext in your controller's constructor.
And since your DbContext is request scoped it is going to be disposed as soon as your Request is over,
BUT since you have already fired and forgot your OnRequestCompleted events there is no guarantee that your DbContext won't be disposed.
From there on , the fact that one of our methods succeeds and the other fails i think is seer "Luck".
One method might be faster than the other and completes before the Garbage collector disposes the DbContext.
What you can do about this is to change the return type of your Events from
async void
To
async Task<T>
This way you can wait your RequestCompleted Task within your controller to finish and that will guarantee you that your Controller/DbContext will not get Disposed until your RequestCompleted task is finished.
Concerning Properly handling DbContexts
There are two contradicting recommendations here by microsoft and many people use DbContexts in a completely divergent manner.
One recommendation is to "Dispose DbContexts as soon as posible"
because having a DbContext Alive occupies valuable resources like db
connections etc....
The other states that One DbContext per request is highly
reccomended
Those contradict to each other because if your Request is doing a lot of unrelated to the Db stuff , then your DbContext is kept for no reason.
Thus it is waste to keep your DbContext alive while your request is just waiting for random stuff to get done...
So many people who follow rule 1 have their DbContexts inside their "Repository pattern" and create a new Instance per Database Query
public User GetUser(int id)
{
User usr = null;
using (Context db = new Context())
{
usr = db.Users.Find(id);
}
return usr;
}
They just get their data and dispose the context ASAP.
This is considered by MANY people an acceptable practice.
While this has the benefits of occupying your db resources for the minimum time it clearly sacrifices all the UnitOfWork and "Caching" candy EF has to offer.
So Microsoft's recommendation about using 1 Db Context per request it's clearly based on the fact that your UnitOfWork is scoped within 1 request.
But in many cases and i believe your case also this is not true.
I consider Logging a separate UnitOfWork thus having a new DbContext for your Post-Request Logging is completely acceptable (And that's the practice i also use).
An Example from my project i have 3 DbContexts in 1 Request for 3 Units Of Work.
Do Work
Write Logs
Send Emails to administrators.

Bind Vita ORM using Ninject

I am new to both Ninject and the Vita ORM (vita docs) and am just having issues coming up with a binding strategy for dependency injection and would really appreciate any help.
Firstly, my application in split into 3 layers, namely the data layer ( using Vita ORM so in essence it is a single EntityApp class similar to dbContext for EF), then I have a service layer with well defined interfaces ( and existing implementations using my current repository pattern using dapper.net ) and finally a simple MVC web application with controllers calling off to the service layer.
Currently I am using ninject constructor intection to inject the repo into the service and the service into the controller and scoping them accordingly. this works great because everything is interface driven and there is no shared context between services / repositories.
But now with the introduction of an "entity context" I need to create the context once in the app ( so singleton scope ) and then would in essence like to open a new session per request and pass that to any service layer objects that require it. Over and above this, the ORM needs to be initialized at application startup ( or I suppose it can be done lazily, but at app start would be better )
Here is some code generated by vita as to how to initialize the ORM
class Program {
public static MyEntityApp App;
static void Main(string[] args) {
Console.WriteLine(" Sample application for VITA-generated model. ");
Init();
//Open session and run query
var session = App.OpenSession();
var query = from ent in session.EntitySet<IConnections>() // just random entity
// where ?condition?
select ent;
var entities = query.Take(5).ToList();
Console.WriteLine("Loaded " + entities.Count + " entities.");
foreach(var ent in entities)
Console.WriteLine(" Entity: " + ent.ToString()); // change to smth more meaningful
Console.WriteLine("Press any key ...");
Console.ReadKey();
}
private static void Init() {
App = new MyEntityApp();
App.CacheSettings.AddCachedTypes(CacheType.FullSet /* , <fully cached entity types> */ );
App.CacheSettings.AddCachedTypes(CacheType.Sparse /* , <sparsely cached entity types> */ );
var connString = #".......";
var driver = new Vita.Data.MsSql.MsSqlDbDriver();
App.LogPath = "_appLog.log";
var dbSettings = new DbSettings(driver, DbOptions.Default, connString, upgradeMode: DbUpgradeMode.Always);
App.ConnectTo(dbSettings);
}
}
As you can see they initialize and set a static variable which has reference to the context container. As can also be seen by the above is that you need to call var session = App.OpenSession(); to work with the context, so I was hoping to create one session per request and then inject that session into the service object contructors.
So here is what I have done so far
/*Map the Vita ORM session into the request scope*/
kernel.Bind<MyEntities>().ToSelf().InSingletonScope();
I am assuming that will call the contructor, and inside there I have initialized the context correctly ( and this should also only be called once )
then in the service objects i want to do something like this
here is the service impl
private IEntitySession _Session { get; set; }
public VitaSettingsServiceImpl(IEntitySession Session)
{
_Session = Session;
}
And here is my attempt at injecting that session object...
kernel.Bind<ISettingsService>().To<VitaSettingsServiceImpl>().InRequestScope().WithConstructorArgument("Session", [WANT TO CALL MYENTITIES.OpenSession() HERE]);
As you can see it is that last binding that is stumping me? how do I bind a contructor object param to a method call on an existing singleton bound object?
Like I said in the begining, I am VERY green at this and maybe I am going about it all wrong, but i have scoured the web and cant find any info regarding these to technologies being used together, so any help would be greatly appreciated
So for all the lonely souls trolling the web with similar issues, here is what I eventually cam up with :
/*Map the Vita ORM session into the request scope*/
kernel.Bind<SorbetEntities>().ToSelf().InSingletonScope();
/*Map all the services to their respective implementations*/
kernel.Bind<ISettingsService>().To<sorbet.Vita.VitaSettingsServiceImpl>().InRequestScope().WithConstructorArgument("Session", CreateVitaSession);
and then a static extension method to execute the method that will return my connection context
private static object CreateVitaSession(IContext context)
{
return context.Kernel.Get<SorbetEntities>().OpenSession();
}
And thats it. now i get a new connection context per request and I am a happy camper

How to use Lazy to handle concurrent request?

I'm new in C# and trying to understand how to work with Lazy.
I need to handle concurrent request by waiting the result of an already running operation. Requests for data may come in simultaneously with same/different credentials.
For each unique set of credentials there can be at most one GetDataInternal call in progress, with the result from that one call returned to all queued waiters when it is ready
private readonly ConcurrentDictionary<Credential, Lazy<Data>> Cache
= new ConcurrentDictionary<Credential, Lazy<Data>>();
public Data GetData(Credential credential)
{
// This instance will be thrown away if a cached
// value with our "credential" key already exists.
Lazy<Data> newLazy = new Lazy<Data>(
() => GetDataInternal(credential),
LazyThreadSafetyMode.ExecutionAndPublication
);
Lazy<Data> lazy = Cache.GetOrAdd(credential, newLazy);
bool added = ReferenceEquals(newLazy, lazy); // If true, we won the race.
Data data;
try
{
// Wait for the GetDataInternal call to complete.
data = lazy.Value;
}
finally
{
// Only the thread which created the cache value
// is allowed to remove it, to prevent races.
if (added) {
Cache.TryRemove(credential, out lazy);
}
}
return data;
}
Is that right way to use Lazy or my code is not safe?
Update:
Is it good idea to start using MemoryCache instead of ConcurrentDictionary? If yes, how to create a key value, because it's a string inside MemoryCache.Default.AddOrGetExisting()
This is correct. This is a standard pattern (except for the removal) and it's a really good cache because it prevents cache stampeding.
I'm not sure you want to remove from the cache when the computation is done because the computation will be redone over and over that way. If you don't need the removal you can simplify the code by basically deleting the second half.
Note, that Lazy has a problem in the case of an exception: The exception is stored and the factory will never be re-executed. The problem persists forever (until a human restarts the app). In my mind this makes Lazy completely unsuitable for production use in most cases.
This means that a transient error such as a network issue can render the app unavailable permanently.
This answer is directed to the updated part of the original question. See #usr answer regarding thread-safety with Lazy<T> and the potential pitfalls.
I would like to know how to avoid using ConcurrentDictionary<TKey, TValue> and start
using MemoryCache? How to implement
MemoryCache.Default.AddOrGetExisting()?
If you're looking for a cache which has a mechanism for auto expiry, then MemoryCache is a good choice if you don't want to implement the mechanics yourself.
In order to utilize MemoryCache which forces a string representation for a key, you'll need to create a unique string representation of a credential, perhaps a given user id or a unique username?
If you can, you can create an override of ToString which represents your unique identifier or simply use the said property, and utilize MemoryCache like this:
public class Credential
{
public Credential(int userId)
{
UserId = userId;
}
public int UserId { get; private set; }
}
And now your method will look like this:
private const EvictionIntervalMinutes = 10;
public Data GetData(Credential credential)
{
Lazy<Data> newLazy = new Lazy<Data>(
() => GetDataInternal(credential), LazyThreadSafetyMode.ExecutionAndPublication);
CacheItemPolicy evictionPolicy = new CacheItemPolicy
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(EvictionIntervalMinutes)
};
var result = MemoryCache.Default.AddOrGetExisting(
new CacheItem(credential.UserId.ToString(), newLazy), evictionPolicy);
return result != null ? ((Lazy<Data>)result.Value).Value : newLazy.Value;
}
MemoryCache provides you with a thread-safe implementation, this means that two threads accessing AddOrGetExisting will only cause a single cache item to be added or retrieved. Further, Lazy<T> with ExecutionAndPublication guarantess only a single unique invocation of the factory method.

Categories

Resources