Home » Ado.Net » Enterprise Asp.Net MVC Part 5: Database Context and Impersonate data

Enterprise Asp.Net MVC Part 5: Database Context and Impersonate data

The database context is abstracting the connection between entity and Entity Framework. We won’t abstract all method of the Entity Framework and Linq to Entity like “Where”, “Select”, “Find”, “First”, etc but we will abstract the entry point : DbSet. In fact, the reason is to be able to add ability to impersonate later and to be able to configure your entity that you need to have this DatabaseContext. The role of the factory is not to configure Entity Framework, neither to impersonate. The database context role is to do those task.

public interface IDatabaseContext   
{
	int SaveChanges();
	IDbSet<TEntity> SetOwnable<TEntity>() where TEntity : class, IUserOwnable;
	DbSet<TEntity> Set<TEntity>() where TEntity : class;
	DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
	void InitializeDatabase();
	UserProfileImpersonate Impersonate(ICurrentUser userProfile);
}

For the moment, the interface of IDatabaseContext looks like this. We have a SaveChanges because we might want to do operation over several repository and want to manually commit changes in a specific time. This will be the role of SaveChanges method. The SetOwnable<> method will act like the default Set method but will automatically assign the user to the entity. This will be good for the loading and for the saving. When in the loading, we won’t have to specify every time that we want the workout for the userA, etc. It will be automatically. This save us time, possibility of error and also improve the security because by default, everything will be bound the the current user. The InitializeDatabase method will be a method to configure extra database stuff. For example, in this project, I am using this method to setup the WebSecurity (membership layout for WebMatrix). The last method is the method that will give us some impersonation for the time of a query depending of another user profile.

public class DatabaseContext : DbContext, IDatabaseContext
{
	public const string DEFAULTCONNECTION = "DefaultConnection";

	public DatabaseContext(IUserProvider userProvider)
	{
		UserProvider = userProvider;

		base.Database.Connection.ConnectionString = ConfigurationManager.ConnectionStrings[DEFAULTCONNECTION].ConnectionString;
		Configuration.ProxyCreationEnabled = false;
	}

	public IUserProvider UserProvider { get; set; }

	public ICurrentUser CurrentUser
	{
		get { return UserProvider.Account; }
	}

        public new DbSet<TEntity> Set<TEntity>() where TEntity : class
        {
            if (typeof(IUserOwnable) is TEntity)
            {
                throw new SecurityException("You cannot by pass the ownable security");
            }
            return base.Set<TEntity>();
        }
	public IDbSet<TEntity> SetOwnable<TEntity>() where TEntity : class, IUserOwnable
	{
		return new FilteredDbSet<TEntity>(this, entity => entity.UserId == CurrentUser.UserId, entity => entity.UserId = CurrentUser.UserId);
	}

	public void InitializeDatabase()
	{
		WebSecurity.InitializeDatabaseConnection(DEFAULTCONNECTION, "UserProfile", "UserId", "UserName", autoCreateTables: true);
	}

	protected override void OnModelCreating(DbModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);
		//Call here some other classes to build the configuration of Entity Framework
	}

	public UserProfileImpersonate Impersonate(ICurrentUser userProfile)
	{
		return new UserProfileImpersonate(this, userProfile);
	}
}

This is a small example. That talk for itself. The two interesting part is the SetOwnable that use a FilteredDbSet which the code has been trimmed from a version that you can find over the web and that we will discuss later. The other part is the Impersonate method that we will talk now.

Lets start with the end result. For now, if you want to insert into the database a new Workout entity you need in the WorkoutRepository to do :

DatabaseContext.SetOwnable<Workout>().Add(entity);      

This will automatically insert a new workout to the current logged user. If you want to change the user, you could use the Set but because we override the Set method and check if the it inherit from the IUserOwnable interface. This is the required interface to use SetOwnable method. This way, we can get the user id. But, to protect developer to by pass this mechanism, an exception is thrown if we use Set method with entity that are ownable. That doesn’t mean that you cannot save to an other user, but will require more work with impersonating. Why adding some over head and not letting the developer directly use the Set when he want to save an entity to someone else authority? Simply because all entity will inherit from IUserOwnable because it will be a lot easier to work with without having to always specify the user inside the repository. Also, repository doesn’t have access directly to the user id. That’s say, it’s a painful process. Not letting access directly to the Set avoid the mistake to simply user Set method for an entity. An exception will be thrown and the developer will automatically remember to user the SetOwnable method instead. If he really mean to use the Set method, than the impersonate method will be appropriate.

For general entity, let say that we have a list of status that are shared across all entities or shared across all users, the entity won’t inherit of IUserOwnable because it’s not a user ownable entity. So in theory it works, let check in practice!

using (var db = DatabaseContext.Impersonate(new UserProfile { UserId = 1 }))
{
     db.SetOwnable<Workout>().Add(entity);
}

This would be in the repository instead of the last piece of code. As you can see, we impersonate with a UserProfile with the Id 1. The code is around curly bracket and give us the scope of when the impersonation start and end.

The DatabaseContext class implementation of Impersonate simply call a new DbContext.

public UserProfileImpersonate Impersonate(ICurrentUser userProfile)
{
    return new UserProfileImpersonate(this, userProfile);
}

A new class is used because we want to have a scope which is done by inherit from IDisposable interface. we will create a new instance of Impersonate and dispose it to come back with the real Current User and not the impersonate one. The class is mostly the same as the DbContext but has a reference to the user profile before the impersonate because we want to set it back once it’s done.

public class UserProfileImpersonate : IDatabaseContext, IDisposable
{
	private readonly DatabaseContext _databaseContext;

	private readonly IUserProvider _oldUserProvider;

	#region Implementation of IDisposable

	public UserProfileImpersonate(DatabaseContext dbContext, ICurrentUser userProfile)
	{
		_databaseContext = dbContext;
		_oldUserProvider = dbContext.UserProvider;
		_databaseContext.UserProvider = new ImpersonateUserProvider(userProfile);
	}

	public void Dispose()
	{
		_databaseContext.UserProvider = _oldUserProvider;
	}

	#endregion

	#region Implementation of IDatabaseContext

	public int SaveChanges()
	{
		return _databaseContext.SaveChanges();
	}

	public IDbSet<TEntity> SetOwnable<TEntity>() where TEntity : class, IUserOwnable
	{
		return _databaseContext.SetOwnable<TEntity>();
	}

	public DbSet<TEntity> Set<TEntity>() where TEntity : class
	{
		return _databaseContext.Set<TEntity>();
	}

	public DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class
	{
		return _databaseContext.Entry(entity);
	}

	public void InitializeDatabase()
	{
		_databaseContext.InitializeDatabase();
	}

	public UserProfileImpersonate Impersonate(ICurrentUser userProfile)
	{
		return _databaseContext.Impersonate(userProfile);
	}

	#endregion
}

Simple isn’t? We simply call the same database context method but only change the current logged user profile. Single task to do which respect the single responsibility principle.

Series Articles

Article #1: Asp.Net MVC Enterprise Quality Web Application
Article #2: Asp.Net MVC Enterprise Quality Web Application Model
Article #3: Asp.Net MVC Enterprise Quality Web Application Controller
Article #4: Asp.Net MVC Enterprise Quality Web Repository Layer
Article #5: Asp.Net MVC Enterprise Quality Web with Entity Framework
Article #6: Asp.Net MVC Enterprise Quality Layers
Article #7: Asp.Net MVC Enterprise Quality Web Security

Source code on GitHub

If you like my article, think to buy my annual book, professionally edited by a proofreader. directly from me or on Amazon. I also wrote a TypeScript book called Holistic TypeScript

2 Responses so far.

  1. […] Quality Web Application Controller Article #4: Asp.Net MVC Enterprise Quality Web Repository Layer Article #5: Asp.Net MVC Enterprise Quality Web with Entity Framework Article #6: Asp.Net MVC Enterprise Quality Layers Article #7: Asp.Net MVC Enterprise Quality Web […]

  2. […] Quality Web Application Controller Article #4: Asp.Net MVC Enterprise Quality Web Repository Layer Article #5: Asp.Net MVC Enterprise Quality Web with Entity Framework Article #6: Asp.Net MVC Enterprise Quality Layers Article #7: Asp.Net MVC Enterprise Quality Web […]

Leave a Reply to Enterprise Asp.Net MVC Part 4: Repository | Patrick Desjardins' Blog Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.