Really impressed with Juile Lerman's pluralsight course on "EF in Enterprise" and decided to build my demo app.
I am using VS 2012 and latest versions of EF,SQL Server and MVC. I am building a demo application which applies SOLID principles. I am doing this to better understand how to implement DI & unit testing.
I have used DB first approach for this demo application. It contains only one table named UserDetails and below is how it looks in SQL server. I will use this table for CRUD operations.
Below is how i have layered my application :
1. WESModel Solution: This layer contains my Model1.edmx file and the context class as below.
namespace WESModel
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using WESDomain;
public partial class WESMVCEntities : DbContext
{
public WESMVCEntities()
: base("name=WESMVCEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<UserDetail> UserDetails { get; set; }
}
}
2. WESDomain Solution: This layer contains my Domain classes (or POCO classes). These POCO classes were actually auto generated in my WESModel layer. I moved them out to this layer. Here is how the single POCO class looks.
namespace WESDomain
{
using System;
using System.Collections.Generic;
public partial class UserDetail:IUserDetail
{
public int Id { get; set; }
public string UserName { get; set; }
}
}
3: WESDataLayer Solution: This layer contains reference to dlls from my above 2 layers. This layer has my Repository class as shown below. For now , I am keeping the IRepository in the same class :)
namespace WESDataLayer
{
public class UserDetailRepository : IUserDetailRepository
{
WESMVCEntities context = new WESMVCEntities();
public IQueryable<IUserDetail> All
{
get { return context.UserDetails; }
}
public IQueryable<IUserDetail> AllIncluding(params Expression<Func<IUserDetail, object>>[] includeProperties)
{
IQueryable<IUserDetail> query = context.UserDetails;
foreach (var includeProperty in includeProperties) {
query = query.Include(includeProperty);
}
return query;
}
public IUserDetail Find(int id)
{
return context.UserDetails.Find(id);
}
public void InsertOrUpdate(UserDetail userdetail)
{
if (userdetail.Id == default(int)) {
// New entity
context.UserDetails.Add(userdetail);
} else {
// Existing entity
context.Entry(userdetail).State = EntityState.Modified;
}
}
public void Delete(int id)
{
var userdetail = context.UserDetails.Find(id);
context.UserDetails.Remove(userdetail);
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public interface IUserDetailRepository : IDisposable
{
IQueryable<IUserDetail> All { get; }
IQueryable<IUserDetail> AllIncluding(params Expression<Func<UserDetail, object>>[] includeProperties);
UserDetail Find(int id);
void InsertOrUpdate(UserDetail userdetail);
void Delete(int id);
void Save();
}
}
4:ConsoleApplication1 Solution: This is my UI layer. It will be my MVC application in my final app. Here i simply query the DB and display the data. This is how the code looks.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IUserDetailRepository repo = new UserDetailRepository();
var count = repo.All.ToList().Count().ToString();
Console.WriteLine("Count: {0}", count);
Console.ReadLine();
}
}
}
Question: My UI layer does not have any ref to EF DLL. However, It has an instance of Repository class. In MVC application, my controller will have an instance of repository class or UnitOfWork.
a) Is this the right thing to do ?
b) Is there any way i can abstract it ?
c) What if in future i want to swap out EF with Dapper or any other ORM tool ?
d) How do i fit my DI tool in this project ? In which layer it should be ?
e) Unit testing. I am aware of StructureMap and want to make use of it in this project in such a way that in future i should be able to swap it out with Ninject. How do i achieve this ?
Thanks for reading this big question and i really appreciate if anyone can point me in right direction.