Introduction
I was looking for Repository Pattern with Entity Framework Code First with EntityTypeConfiguration
, but I was unable to find such an integrated sample. Here, I am touching only the basics. It's my first article.
Background
I am taking plain two objects, Category
and Product
to make this article simple.
I am using EntityTypeConfiguration
for making different layers to interact with database and field validation.
Following is the Solution tree.
**Note: We need to add Entity Framework reference in the EfRepPatTest.Data
project.
Development Tools
- VS 2010
- Entity Framework Library
Using the Code
First of all, we shall create three projects in the solution, of them, two are class libraries and the other one is a console project for implementation.
Solution Name | EfRepositoryPatterTest |
Class Library Name | EfRepPatTest.Entity
EfRepPatTest.Data |
Console Application | EfRepPatTest.Implementation |
In the EfRepPatTest.Entity
project, we shall create the following class:
BaseEntity.cs
public class BaseEntity<T>
{
public T Id { get; set; }
}
IRepository.cs [Interface]
That contains action of:
public interface IRepository<TEntity> where TEntity:class
{
IQueryable<TEntity> GetAll();
TEntity GetById(object id);
void Insert(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
}
Category.cs
It will inherit BaseEntity
class for getting the Id
property and assigning Id
field as integer.
public class Category:BaseEntity<int>
{
public virtual string Name { get; set; }
public List<Product> Products { get; set; }
}
Product.cs
Same as category
class.
public class Product:BaseEntity<long>
{
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual string Name { get; set; }
public virtual int MinimumStockLevel { get; set; }
}
EfRepPatTest.Data
project holds the following classes or interfaces.
Add reference of EntityFramework
and EfRepPatTest.Entity
in this project.
First of all, create an interface that drives DbContext
class means DataContext
class will consume.
IDbContext.cs
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity:class;
int SaveChanges();
void Dispose();
}
Here, IDbSet
interface belongs to System.Data.Entity
namespace. Create a class named DataContext
that will inherit DbContext
and consume IDbContext
.
DataContext.cs [Partial]
public class DataContext: DbContext,IDbContext
{
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
}
Before discussing the DataContext
class’s method, we need another two class that will map Entity
with EntityTypeConfiguration
and hold data validation logic of Database table.
Create folder name, Mapping and place the CategoryMap
and ProductMap
class in it [optional].
CategoryMap.cs
public class CategoryMap:EntityTypeConfiguration<Category>
{
public CategoryMap()
{
ToTable("Category");
HasKey(c => c.Id).Property
(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(c => c.Name).IsRequired().HasMaxLength(50);
}
}
ProductMap.cs
public class ProductMap:EntityTypeConfiguration<Product>
{
public ProductMap()
{
ToTable("Product");
HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption
(DatabaseGeneratedOption.Identity);
HasRequired(p => p.Category)
.WithMany(c=>c.Products)
.HasForeignKey(p => p.CategoryId);
Property(p => p.Name).IsRequired().HasMaxLength(100);
Property(p => p.MinimumStockLevel);
}
}
The above part will help us in avoiding direct use of Entities[Product
and Category
] as database table, instead it will cover db table logic, validation.
Now add "OnModelCreating
" method in DataContext
class for attaching the Entity as part of model. The following code snippet will demonstrate how to configure EntityTypeConfiguration
driven class [ProductMap
, CategoryMap
] with ModelBuilder
manually.
{
modelBuilder.Configurations.Add(new CategoryMap());
modelBuilder.Configurations.Add(new ProductMap());
base.OnModelCreating(modelBuilder);
}
Here, the problem is all of the classes need to be configured manually. That’s why I would like to avoid it. The following codes will help us to do it automatically.
DataContext.cs [Full]
public class DataContext: DbContext,IDbContext
{
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => type.BaseType != null && type.BaseType.IsGenericType &&
type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
base.OnModelCreating(modelBuilder);
}
}
We haven’t implemented the IRepository
interface yet, through RepositoryService
class, we will implement it.
RepositoryService.cs
public class RepositoryService<TEntity>:IRepository<TEntity> where TEntity:class
{
private IDbContext Context;
private IDbSet<TEntity> Entities
{
get { return this.Context.Set<TEntity>(); }
}
public RepositoryService(IDbContext context)
{
this.Context = context;
}
public IQueryable<TEntity> GetAll()
{
return Entities.AsQueryable();
}
public TEntity GetById(object id)
{
return Entities.Find(id);
}
public void Insert(TEntity entity)
{
Entities.Add(entity);
}
public void Update(TEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
this.Context.SaveChanges();
}
public void Delete(TEntity entity)
{
Entities.Remove(entity);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (this.Context != null)
{
this.Context.Dispose();
this.Context = null;
}
}
}
}
The "private IDbSet<TEntity
> Entities
" property will return DbSet
instance for access to entities of the given type in the context.
Now, you need a class for initializing the database. Add a class named DataBaseInitializer
.
DataBaseInitializer.cs
public class DataBaseInitializer : IDatabaseInitializer<DataContext>
{
public void InitializeDatabase(DataContext context)
{
context.Database.CreateIfNotExists();
}
}
Implementing the application through EfRepPatTest.Implementation
console project:
The Program.cs class looks like the following:
class Program
{
static void Main(string[] args)
{
var context = new DataContext();
var dataBaseInitializer = new DataBaseInitializer();
dataBaseInitializer.InitializeDatabase(context);
var categoryRepository = new RepositoryService<Category>(context);
var category = new Category()
{
Name = "Baverage"
};
var products = new List<Product>();
var product = new Product()
{
Name = "Soft Drink A",
MinimumStockLevel = 50
};
products.Add(product);
product = new Product()
{
Name = "Soft Drink B",
MinimumStockLevel = 30
};
products.Add(product);
category.Products = products;
categoryRepository.Insert(category);
context.SaveChanges();
IProductService productRepository = new ProductService();
Console.WriteLine("\n");
Console.WriteLine("Product List:");
Console.WriteLine("-------------------------------------------------");
foreach (var product1 in productRepository.GetAll())
{
Console.WriteLine(string.Format("Product Name : {0}",product1.Name));
if (product1.Id == 9)
{
product1.Name = "Soft Drink AAA";
productRepository.Update(product1);
}
}
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
Here, we have added "EfRepPatTest.Service
" project to test the service. It will help us in the next article.
Connection string in App.config [highlighted portion]:
="1.0"="utf-8"
<configuration>
<configSections>
<section name="entityFramework"
type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,
EntityFramework, Version=4.4.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</configSections>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory,
EntityFramework" />
</entityFramework>
<connectionStrings>
<add name="DataContext"
providerName="System.Data.SqlClient"
connectionString="Data
Source=YourSERVER;Initial Catalog=EfDBExistRepository;Integrated
Security=True;MultipleActiveResultSets=True;"/>
</connectionStrings>
</configuration>
As name of connection string [name="DataContext"]
is the same as DataContext.cs class, it will work automatically.
That may help!
Next Article
History
- 20th March, 2013: Initial version