Right now I'm working my way thru the SportsStore exercise of Sanderson's "Pro ASP.Net MVC 2 Framework" book (page 107) and the exercise has me implementing a Repository pattern backed by a DB store using LINQ-to-SQL. I'm trying to figure out how to implement this same repository with the Subsonic ORM.
The basic repository code is as follows:
using System.Data.Linq;
namespace DomainModel.Concrete
{
public class SqlProductsRepository : IProductsRepository
{
private Table<Product> productsTable;
public SqlProductsRepository(string connectionString)
{
productsTable =
(new DataContext(connectionString)).GetTable<Product>();
}
public IQueryable<Product> Products
{
get { return productsTable; }
}
}
}
The LINQ-To-SQL specific part of the code is the line involving DataContext and GetTable, I think.
Does Subsonic have a similar mechanism to this? If so,
what would the code look like?
would I be able to use the methods available in System.Data.Linq's Table like:
InsertOnSubmit()
Attach()
DeleteOnSubmit()
SubmitChanges()
etc.
UPDATE
Got your message. Impressive Template, but I decided to try something simpler first:
using SubSonic.Repository;
namespace DomainModel.Concrete
{
public class SqlProductsRepository : IProductsRepository
{
private SimpleRepository repo;
private IQueryable<Product> products;
public IQueryable<Product> Products
{
get { return products; }
}
public DbProductsRepository(string subsonicDatastore)
{
repo = new SimpleRepository(subsonicDatastore, SimpleRepositoryOptions.RunMigrations);
this.Refresh();
}
public void SaveProduct(Product product) { SaveProduct(new List<Product>() { product }); }
public void SaveProduct(IEnumerable<Product> productList)
{
var newProducts = from product in productList
where product.ID == 0
select product;
var oldProducts = from product in productList
where product.ID > 0
select product;
// If it's a new Product, just add it to the Repo
repo.AddMany<Product>(newProducts);
// If it's old, just update it.
repo.UpdateMany<Product>(oldProducts);
// Refresh internal list of products, in case table has changed.
this.Refresh();
}
public void DeleteProduct(Product product) { DeleteProduct(new List<Product>() { product }); }
public void DeleteProduct(IEnumerable<Product> productList)
{
repo.DeleteMany<Product>(productList);
this.Refresh();
}
private void Refresh()
{
products = repo.All<Product>();
}
}
}
As far as I can tell, there is no drop-in replacement for DataContext, but there are other options that are just as easy.
I still plan on checking out your solution when I get more time. It seems more complex, but much more flexible/adaptable.
Thanks!
Pretzel - we meet again :)
I use subsonic 3 but use it with the generic repository pattern. this means that i only have a single 'factory' for dishing out the entities. it looks a bit like this:
public class SubSonicRepository<T> : IRepository<T> where T : new()
{
private readonly IQuerySurface _db;
public SubSonicRepository()
: this(SubsonicFrameworkObjectContextPerRequest.CurrentDatabase)
{
}
public SubSonicRepository(IQuerySurface db)
{
_db = db ?? DB.CreateDB();
}
// lots of stuff omitted!!
private IQueryable<T> GetAll()
{
var result = _db.GetQuery<T>();
return result;
}
T IRepository<T>.GetByKey(object key)
{
ITable tbl = _db.FindTable(typeof(T).Name);
var result = _db.Select.From(tbl)
.Where(tbl.PrimaryKey.Name).IsEqualTo(key)
.ExecuteSingle<T>();
return result;
}
}
... with lots of other code that follows. The _repository is instantiated in each controller (or service layer) and the scenario that you mention above happens in a slightly different way.
now, if i can get a copy of my T4 templates over to you, then you'd be able to follow it without my message by message rantings :)
Related
We have ASP MVC web project. After reading a lot of articles and discussions here in stackoverflow about the correct architechture we have decided to go with the following one, although there is not only one correct way of doing things this is the way we have decided, but we still have some doubts.
We are publishing this here not only to be helped but also to show what we have done in case it is helpful to somebody.
We are working in ASP .NET MVC project, EF6 Code first with MS SQL Server.
We have divided the project into 3 main layers that we have separate into 3 projects: model, service and web.
The model creates the entities and setup the DataContext for the database.
The service make the queries to the data base and transform those entities into DTOs to pass them to the web layer, so the web layer doesn't know anything about the database.
The web uses AutoFac for the DI (dependency Injection) to call the services we have in the service layer and obtain the DTOs to transform those DTOs into Model Views to use them in the Views.
After reading a lot of articles we decided not to implement a repository pattern and unit of work because, in summary, we have read the EF acts as a unit of work itself. So we are simplifying things a little here.
https://cockneycoder.wordpress.com/2013/04/07/why-entity-framework-renders-the-repository-pattern-obsolete/
This is the summary of our project. Now I'm going to go through every project to show the code. We are going to show only a couple of entities, but our project has more than 100 different entities.
MODEL
Data Context
public interface IMyContext
{
IDbSet<Language> Links { get; set; }
IDbSet<Resources> News { get; set; }
...
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
}
public class MyDataContext : DbContext, IMyContext
{
public MyDataContext() : base("connectionStringName")
{
}
public IDbSet<Language> Links { get; set; }
public IDbSet<Resources> News { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}
}
Here is how we declare the entities
public class Link
{
public int Id{ get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
SERVICES
These are the generic classes we use for all the services.
As you see we use the DTOs to get data from the web layer. Also we connect to the database using Dbset = Context.Set()
public interface IService
{
}
public interface IEntityService<TDto> : IService where TDto : class
{
IEnumerable<TDto> GetAll();
void Create(TDto entity);
void Update(TDto entity);
void Delete(TDto entity);
void Add(TDto entity);
void Entry(TDto existingEntity, object updatedEntity);
void Save();
}
public abstract class EntityService<T, TDto> : IEntityService<TDto> where T : class where TDto : class
{
protected IClientContext Context;
protected IDbSet<T> Dbset;
protected EntityService(IClientContext context) { Context = context; Dbset = Context.Set<T>(); }
public virtual IEnumerable<TDto> GetAll()
{
return Mapper.Map<IEnumerable<TDto>>(Dbset.AsEnumerable());
}
public virtual void Create(TDto entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
Dbset.Add(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Update(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
public virtual void Delete(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Dbset.Remove(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Add(TDto entity)
{
Dbset.Add(Mapper.Map<T>(entity));
}
public virtual void Entry(TDto existingEntity, object updatedEntity)
{
Context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
}
public virtual void Save()
{
Context.SaveChanges();
}
}
We declare the DTOs in this project (this is a very simple example so we don't have to put all the code here):
public class LinkDto
{
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
Then one of our services:
public interface ILinkService : IEntityService<LinkDto>
{
IPagedList<LinkDto> GetAllLinks(string searchTitle = "", bool searchActive = false, int pageNumber = 1, int pageSize = 10);
LinkDto FindById(int id);
LinkDto Test();
}
public class LinkService : EntityService<Link, LinkDto>, ILinkService
{
public LinkService(IClientContext context) : base(context) { Dbset = context.Set<Link>(); }
public virtual IPagedList<LinkDto> GetAllLinks(bool searchActive = false, int pageNumber = 1, int pageSize = 10)
{
var links = Dbset.Where(p => p.Active).ToPagedList(pageNumber, pageSize);
return links.ToMappedPagedList<Link, LinkDto>();
}
public virtual LinkDto FindById(int id)
{
var link = Dbset.FirstOrDefault(p => p.Id == id);
return Mapper.Map<LinkDto>(link);
}
public LinkDto Test()
{
var list = (from l in Context.Links
from o in Context.Other.Where(p => p.LinkId == l.Id)
select new OtherDto
{ l.Id, l.Title, l.Url, o.Other1... }).ToList();
return list;
}
}
As you see we use AutoMapper (version 5 which has changed a little) to transform from Entities to DTOs the data.
One of the doubts we have is if the use of "Dbset.Find" or "Dbset.FirstOrDefault" is correct and also if the use of "Context.Links" (for any entity).
WEB
FInally the web project where we receive the DTOs and transform those DTOs into ModelViews to show in our views.
We need to call, in the Global.asax Application_Start, AutoFac to do the DI so we can use our services.
protected void Application_Start()
{
...
Dependencies.RegisterDependencies();
AutoMapperBootstrapper.Configuration();
...
}
public class Dependencies
{
public static void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EfModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class ServiceModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.Load("MyProject.Service")).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();
}
}
public class EfModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType(typeof(MyDataContext)).As(typeof(IMyContext)).InstancePerLifetimeScope();
}
}
As you see we also call AutoMapper to configure the different maps.
Then in our controllers we have this.
public class LinksController : Controller
{
private readonly ILinkService _linkService;
public LinksController(ILinkService linkService)
{
_linkService = linkService;
}
public ActionResult Index()
{
var links = _linkService.GetAllLinks();
return View(links.ToMappedPagedList<LinkDto, LinksListModelAdmin>());
}
...
public ActionResult Create(LinksEditModelAdmin insertedModel)
{
try
{
if (!ModelState.IsValid) return View("Create", insertedModel);
var insertedEntity = Mapper.Map<LinkDto>(insertedModel);
_linkService.Create(insertedEntity);
return RedirectToAction("Index");
}
catch (Exception ex)
{
throw ex;
}
}
}
Well, this is it...I hope this can be useful for somebody...and also I hope we can have a little help with the questions we have.
1) Although we are separating database from the web project we do need a reference in the web project to initialize the database and also to inject dependencies, is this correct?
2) Is it correct the approach we have done having our Entities->DTOs->ViewModels? It's a little more work but we have everything separated.
3) In the Service project, when we need to reference a different entity than the main one we are using in the service, is it correct to call Context.Entity?
For example, if we need to retrieve also data from the News entity in the links service, is it correct to call "Context.News.Where..."?
4) We do have a little problem with Automapper and EF proxy, because when we call "Dbset" to retrieve data, it gets a "Dynamic proxies" object so Automapper can't find the proper map so, in order to work, we have to set ProxyCreationEnabled = false in the DataContext definition. This way we can get an Entity in order to map it to the DTO. This disables LazyLoading, which we don't mind, but is this a correct approach or there is a better way to solve this?
Thanks in advance for your comments.
For Question no. 2
Entities->DTOs->ViewModels? is good approach
because you are doing the clean separation, the programmer can work together with ease.
The person who design ViewModels, Views and Controllers don't have to worry about the service layer or the DTO implementation because he will make the mapping when the others developpers finish their implementation.
For Question no. 4
When the flag ProxyCreationEnabled is set to false, the proxy instance will not be created with creating a new instance of an entity. This might not be a problem but we can create a proxy instance using the Create method of DbSet.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create();
}
The Create method has an overloaded version that accepts a generic type. This can be used to create an instance of a derived type.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create<Student>();
}
The Create method just creates the instance of the entity type if the proxy type for the entity would have no value (it is nothing to do with a proxy). The Create method does not add or attach the entity with the context object.
Also i read some where if you set ProxyCreationEnabled = false the child element will not loaded for some parent object unless Include method is called on parent object.
Over the past few months I've been learning MVC5 with EF6. It's honestly been a love/hate affair, not with the framework itself, but with Microsoft's online tutorials. Not all, but a majority of their tutorials seem to end like the final season of a Television Series. Viewers are at home scratching their heads trying to figure out the rest of the story, or the reason why they even started watching it in the first place.
Anyway, my latest stumbling block is whether or not I should implement a repository pattern and unit of work with EF6? The reason I am interested in any sort of additional layer is just to help reduce the amount of logic code in my controllers.
Before considering a repository, I just had a simple public class in my models folder called productservice.cs that allowed me to pass a product model from the controller to perform some data manipulation and return it to the view to be saved. It worked flawlessly from my point of view, but I was calling an additional dbcontext in that service class which seemed incorrect, I think.
After some research online, I started to implement a repository which would allow me to perform the same data manipulation as in my productservice.cs, but it seemed to follow a well established pattern and allow the context to be passed from the controller to the depository where I can perform some manipulation before the save. It's more code to write considering the EF Save, Update, and Remove was working just fine, but I don't mind writing more code if it will put me in a better place moving forward.
Now the question is how do I get the lists for my dropdowns in the controller? A quick solution was to place a Get() in my productdepository for each list. It worked, but it seems like I'm writing more code than I need to. I see an advantage to writing individual repositories for each model, since they may have different store and retrieve methods down the road. Would the correct solution using repository be to create a unit of work which references each repository needed for that controller? Here's some of my code
The product repository interface:
public interface IProductRepository : IDisposable
{
IEnumerable<Category> GetCategories();
IEnumerable<Manufacturer> GetManufacturers();
IEnumerable<ProductType> GetProductTypes();
IEnumerable<Availability> GetAvailabilities();
IEnumerable<ShipMethod> GetShipMethods();
IEnumerable<Product> GetProducts();
IEnumerable<Product> GetProductsByName(string productName);
Product GetProductById(int productId);
void InsertProduct(Product product);
void DeleteProduct(int productId);
void UpdateProduct(Product product);
void Save();
}
The top portion of my contoller:
public class ProductController : Controller
{
private IProductRepository productRepository;
public ProductController()
{
this.productRepository = new ProductRepository(new ProductContext())
}
public ProductController(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
public ActionResult Create()
{
Product product = new Product();
product.Created = DateTime.Now;
ViewBag.AvailabilityId = new SelectList(productRepository.GetAvailabilities(), "AvailabilityId", "Name");
ViewBag.CategoryId = new SelectList(productRepository.GetCategories(), "CategoryId", "Name");
ViewBag.ManufacturerId = new SelectList(productRepository.GetManufacturers(), "ManufacturerId", "Name");
ViewBag.ProductTypeId = new SelectList(productRepository.GetProductTypes(), "ProductTypeId", "Name");
ViewBag.ShipMethodId = new SelectList(productRepository.GetShipMethods(), "ShipMethodId", "Name");
return View(product);
}
After spending some time to figure out the final solution, I keep coming back to the same question. Why can't I just turn the IProductRepository and Repository into a IProductService and ProductService?
Basically keep all the CRUD in the controller and call a service if it's needed which can be passed back to the controller for final storage or presentation? What's the real point of creating a bunch of CRUD methods for each entity if EF is already doing it for me in the controller?
Thank you in advance for any help. Just a little confused.
UPDATE - I wanted to show an example of my original controller and service idea. I understand the logic is simple and could be in the controller, but some of my other services like cropping and saving both a thumbnail and original image while creating a new product take up a lot of space in the controller.
The a block from the controller:
public class ProductTesterController : Controller
{
private ProductContext db = new ProductContext();
private ProductService service = new ProductService();
// GET: Dashboard/ProductManager/Details/5
public ActionResult Detail(int id)
{
Product product = db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
return View(product);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
service.UpdatePid(product, db);
db.Entry(product).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Detail", new { id = product.ProductId });
}
ViewBag.AvailabilityId = new SelectList(db.Availability, "AvailabilityId", "Name", product.AvailabilityId);
ViewBag.CategoryId = new SelectList(db.Categories, "CategoryId", "Name", product.CategoryId);
ViewBag.ManufacturerId = new SelectList(db.Manufacturers, "ManufacturerId", "Name", product.ManufacturerId);
ViewBag.ProductTypeId = new SelectList(db.ProductTypes, "ProductTypeId", "Name", product.ProductTypeId);
ViewBag.ShipMethodId = new SelectList(db.ShipMethods, "ShipMethodId", "Name", product.ShipMethodId);
return View(product);
}
And then the service:
public class ProductService
{
public Product CreatePid(Product product, ProductContext context)
{
int lastProductId = context.Products.OrderByDescending(x => x.ProductId).First().ProductId;
product.PID = Convert.ToInt32("" + (100 * product.CategoryId) + (lastProductId + 1));
return (product);
}
public Product UpdatePid(Product product, ProductContext context)
{
product.PID = Convert.ToInt32("" + (100 * product.CategoryId) + product.ProductId);
return (product);
}
}
I don't create a new context inside the service, just pass the context from the controller and return what I need with the context. For the size of my project and lack of real repository/uow experience this seems to suite my situation. I mean the whole reason for the service class is to reduce the size of my controller and keep it simple. Is there any negative effect of just doing it like this rather than creating a repository/uow/service? Thanks!
UPDATE
After spending alot of time researching this I stumbled upon this blog which seems to be exactly what I was looking for. Since this post I have also started using Autofac to inject my context into constructors. Works like a charm.
Here's the link that will hopefully help someone seeking a similar solution -
Making Entity Framework More Unit Testable - Josh Kodroff
I use a GenericRepository class with a UnitOfWork which creates an instance of the GenericRepository for each model linking through the EntityFramework back to the database. See here: http://www.codeproject.com/Articles/825646/Generic-Repository-and-UnitofWork-patterns-in-MVC
Here is a very simple example of how I have used this in the past. It's worth noting that for more complex SQL needs I use an ORM like Dapper which is awesome! :
dbEntities.cs
public class dbEntities : DbContext
{
public DbSet<Product> Products{ get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Product>().HasKey(x => x.id);
}
}
GenericRepository.cs
public class GenericRepository<TEntity> where TEntity :class
{
internal dbEntities _db;
internal DbSet<TEntity> dbSet;
public GenericRepository(dbEntities _db)
{
this._db = _db;
this.dbSet = _db.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query);
}
else
{
return query;
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (_db.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
_db.Entry(entityToUpdate).State = EntityState.Modified;
}
}
UnitOfWork.cs
public class UnitOfWork :IDisposable
{
private dbEntities _db = new dbEntities();
private GenericRepository<Product> productRepository;
public GenericRepository<Product> ProductRepository
{
get
{
if (this.productRepository == null)
{
this.productRepository = new GenericRepository<Product>(_db);
}
return productRepository;
}
}
public void Save()
{
_db.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
A new entry is added for each model in the unitofwork.cs and dbEntities.cs files. The model class corresponds structurally to a DB table.
Simple Usage example
var _uow = new UnitOfWork();
//get all products over £100
var lst = _uow.ProductRepository.Get(n=>!n.price>100);
_uow.Dispose();
I've been looking at a few blog posts to try and create an appropriate solution for the following requirements but I can't seem to piece them together. Hope fully someone can help.
I've been using Repository pattern with interfaces using Automapper...here's a trimmed down example:
public class BookingRepository : IBookingRepository
{
Entities context = new Entities();
public IEnumerable<BookingDto> GetBookings
{
get { return Mapper.Map<IQueryable<Booking>, IEnumerable<BookingDto>>(context.Bookings); }
}
public BookingDto GetBookingWithProduct(Guid bookingId)
{
return Mapper.Map<BookingDto>(context.Bookings.Include(c => c.Products).SingleOrDefault(c => c.BookingId == bookingId));
}
public void Update(BookingDto bookingDto)
{
var booking = Mapper.Map<Booking>(bookingDto);
context.Entry(booking).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public interface IBookingRepository : IDisposable
{
IEnumerable<BookingDto> GetBookings { get; }
BookingDto GetBooking(Guid bookingId);
void Update(BookingDto bookingDto);
void Save();
}
With a seperate Repository for a different Entity, for example
public class ProductRepository : IProductRepository
{
Entities context = new Entities();
public IEnumerable<ProductDto> GetProducts
{
get { return Mapper.Map<IQueryable<Product>, IEnumerable<ProductDto>>(context.Products); }
}
public ProductDto GetProductWithDesign(int productId)
{
return Mapper.Map<ProductDto>(context.Products.Include(c => c.Designs).SingleOrDefault(c => c.ProductId == productId));
}
public void Update(ProductDto productDto)
{
var product = Mapper.Map<Product>(productDto);
context.Entry(product).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public interface IProductRepository : IDisposable
{
IEnumerable<ProductDto> GetProducts { get; }
ProductDto GetProduct(int productId);
void Update(ProductDto productDto);
void Save();
}
Then in my Controller I'm using the repositories as so:
public class HomeController : Controller
{
private readonly IBookingRepository bookingRepository;
private readonly IProductRepository productRepository;
public HomeController() : this(new BookingRepository(), new ProductRepository()) { }
public HomeController(IBookingRepository bookingRepository, IProductRepository productRepository)
{
this.bookingRepository = bookingRepository;
this.productRepository = productRepository;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && this.bookingRepository != null)
this.bookingRepository.Dispose();
if (disposing && this.productRepository != null)
this.productRepository.Dispose();
}
}
So now I'm hoping to create a Unit Of Work to abstract these repositories and share the context and also create a generic repository for the duplicated actions (Save and Update) bearing in mind I'm passing in Dtos and Mapping to Entity objects. I'm having difficulty understanding how to knit it all together.
Additionally, I've seen this post
Repository pattern with generics and DI
which states "You should not have other repository interfaces besides your generic repository" and that custom queries "deserve their own (generic) abstraction:" which is adding another complication to my overworked brain as my repositories will have custom queries that return complex linked objects using Include Statements as Lazy Loading is disabled.
So I'm prepared to be shot down and told that I'm going about this the wrong way but would be grateful for any direction given.
Thanks in advance.
Don't use generic repositories. They are all leaky abstractions. Ask yourself, what benefit to you get by using an abstraction that doesn't really abstract away something? You could use your OR/M directly in those cases.
What I means is that anything that exposes IQueryable<T> forces the user to learn about the weaknesses that the underlying OR/M has. Examples: How do the orm handle lazy loading? How do I eagerly load related entities? How do I create a IN clause?
If you truly want to use the repository pattern either use it together with the specification pattern (you can keep on using a generic repository then) or create repositories that are specific for each root aggregate.
I've blogged about it: http://blog.gauffin.org/2013/01/repository-pattern-done-right/
What I usually do in this case is to create a Base abstract Repository class like this:
public abstract class BaseRepository<T> : IRepository<T>
{
Entities context = new Entities();
public virtual T GetAll()
{
return context.Set<T>();
}
// Add base implementation for normal CRUD here
}
If you don't need special queries then you don't need to create special interface and classes (but you can of course, to improve readability). So you will use, for example:
var bookingsRepo = new BaseRepository<BookingsDto>();
var allBookings = bookingsRepo.GetAll();
If you need some special queries, you create an interface that extends the base interface:
public interface IProductRepository : IRepository<Product>
{
Product GetSpecialOffer();
}
Then create your class:
public class ProductRepository : BaseRepository<Product>, IProductRepository
{
public Product GetSpecialOffer()
{
// your logic here
}
}
That way you only specify a minimal number of special cases while relying on the Base abstract implementation for all things normal.
I added virtual to the base methods because I always like to give derived class the ability to override stuff...
I'm in my first time with DDD, so I'm begginer! So, let's take it's very simple :D
I developed an application using asp.net mvc 2 , ddd and nhibernate. I have a domain model in a class library, my repositories in another class library, and an asp.net mvc 2 application. My Repository base class, I have a construct that I inject and dependency (my unique ISessionFactory object started in global.asax), the code is:
public class Repository<T> : IRepository<T>
where T : Entidade
{
protected ISessionFactory SessionFactory { get; private set; }
protected ISession Session
{
get { return SessionFactory.GetCurrentSession(); }
}
protected Repository(ISessionFactory sessionFactory)
{
SessionFactory = sessionFactory;
}
public void Save(T entity)
{
Session.SaveOrUpdate(entity);
}
public void Delete(T entity)
{
Session.Delete(entity);
}
public T Get(long key)
{
return Session.Get<T>(key);
}
public IList<T> FindAll()
{
return Session.CreateCriteria(typeof(T)).SetCacheable(true).List<T>();
}
}
And After I have the spefic repositories, like this:
public class DocumentRepository : Repository<Domain.Document>, IDocumentRepository
{
// constructor
public DocumentRepository (ISessionFactory sessionFactory) : base(sessionFactory)
{ }
public IList<Domain.Document> GetByType(int idType)
{
var result = Session.CreateQuery("from Document d where d.Type.Id = :IdType")
.SetParameter("IdType", idType)
.List<Domain.Document>();
return result;
}
}
there is not control of transaction in this code, and it's working fine, but, I would like to make something to control this repositories in my controller of asp.net mvc, something simple, like this:
using (var tx = /* what can I put here ? */) {
try
{
_repositoryA.Save(objA);
_repositoryB.Save(objB);
_repositotyC.Delete(objC);
/* ... others tasks ... */
tx.Commit();
}
catch
{
tx.RollBack();
}
}
I've heared about NHibernateUnitOfWork, but i don't know :(, How Can I configure NHibernateUnitOfWork to work with my repositories ? Should I change the my simple repository ? Sugestions are welcome!
So, thanks if somebody read to here! If can help me, I appretiate!
PS: Sorry for my english!
bye =D
Session is NHibernate's unit of work. But you can always create your own abstraction of it.
using (var tx = Session.BeginTransaction) { ...
There is an excellent library called NCommon (source) that provides a great UnitOfWork implementation built right in. Version 1.1 allows you to do something like:
public class Foo
{
private readonly IRepository<Stuff> _repository;
public Foo(IRepository<Stuff> repository)
{
_repository = repository;
}
public void DoSomething()
{
using (var scope = new UnitOfWorkScope())
{
_repository.Save(a);
scope.Commit();
}
}
}
It integrates with the latest NHibernate and even uses NHibernate.Linq to provide some powerful querying features. You have little or nothing to build yourself and it works great out of the box.
Edit:
I elaborated on my example to show the full recommended way to use NCommon in a project with dependency-injection.
You can make the Session on your Repository a public property. Then you can do the following:
using(var tx = _repository.Session.BeginTransaction())
On a somewhat related note, this should all be inside of a service layer, not in your controller. Then the controller should have references to your services.
How can I mock a DataServiceQuery for unit testing purpose?
Long Details follow:
Imagine an ASP.NET MVC application, where the controller talks to an ADO.NET DataService that encapsulates the storage of our models (for example sake we'll be reading a list of Customers). With a reference to the service, we get a generated class inheriting from DataServiceContext:
namespace Sample.Services
{
public partial class MyDataContext : global::System.Data.Services.Client.DataServiceContext
{
public MyDataContext(global::System.Uri serviceRoot) : base(serviceRoot) { /* ... */ }
public global::System.Data.Services.Client.DataServiceQuery<Customer> Customers
{
get
{
if((this._Customers==null))
{
this._Customers = base.CreateQuery<Customer>("Customers");
}
return this._Customers;
}
}
/* and many more members */
}
}
The Controller could be:
namespace Sample.Controllers
{
public class CustomerController : Controller
{
private IMyDataContext context;
public CustomerController(IMyDataContext context)
{
this.context=context;
}
public ActionResult Index() { return View(context.Customers); }
}
}
As you can see, I used a constructor that accepts an IMyDataContext instance so that we can use a mock in our unit test:
[TestFixture]
public class TestCustomerController
{
[Test]
public void Test_Index()
{
MockContext mockContext = new MockContext();
CustomerController controller = new CustomerController(mockContext);
var customersToReturn = new List<Customer>
{
new Customer{ Id=1, Name="Fred" },
new Customer{ Id=2, Name="Wilma" }
};
mockContext.CustomersToReturn = customersToReturn;
var result = controller.Index() as ViewResult;
var models = result.ViewData.Model;
//Now we have to compare the Customers in models with those in customersToReturn,
//Maybe by loopping over them?
foreach(Customer c in models) //*** LINE A ***
{
//TODO: compare with the Customer in the same position from customersToreturn
}
}
}
MockContext and MyDataContext need to implement the same interface IMyDataContext:
namespace Sample.Services
{
public interface IMyDataContext
{
DataServiceQuery<Customer> Customers { get; }
/* and more */
}
}
However, when we try and implement the MockContext class, we run into problems due to the nature of DataServiceQuery (which, to be clear, we're using in the IMyDataContext interface simply because that's the data type we found in the auto-generated MyDataContext class that we started with). If we try to write:
public class MockContext : IMyDataContext
{
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers { get { /* ??? */ } }
}
In the Customers getter we'd like to instantiate a DataServiceQuery instance, populate it with the Customers in CustomersToReturn, and return it. The problems I run into:
1~ DataServiceQuery has no public constructor; to instantiate one you should call CreateQuery on a DataServiceContext; see MSDN
2~ If I make the MockContext inherit from DataServiceContext as well, and call CreateQuery to get a DataServiceQuery to use, the service and query have to be tied to a valid URI and, when I try to iterate or access the objects in the query, it will try and execute against that URI. In other words, if I change the MockContext as such:
namespace Sample.Tests.Controllers.Mocks
{
public class MockContext : DataServiceContext, IMyDataContext
{
public MockContext() :base(new Uri("http://www.contoso.com")) { }
public IList<Customer> CustomersToReturn { set; private get; }
public DataServiceQuery<Customer> Customers
{
get
{
var query = CreateQuery<Customer>("Customers");
query.Concat(CustomersToReturn.AsEnumerable<Customer>());
return query;
}
}
}
}
Then, in the unit test, we get an error on the line marked as LINE A, because http://www.contoso.com doesn't host our service. The same error is triggered even if LINE A tries to get the number of elements in models.
Thanks in advance.
I solved this by creating an interface IDataServiceQuery with two implementations:
DataServiceQueryWrapper
MockDataServiceQuery
I then use IDataServiceQuery wherever I would have previously used a DataServiceQuery.
public interface IDataServiceQuery<TElement> : IQueryable<TElement>, IEnumerable<TElement>, IQueryable, IEnumerable
{
IDataServiceQuery<TElement> Expand(string path);
IDataServiceQuery<TElement> IncludeTotalCount();
IDataServiceQuery<TElement> AddQueryOption(string name, object value);
}
The DataServiceQueryWrapper takes a DataServiceQuery in it's constructor and then delegates all functionality to the query passed in. Similarly, the MockDataServiceQuery takes an IQueryable and delegates everything it can to the query.
For the mock IDataServiceQuery methods, I currently just return this, though you could do something to mock the functionality if you want to.
For example:
// (in DataServiceQueryWrapper.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return new DataServiceQueryWrapper<TElement>(_query.Expand(path));
}
// (in MockDataServiceQuery.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
return this;
}
[Disclaimer - I work at Typemock]
Have you considered using a mocking framework?
You can use Typemock Isolator to create a fake instance of DataServiceQuery:
var fake = Isolate.Fake.Instance<DataServiceQuery>();
And you can create a similar fake DataServiceContext and set it's behavior instead of trying to inherit it.