EF Code First Common Practices

Posted by ben 13. April 2011 08:56

This post covers some common practices for working with Entity Framework Code First (or EF 4.1 as was just released today).

Session Per Request

With Session-Per-Request our entity framework DbContext implementation is shared across a http request. This is a fairly standard approach when working with ORMs (we do something similar with NHibernate’s ISession).

The first thing we did was to make our DbContext a dependency within our repositories. We could then use our IoC container to inject the context and scope it accordingly (per http request). However, this gave us some unwanted coupling between our repository classes and Entity Framework so we decided to abstract DbContext into a simple context, as seen below:

    public interface IDbContext {
        IDbSet<TEntity> Set<TEntity>() where TEntity : class;
        int SaveChanges();
    }

Strictly speaking there is still some coupling since IDbSet<T> is part of System.Data.Entity but overall this interface makes things more testable.

Our application context, then implements this interface, in addition to inheriting from the Entity Framework DbContext:

    public class PortfolioContext : DbContext, IDbContext
    {
        public PortfolioContext(string connectionStringName) : base(connectionStringName) {}
        
        public IDbSet<Project> Projects { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            modelBuilder.Configurations.Add(new ProjectMap());
            base.OnModelCreating(modelBuilder);
        }

        public string CreateDatabaseScript() {
            return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
        }

        public new IDbSet<TEntity> Set<TEntity>() where TEntity : class {
            return base.Set<TEntity>();
        }
    }

I can then inject IDbContext into my repositories instead of my concrete DbContext. The code below shows the StructureMap configuration to handle session per request:

For<IDbContext>().HttpContextScoped()
	.Use(ctx => new PortfolioContext("Portfolio"));

Specifying the connection provider using your connection string

If you’ve installed the SQL Compact Edition Code First NuGet package it will add some pre app start code that sets the default connection factory to the SQL CE 4.0 provider.

In my experience this is not necessary and you can change the provider just by specifying it in your connection string; for example:

    <add name="Portfolio" connectionString="Data Source=|DataDirectory|Portfolio.sdf" providerName="System.Data.SqlServerCe.4.0" />
    <add name="Portfolio" connectionString="Data Source=(local)\sqlexpress;user id=sa;password=xxxxx;Database=portfolio" providerName="System.Data.SqlClient" />

Generic Repositories

People have mixed views about generic repositories. I find that they can save a lot of code duplication. They are only really of value due to the IQueryable collections (and delayed execution) that EF provides (just like we get with NH Linq).

We typically have a service layer that will consume the generic repositories and construct more specific queries e.g GetRecentPosts.

These services will have a dependency on IRepository<T> that is located in a common assembly (we actually use the same interface when working with NHibernate):

    public interface IRepository<T> where T : class
    {
        void DeleteOnSubmit(T entity);
        IQueryable<T> GetAll();
        T GetById(object id);
        void SaveOrUpdate(T entity);
    }

Our EF repository implementation can be seen below:

    public class EFRepository<T> : IRepository<T> where T : class, IEntity
    {
        protected readonly IDbContext context;
        protected readonly IDbSet<T> entities;

        public EFRepository(IDbContext context) {
            this.context = context;
            entities = context.Set<T>();
        }

        public virtual T GetById(object id) {
            return entities.Find(id);
        }

        public virtual IQueryable<T> GetAll() {
            return entities;
        }

        public virtual void SaveOrUpdate(T entity) {
            if (entities.Find(entity.Id) == null) {
                entities.Add(entity);
            }

            context.SaveChanges();
        }

        public virtual void DeleteOnSubmit(T entity) {
            entities.Remove(entity);

            context.SaveChanges();
        }
    }

Notice the generic constraint that our class must implement IEntity. This is just a simple interface that defines an Id property:

    public interface IEntity
    {
        Guid Id { get; set; }
    }

Since StructureMap supports generic types, we can now register our generic interface like so:

For(typeof(IRepository<>)).Use(typeof(EFRepository<>));

Occasionally the generic repository didn’t meet all our requirements. We had a number of cases where we needed to eagerly load associations. Fortunately, since the EFRepository methods are virtual we can just create a new implementation and override the necessary methods:

    public class ProjectRepository : EFRepository<Project> {

        public ProjectRepository(IDbContext context) 
            : base(context) { }
        
        public override Project GetById(object id) {
            var project = entities
                .Include(p => p.Template)
                .Include(p => p.Category)
                .SingleOrDefault(p => p.Id == (Guid)id);
            return project;
        }
    }

Now to use this implementation instead of the default, we just update our StructureMap configuration:

For<IRepository<Project>>().Use<ProjectRepository>();

Seeding the Database

Below is the database initializer that we use to create our database (if it doesn’t already exist).

The initializer accepts a boolean parameter that determines whether or not to create sample data (a setting from web.config):

    public class PortfolioDatabaseInitializer : IDatabaseInitializer<PortfolioContext>
    {
        private readonly bool createSampleData;
        
        public PortfolioDatabaseInitializer(bool createSampleData = false) {
            this.createSampleData = createSampleData;
        }
        
        public void InitializeDatabase(PortfolioContext context)
        {
            if (context.Database.Exists()) return;
            
            context.Database.Delete();
            context.Database.Create();

            // Add seed data
            Seed(context);
        }

        protected void Seed(PortfolioContext context)
        {
            context.ApplyTo(
                SetupData.InsertDefaultUser
            );

            if (createSampleData) {
                context.ApplyTo(
                    TestData.InsertTestImages,
                    TestData.InsertCategories,
                    TestData.InsertProjects,
                    TestData.InsertBlogPosts
                );
            }

            context.SaveChanges();
        }
    }

The context.ApplyTo method is a handy functional extension method, courtesy of Mike Hadlow:

public static void ApplyTo(this T arg, params Action[] actions) {
            Array.ForEach(actions, action => action(arg));
        }

This will apply the context to the specified methods, for example:

	public static void InsertCategories(IDbContext context)
	{
		category1 = new Category { Name = "Test Category 1", Slug = "test-category-1" };
		category2 = new Category { Name = "Test Category 2", Slug = "test-category-2" };

		context.AddRange(category1, category2);
	}

Testing entity persistence

Below is the base class we use for testing entity persistence. For this we use SQL Compact Edition as it works well in groups. Note that you don’t even need to set a connection string, it will just create the database in your test project output directory, in our case the database will be named “Portfolio.sdf”.

    [TestFixture]
    public abstract class PersistenceTest
    {
        protected IDbContext context;

        [SetUp]
        public void SetUp()
        {
            DbDatabase.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
            SetupContext();
            context.Database.Delete();
            context.Database.Create();
        }
      
        /// <summary>
        /// Persistance test helper
        /// </summary>
        /// <typeparam name="T">Entity type</typeparam>
        /// <param name="entity">Entity</param>
        protected T SaveAndLoadEntity<T>(T entity) where T : BaseEntity
        {
            context.Set<T>().Add(entity);
            context.SaveChanges();

            object id = entity.Id;

            context.Dispose();
            SetupContext();

            var fromDb = context.Set<T>().Find(id);
            fromDb.ShouldNotBeNull();
            return fromDb;
        }
		
	protected void SetupContext() {
		context = new PortfolioContext("Portfolio");
	}
    }

Finally an example persistence test:

	[Test]
	public void Can_save_and_load_project()
	{
		var project = new Project
		{
			Title = "Some project",
			Slug = "some-project",
			ShortDescription = "A short description",
			Description = "<strong>Html for the win</strong>",
		};

		SaveAndLoadEntity(project);
	}

Overall we are finding that code first along with the above practices makes Entity Framework much more enjoyable to use, and due to it’s partial trust support and great integration with SQL CE, we are using it over NH on smaller projects.

Tags:
Categories Entity Framework | ASP.NET | C# | Development

How to extend nopCommerce

Posted by ben 20. September 2010 23:58

Please note that this article applies to nopCommerce 1.80 only.

Since nopCommerce swapped out it’s existing provider based data access layer with the entity framework, there have been quite a few posts on the nopCommerce forums regarding how to extend nopCommerce.

In this post I am going to cover two of the most common requests

1) How to add a new property to an existing entity

2) How to add a new entity

Adding a new property to an existing entity

The process for this is very simple.

  1. Add your new property to the relevant class
  2. Update repository methods (insert/update)
  3. Add the field to your database table
  4. Update the Entity Framework model (edmx file)

In this example, I’m going to add a new property “Stylesheet” to my categories, that allows me to assign a specific CSS style sheet to each category.

So to begin with we should add our property. Rather than adding the property to the existing Category.cs file, I recommend you keep your extensions separate. Since the entity classes in nopCommerce are all partial classes, we can put our extension in a separate file.

image

We create our Category extension class in our extensions directory. We need to make sure that it sits in the same namespace as the existing category class so that the two are compiled together:

namespace NopSolutions.NopCommerce.BusinessLogic.Categories
{
    /// 
    /// Represents a category
    /// 
    public partial class Category : BaseEntity {
        public string Stylesheet { get; set; }
    }
}

Now that we have added our new property, we need to update the repository (or manager) insert and update methods accordingly (in the “adding a new entity” example, you will see my preferred approach for doing this).

We open up CategoryManager and find the insert and update methods, changing the method signatures like so:

        public static Category InsertCategory(string name, string description,
            int templateId, string metaKeywords, string metaDescription, string metaTitle,
            string seName, int parentCategoryId, int pictureId,
            int pageSize, string priceRanges, bool showOnHomePage, bool published, bool deleted,
            int displayOrder, DateTime createdOn, DateTime updatedOn, string stylesheet) { ... }

        public static Category UpdateCategory(int categoryId, string name, string description,
            int templateId, string metaKeywords, string metaDescription, string metaTitle,
            string seName, int parentCategoryId, int pictureId,
            int pageSize, string priceRanges, bool showOnHomePage, bool published, bool deleted,
            int displayOrder, DateTime createdOn, DateTime updatedOn, string stylesheet) { .... }

Note the additional variable “stylesheet”.

Of course now that we have changed these methods we need to update any code that references them. The easiest way of doing this is to right click the method and select “Find all references” from the context menu:

image

In this case we just need to update the CategoryInfo control used in administration (we will add a new textbox for our stylesheet property and use that to set the property).

With that done our application should compile (F6). Now it’s time to update our Entity Framework model.

We need to add our new field to the Nop_Category table. You can do this in Visual Studio, or (as I prefer) using SQL Server Management Studio:

image

Note that I am setting “Allow Nulls” to true. You will need to do this if the table already contains data. If you do not want to allow nulls you will need to run an update script to set a default value for your new property for all existing records.

With our database changes made, open up NopModel.edmx (inside Nop.BusinessLogic/Data).

Right click on the designer surface and select “Update Model from Database”. If it’s the first time you have done this you may need to set up the connection to your database:

image

With that done, you should see our new “Stylesheet” property on the category entity:

image

Note – the entity framework table mapping is case sensitive. If the case differs between entity and database (e.g. CategoryId <> CategoryID) you need to right click on the entity property within the edmx designer and select “Table Mapping”. You can then ensure that your properties are mapped correctly:

image

Now that we have updated our entity framework model, we need to return to our Insert and Update methods in CategoryManager and persist our “stylesheet” variable:

image

Now let’s look at adding a new entity.

Adding a new entity

In this example I am going to add a new “Banner” entity to nopCommerce since I would like to manage my site’s promotional banners from the nopCommerce administration interface.

I prefer to start with my domain, so let’s create a new class “Banner” and put it in our extensions directory.

namespace NopSolutions.NopCommerce.BusinessLogic.Extensions {
    
    public class Banner {
        public int BannerId { get; set; }
        public string Name { get; set; }
        public string Content { get; set; }
    }

}

As you can see, our class is quite simple.

Now let’s add our new Banner table to the nopCommerce database. Personally I don’t like prefixing my table names (you should use schemas for that) but I will use the existing convention in this example i.e. “Nop_Banner”:

image

Note that my database fields are named slightly differently to the properties on our class. We will need to update the mappings after updating our entity framework model.

Open up NopModel.edmx, right click on the designer and select “Update Model from Database”.

On the “Add” tab, we select the new table and click “Finish”:

image

If you scroll up to the top left of the designer you will see the Entity Framework entity that has been created:

image

Let’s rename this to “Banner” (since that’s what our class is called) and rename our properties to match those on our class:

image

Since we are not using the entity framework generated object context in nopCommerce, we need to update our NopObjectContext to include our new entity. Again, since this is a partial class, we can stick it in our “Extensions” directory:

namespace NopSolutions.NopCommerce.BusinessLogic.Data
{
    /// <summary>
    /// Represents a nopCommerce object context
    /// </summary>
    public partial class NopObjectContext : ObjectContext {
        private ObjectSet<Banner> _banners;
        
        public ObjectSet<Banner> Banners {
            get  {
                if ((_banners == null))
                    _banners = CreateObjectSet<Banner>();
                return _banners;
            }
        }
    }
}

This gives our object context a “Banners” property referencing the banner records in our database.

All that’s left to do is create our repository class “BannerManager”. I take a more domain driven approach than the existing manager classes in nopCommerce. We just have one method for update and insert and rather than passing in a variable for each property, we just take in the object itself. When it comes to making changes to our domain model this makes like much easier (if you’ve ever added any properties to Order, you will know what I’m talking about).

Our complete BannerManager class can be seen below.

namespace NopSolutions.NopCommerce.BusinessLogic.Extensions {
    public class BannerManager {
        
        public Banner GetBannerById(int bannerId) {
            var db = ObjectContextHelper.CurrentObjectContext;
            return db.Banners.SingleOrDefault(x => x.BannerId == bannerId);
        }

        public IList<Banner> GetAllBanners() {
            var db = ObjectContextHelper.CurrentObjectContext;
            return db.Banners.OrderBy(x => x.Name).ToList();
        }
        
        public void Save(Banner banner) {
            var db = ObjectContextHelper.CurrentObjectContext;

            if (banner.BannerId == 0) { 
                // new banner    
                db.Banners.AddObject(banner);
            } else {
                // existing banner
                if (!db.IsAttached(banner))
                    db.Banners.Attach(banner);
            }

            db.SaveChanges();
        }
    }
}

I’m not doing any caching here. You can look at one of the existing manager classes for an example of that.

As you can see, entity framework has made extending nopCommerce much easier.

Have fun!

Tags: ,
Categories ASP.NET | C# | Development | Entity Framework | nopCommerce

Efficient GridView paging with IQueryable

Posted by ben 9. September 2010 23:07

Recently nopCommerce switched it’s persistence layer to Entity Framework 4. This has made it much easier to add new features and tweak the existing model.

Today I’ve been working on a new feature and wanted to return a paged list of objects within administration.

The original (pre EF) stored procedures are still used for server side paging in a number of places within nopCommerce.

At the risk of having to write SQL, I thought why not take advantage of the IQueryable interface (or rather ObjectSet) that EF gives us.

To begin with I added the PagedList class that I’ve been using in a lot of projects recently.

    public interface IPagedList<T>
    {
        int PageIndex { get; }
        int PageSize { get; }
        int TotalCount { get; }
        int TotalPages { get; }
        bool HasPreviousPage { get; }
        bool HasNextPage { get; }
    }

    public class PagedList<T> : List<T>, IPagedList<T>
    {
        public PagedList(IQueryable<T> source, int pageIndex, int pageSize)
        {
            int total = source.Count();
            this.TotalCount = total;
            this.TotalPages = total / pageSize;

            if (total % pageSize > 0)
                TotalPages++;

            this.PageSize = pageSize;
            this.PageIndex = pageIndex;
            this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList());
        }

        public PagedList(List<T> source, int pageIndex, int pageSize)
        {
            int total = source.Count();
            this.TotalCount = total;
            this.TotalPages = total / pageSize;

            if (total % pageSize > 0)
                TotalPages++;

            this.PageSize = pageSize;
            this.PageIndex = pageIndex;
            this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList());
        }

        public int PageIndex { get; private set; }
        public int PageSize { get; private set; }
        public int TotalCount { get; private set; }
        public int TotalPages { get; private set; }

        public bool HasPreviousPage
        {
            get { return (PageIndex > 0); }
        }
        public bool HasNextPage
        {
            get { return (PageIndex + 1 < TotalPages); }
        }
    

The PagedList accepts an IQueryable source and then grabs the required page of data (using Skip and Take functions). Since EF gives us deferred execution of queries, we only retrieve a single page of data from the database.

So how do we use this in our application?

One of the first things I needed to do was create some administration screens for my new feature. Most (if not all) of the GridView controls in nopCommerce are using the built in paging support, and by built-in, I mean grab all the data then page it.

As you can appreciate this is not very efficient and as the size of your nopCommerce store increases, can make administration a tad painful.

Wiring our PagedList datasource to a GridView was not a problem. The problem was in rendering out the page links. The built in paging mechanism does a count of all the records in the data source to determine how many page links to display. In our case, since we are being good and only loading a page of data at a time, the GridView only displays 1 page link.

Unfortunately it’s not as simple as overriding the PageCount property as this is read-only – doh!

The solution was to roll our own GridView control so that we could take control of the pager initialization:

namespace NopSolutions.NopCommerce.Controls
{
    public class NopGridView : GridView
    {
        private const string _virtualItemCount = "virtualItemCount";
        private const string _currentPageIndex = "currentPageIndex";
               
        [Browsable(true), Category("Custom")]
        [Description("Set the virtual item count for this grid")]
        public int VirtualItemCount {
            get
            {
                if (ViewState[_virtualItemCount] == null)
                    ViewState[_virtualItemCount] = -1;
                return Convert.ToInt32(ViewState[_virtualItemCount]);
            }
            set
            {
                ViewState[_virtualItemCount] = value;
            }
        }

        private int CurrentPageIndex
        {
            get
            {
                if (ViewState[_currentPageIndex] == null)
                    ViewState[_currentPageIndex] = 0;
                return Convert.ToInt32(ViewState[_currentPageIndex]);
            }
            set
            {
                ViewState[_currentPageIndex] = value;
            }
        }

        public override object DataSource
        {
            get
            {
                return base.DataSource;
            }
            set
            {
                base.DataSource = value;
                this.CurrentPageIndex = this.PageIndex;
            }
        }

        protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
        {
            if (CustomPaging)
            {
                pagedDataSource.AllowCustomPaging = true;
                pagedDataSource.VirtualCount = this.VirtualItemCount;
                pagedDataSource.CurrentPageIndex = this.CurrentPageIndex;
            }
            base.InitializePager(row, columnSpan, pagedDataSource);
        }

        public bool CustomPaging {
            get { return (this.VirtualItemCount != -1); }
        }
    }

The important property here is VirtualItemCount. This is what we will set in our NopGridView.PageIndexChanging event.

Before I show you that, let me show you the repository method that returns our paged list. Notice that I am accepting a LINQ expression that is used to set the sort order of the data (it’s actually a requirement that we sort the data before we use skip and take):

        public static IPagedList<Account> GetAllAccounts<TKey>(int pageIndex, int pageSize, Expression<Func<Account, TKey>> orderBy) {          
            return new PagedList<Account>(Db.Accounts.OrderBy(orderBy), pageIndex, pageSize);
        }

To bind our gridview and page our data we use the following:

       
        protected void BindGrid(int pageIndex, int pageSize)
        {
            var accounts = AccountManager.GetAllAccounts<int>(pageIndex, pageSize, x=> x.AccountId);
            gvAccounts.VirtualItemCount = accounts.TotalCount;
            gvAccounts.DataSource = accounts;
            gvAccounts.DataBind();
        }

        protected void gvAccounts_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            gvAccounts.PageIndex = e.NewPageIndex;
            BindGrid(e.NewPageIndex, gvAccounts.PageSize);
        }

As you can see the combination of the PagedList and our custom GridView control make efficient paging much easier.

Tags: , ,
Categories nopCommerce | Development | C# | Entity Framework | ASP.NET