A reference architecture (part 5)

…continued from part 4

The infrastructure layer (Data Access)

The next thing to do is to build the concrete repository implementation for the repository interfaces that we defined in the domain layer.

Add a class library project called ArchitectureExample.Infrastructure.DataAccess to the Infrastructure Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p5_1

In this project we will implement the IBlogEntryRepository interface and use Entity Framework ‘code first’ to map the database entities to the domain entities, and also create a UnitOfWork based on the Entity Framework context.

We will also need a project to put shared stuff in, so add a class library project called ArchitectureExample.Infrastructure.Shared to the Infrastructure Layer solution folder, and put it physically in the \Trunk\Sources\ folder:

p5_2

As we will use Entity Framework 4, download it, put the EntityFramework.dll assembly in the \Trunk\Libraries\Entity Framework folder and add a reference to it from the ArchitectureExample.Infrastructure.DataAccess and ArchitectureExample.Infrastructure.Shared projects. (Optionally you can of course also get the nuget package.)

Let’s first put some base classes in place needed by the data access layer. Add the following classes to ArchitectureExample.Infrastructure.Shared:

public abstract class DisposableObject : IDisposable
{
public bool Disposed { get; private set; }

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

~DisposableObject()
{
Debug.Assert(Disposed, "WARNING: Object finalized without being disposed!");
Dispose(false);
}

private void Dispose(bool disposing)
{
if (!Disposed)
{
if (disposing)
{
DisposeManagedResources();
}

DisposeUnmanagedResources();
Disposed = true;
}
}

protected virtual void DisposeManagedResources() { }
protected virtual void DisposeUnmanagedResources() { }
}

public interface IDatabaseFactory<TContext> : IDisposable where TContext : DbContext, new()
{
TContext Get();
}

public class DatabaseFactory<TContext> : DisposableObject, IDatabaseFactory<TContext> where TContext : DbContext, new()
{
private TContext _dataContext;
public TContext Get()
{
return _dataContext ?? (_dataContext = new TContext());
}

protected override void DisposeManagedResources()
{
if (_dataContext != null)
{
_dataContext.Dispose();
_dataContext = null;
}
}
}

public abstract class RepositoryBase<TContext, TEntity> : IRepository<TEntity>
where TContext : DbContext, new()
where TEntity : class
{
private TContext _dataContext;

protected TContext DataContext
{
get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
}

protected readonly IDbSet<TEntity> dbset;

protected RepositoryBase(IDatabaseFactory<TContext> databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<TEntity>();
}

protected IDatabaseFactory<TContext> DatabaseFactory { get; private set; }

public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> filter)
{
return dbset.Where(filter);
}

public virtual void Add(TEntity entity)
{
dbset.Add(entity);
}

public virtual void Update(TEntity entity)
{
dbset.Attach(entity);
DataContext.Entry(entity).State = EntityState.Modified;
}

public virtual void Delete(TEntity entity)
{
dbset.Remove(entity);
}

public virtual void Delete(Expression<Func<TEntity, bool>> where)
{
IEnumerable<TEntity> objects = dbset.Where<TEntity>(where).AsEnumerable();
foreach (TEntity obj in objects)
dbset.Remove(obj);
}

public IEnumerable<TEntity> QueryObjectGraph(Expression<Func<TEntity, bool>> filter, string children)
{
return dbset.Include(children).Where(filter);
}
}

Next, create the Sql Server database called ArchitectureExample, and add the tables BlogEntry and Comment:

CREATE TABLE [dbo].[BlogEntry](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](200) NOT NULL,
[Body] [nvarchar](2000) NOT NULL,
[LastPublishDate] [datetime] NULL,
[LastModifiedDate] [datetime] NOT NULL,
CONSTRAINT [PK_BlogEntry] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Comment](
[Id] [int] IDENTITY(1,1) NOT NULL,
[BlogEntryId] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[EmailAddress] [nvarchar](200) NOT NULL,
[CommentText] [nvarchar](1000) NOT NULL,
[DateWritten] [datetime] NOT NULL,
CONSTRAINT [PK_Comment] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Comment] WITH CHECK ADD CONSTRAINT [FK_Comment_BlogEntry] FOREIGN KEY([BlogEntryId])
REFERENCES [dbo].[BlogEntry] ([Id])
GO

ALTER TABLE [dbo].[Comment] CHECK CONSTRAINT [FK_Comment_BlogEntry]
GO

Also create a user ‘ArchitectureExample’  that has sufficient access rights to this database.

Next add the following classes to the ArchitectureExample.Infrastructure.DataAccess project:

public class EfArchitectureExampleDbContext : DbContext, IUnitOfWork
{
public DbSet<BlogEntry> BlogEntries { get; set; }
public DbSet<Comment> Comments { get; set; }

public void Commit()
{
base.SaveChanges();
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BlogEntry>().ToTable("BlogEntry");
modelBuilder.Entity<Comment>().ToTable("Comment");
}
}

public class UnitOfWork : DisposableObject, IUnitOfWork
{
private IDatabaseFactory<EfArchitectureExampleDbContext> _databaseFactory;
private EfArchitectureExampleDbContext _dataContext;

public UnitOfWork(IDatabaseFactory<EfArchitectureExampleDbContext> databaseFactory)
{
_databaseFactory = databaseFactory;
}

public EfArchitectureExampleDbContext DataContext
{
get { return _dataContext ?? (_dataContext = _databaseFactory.Get()); }
}

public void Commit()
{
DataContext.Commit();
}

protected override void DisposeManagedResources()
{
if (_databaseFactory != null)
{
_databaseFactory.Dispose();
_databaseFactory = null;
}
if (_dataContext != null)
{
_dataContext.Dispose();
_dataContext = null;
}
}
}

public class BlogEntryRepository : RepositoryBase<EfArchitectureExampleDbContext, BlogEntry>, IBlogEntryRepository
{
public BlogEntryRepository(IDatabaseFactory<EfArchitectureExampleDbContext> databaseFactory) : base(databaseFactory)
{
}

public IQueryable<BlogEntry> BlogEntries
{
get { return DataContext.BlogEntries; }
}

public IQueryable<Comment> Comments
{
get { return DataContext.Comments; }
}

public void Dispose()
{
DataContext.Dispose();
}
}

At this time we also completed the data access component, and the infrastructure layer should look like:

p5_3

Next time we will continue with the service layer.

Advertisements

2 thoughts on “A reference architecture (part 5)

  1. Gerard Torres says:

    Hi Ludwig,

    What is the purpose of the stuff in the OnModelCreating method here in your DbContext class?

    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity().ToTable(“BlogEntry”);
    modelBuilder.Entity().ToTable(“Comment”);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s