Paul
Paul

Reputation: 36329

Risks of holding an Entity Framework dynamic proxy object in session?

So I have a fairly comprehensive activity-based access control system I built for a web app under MVC 4 using Entity Framework. Well, to be precise the access control doesn't care if it's using EF or not, but the app is.

Anyway, I'm loading the user's permissions on each request right now. I get a reference to my DbContext injected from the IoC container into my ApplicationController, and it overrides OnAuthorization to stuff the user's profile into the HttpContext.Current.Items. Seems to work fairly well, but I can't help but wonder if it's the best way.

My thought was that since the users' permissions don't change often, if ever, the better way to do it would be to load the profile of permissions into the Session instead, and then not have to change them at all until the user logs out and logs back in (pretty common in desktop OS's anyway). But I'm concerned that if I fetch using the DbContext, then the object I get back is a dynamic proxy which holds a reference to the DbContext and I certainly don't want to do that for the whole session.

Thoughts? Is this a good approach, and if so how do I ensure that my DbContext doesn't linger beyond when I really need it?

Upvotes: 1

Views: 940

Answers (2)

Perdom
Perdom

Reputation: 56

Thoughts? Is this a good approach?

Another problem attached to this approach is when you use the common pattern that create one dbContext per http request. This pattern typically dispose dbContext when the request ends.

protected virtual void Application_EndRequest(object sender, EventArgs e)

But what happen when we try to get navigation property of a proxy entity which reference to a disposed DbContext?

We will get a ObjectDisposedException

Upvotes: 1

danludwig
danludwig

Reputation: 47375

Invoke .AsNoTracking() on the Set<UserPermission> before you query out. Entities will still be proxied, but will be detached from the DbContext.

var userPermission = dbContext.Set<UserPermission>().AsNoTracking()
    .SingleOrDefault(x => x.UserName == User.Identity.Name);

Thoughts? Is this a good approach?

Putting a dynamically proxied entity in session will break as soon as you load balance your code across more than 1 web server. Why? Because of the dynamic proxy class. Server A understands the type DynamicProxies.UserPermission_Guid, because it queried out the entity. However Server B through N do not, and therefore cannot deserialize it from the Session. The other servers will dynamically proxy the entity with a different GUID.

That said, you could DTO your data into a POCO object and put it in session instead. However then you do not need to worry about your entity being attached to the context when you first query it out. AsNoTracking will only make the query perform a bit faster.

// you can still call .AsNoTracking for performance reasons
var userPermissionEntity = dbContext.Set<UserPermission>().AsNoTracking()
    .SingleOrDefault(x => x.UserName == User.Identity.Name);

// this can safely be put into session and restored by any server with a
// reference to the DLL where the DTO class is defined.
var userPermissionSession = new UserPermissionInSession
{
    UserName = userPermissionEntity.UserName,
    // etc.
};

Upvotes: 1

Related Questions