In this post I will show you how I implement a generic repository pattern with EF Code First as dataprovider, and MEF as the provider for separation of concern.
This post will be quite long since all the source code will be exposed…
If you never heard about MEF, check my previous post for a brief intro http://average-uffe.blogspot.com/2010/10/managed-extensibility-framework.html
A RoadMap (the steps in this tutorial/post…)
- Pre req´s
- Setup the solution
- Create a simple domainmodel (Person and Cars will do).
- Create data and service interfaces (generic and entity-interfaces).
- Implement the geneic repository and the EFCodeFirst DBContext (and entity-repositorys).
- Create our class for handling MEF imports.
- Implementing the generic ServiceInterfaces and import the exported repository with MEF (and entity-services).
- What have we created? An architectual overview!
- What´s next?
Pre Req´s
- Visual Studio 2010 (Sp1 beta atleast).
- SqlExpress.
Setup the solution
Create 5 projects… 4 Class Library project and one MVC3 WebApp… install MVC if needed.The classlibraries will represent Core (the domainmodel), Data (the repository), Services (the implementation of serviceinterfaces) and Common for some utilities….
The Data-project will export interfaces so we setup a postbuild event in Visual Studio to copy the dll to our pluginfolder.
Run Nuget PackageManager on Core and Data-projects for EFCodeFirst (run the command twice.. one for Core and one for Data).
Create a simple domainmodel
Finally some coding[DataContract]
public abstract class PersistentEntity
{
[DataMember]
[Key]
public int Id { get; protected set; }
[DataMember]
public DateTime Created { get; set; }
[DataMember]
public DateTime Updated { get; set; }
}
public abstract class PersistentEntity
{
[DataMember]
[Key]
public int Id { get; protected set; }
[DataMember]
public DateTime Created { get; set; }
[DataMember]
public DateTime Updated { get; set; }
}
[DataContract]
public class Car : PersistentEntity
{
[DataMember]
[StringLength(15)]
public string Color { get; set; }
[DataMember]
[StringLength(10)]
public string RegNr { get; set; }
[DataMember]
[StringLength(20)]
public string Brand { get; set; }
[DataMember]
public virtual Person Owner { get; set; }
}
public class Car : PersistentEntity
{
[DataMember]
[StringLength(15)]
public string Color { get; set; }
[DataMember]
[StringLength(10)]
public string RegNr { get; set; }
[DataMember]
[StringLength(20)]
public string Brand { get; set; }
[DataMember]
public virtual Person Owner { get; set; }
}
[DataContract]
public class Person : PersistentEntity
{
[DataMember]
[StringLength(50)]
public string FirstName { get; set; }
[DataMember]
[StringLength(50)]
public string LastName { get; set; }
[DataMember]
[StringLength(12)]
public string SSN { get; set; }
[DataMember]
public DateTime Birthdate { get; set; }
[DataMember]
public virtual IList<Car> Cars { get; set; }
}
public class Person : PersistentEntity
{
[DataMember]
[StringLength(50)]
public string FirstName { get; set; }
[DataMember]
[StringLength(50)]
public string LastName { get; set; }
[DataMember]
[StringLength(12)]
public string SSN { get; set; }
[DataMember]
public DateTime Birthdate { get; set; }
[DataMember]
public virtual IList<Car> Cars { get; set; }
}
Create Data & Service-Interfaces
Above we created the simple domainmodel and now we will create the interfaces for the repository and the service. To understand the reason to why we need interfaces is important! The interfaces data and service may very well have the same signature, but we will need them both to separate and hide the datainterfaces from being exposed further than to the servicelayer. Since we are using MEF the interfaces is also important because we tell MEF that we export the interface (and also that we want to import anything that implements a certain interface).We will have baseinterfaces for both data and service by using Generics.
The RepositoryInterface
public interface IRepository<T>
{
IEnumerable<T> GetAll();
T GetById(int id);
int SaveOrUpdate(T entity);
bool Delete(T entity);
}
Above we defined the basic CRUD operations for all entities.. Now we have a very simple implementation of the entities in our domainmodel, displayed below.{
IEnumerable<T> GetAll();
T GetById(int id);
int SaveOrUpdate(T entity);
bool Delete(T entity);
}
Definition for the domainmodels datainterfaces
interface IPersonRepository : IRepository<Person>
{
}
{
}
interface ICarRepository : IRepository<Car>
{
}
Thats it… Now we will do the same thing for the serviceinterfaces… {
}
The ServiceInterface
public interface IService<T>
{
IEnumerable<T> GetAll();
T GetById(int id);
int SaveOrUpdate(T entity);
bool Delete(T entity);
}
{
IEnumerable<T> GetAll();
T GetById(int id);
int SaveOrUpdate(T entity);
bool Delete(T entity);
}
public interface ICarService : IService<Car>
{
}
{
}
public interface IPersonService : IService<Person>
{
}
{
}
Implement the geneic repository and the EFCodeFirst DBContext
In our Data-project we will implement the repositoryinterfaces that we created in the Core-project. We start with the baserepository.To start, we only create the abstract class called baserepository and implemt the stubs for IRepository<T> (shown below)
public abstract class BaseRepository<T> : IRepository<T> where T : PersistentEntity
{
public IEnumerable<T> GetAll()
{
throw new NotImplementedException();
}
public T GetById(int id)
{
throw new NotImplementedException();
}
public int SaveOrUpdate(T entity)
{
throw new NotImplementedException();
}
public bool Delete(T entity)
{
throw new NotImplementedException();
}
}
Nothing existing in that class… Since we want to use EFCodeFirst we now need to tell our repository how to handle the CRUD operations. To start we add a new class (DemoContext) that implements the DBContext. This is the class that will tell EFCodeFirst what entities to include in the mapping. {
public IEnumerable<T> GetAll()
{
throw new NotImplementedException();
}
public T GetById(int id)
{
throw new NotImplementedException();
}
public int SaveOrUpdate(T entity)
{
throw new NotImplementedException();
}
public bool Delete(T entity)
{
throw new NotImplementedException();
}
}
public class DemoContext : DbContext
{
public DbSet<Person> Companies { get; set; }
public DbSet<Car> Employments { get; set; }
public DemoContext() : base("XlentDemoDB")
{
//DropAndReCreate if in debug and model is changed.
if (System.Diagnostics.Debugger.IsAttached)
DbDatabase.SetInitializer(new DropCreateDatabaseIfModelChanges<DemoContext>());
}
public ObjectContext ObjectContext()
{
return ((IObjectContextAdapter)this).ObjectContext;
}
}
Now we have a context to work with… Lets use it in our repository… And that will give us a new verison of the stubb of the repository above.{
public DbSet<Person> Companies { get; set; }
public DbSet<Car> Employments { get; set; }
public DemoContext() : base("XlentDemoDB")
{
//DropAndReCreate if in debug and model is changed.
if (System.Diagnostics.Debugger.IsAttached)
DbDatabase.SetInitializer(new DropCreateDatabaseIfModelChanges<DemoContext>());
}
public ObjectContext ObjectContext()
{
return ((IObjectContextAdapter)this).ObjectContext;
}
}
Repository Implementing our DbContext
Okay, this is alot of code in a post but this is the biggest class in this example… And quite important to… This handles all CRUD operations regardless of the entity as long as the entity inherits the “PersistentEntity” class. Note that we use the export attribut to expose the class to MEF.[Export(typeof(IRepository<>))]
public abstract class BaseRepository<T> : IRepository<T> where T : PersistentEntity
{
protected DemoContext Ctx;
protected string EntitySetName { get; set; }
protected string ContainerName { get; set; }
protected ObjectContext ObjectContext { get; set; }
protected Type CurrentType { get; set; }
protected BaseRepository()
{
//Setup context
this.Ctx = new DemoContext();
//Get The ObjectContext to be able to work with generics.
this.ObjectContext = this.Ctx.ObjectContext();
//Attach the SavingChanges event.
this.ObjectContext.SavingChanges += (sender, e) => BeforeSave(this.GetChangedOrNewEntities());
//Get EntityName and ContainerName
this.EntitySetName = this.ObjectContext.CreateObjectSet<T>().EntitySet.Name;
this.CurrentType = typeof(T);
}
private IEnumerable<PersistentEntity> GetChangedOrNewEntities()
{
const EntityState newOrModified = EntityState.Added | EntityState.Modified;
return this.ObjectContext.ObjectStateManager.GetObjectStateEntries(newOrModified)
.Where(x => x.Entity != null).Select(x => x.Entity as PersistentEntity);
}
public void BeforeSave(IEnumerable<PersistentEntity> entities)
{
foreach (var entity in entities)
{
entity.Updated = DateTime.Now;
entity.Created = !IsPersistent(entity) ? DateTime.Now : entity.Created;
}
}
private static bool IsPersistent(PersistentEntity entity)
{
return entity.Id != 0;
}
public virtual int SaveOrUpdate(T entity)
{
if (IsPersistent(entity))
this.Ctx.Set(this.CurrentType).Attach(entity);
else
this.Ctx.Set(typeof(T)).Add(entity);
return this.Ctx.SaveChanges();
}
public virtual T GetById(int id)
{
return this.Ctx.Set(this.CurrentType).Find(id) as T;
}
public virtual IEnumerable<T> GetAll()
{
return this.ObjectContext.CreateObjectSet<T>(this.EntitySetName);
}
public virtual bool Delete(T entity)
{
try
{
this.Ctx.Set(this.CurrentType).Remove(entity);
return true;
}
catch
{
return false;
}
}
}
Now that we have the baserepository in place we can create really simple Repositorys for our Person and Car entities…public abstract class BaseRepository<T> : IRepository<T> where T : PersistentEntity
{
protected DemoContext Ctx;
protected string EntitySetName { get; set; }
protected string ContainerName { get; set; }
protected ObjectContext ObjectContext { get; set; }
protected Type CurrentType { get; set; }
protected BaseRepository()
{
//Setup context
this.Ctx = new DemoContext();
//Get The ObjectContext to be able to work with generics.
this.ObjectContext = this.Ctx.ObjectContext();
//Attach the SavingChanges event.
this.ObjectContext.SavingChanges += (sender, e) => BeforeSave(this.GetChangedOrNewEntities());
//Get EntityName and ContainerName
this.EntitySetName = this.ObjectContext.CreateObjectSet<T>().EntitySet.Name;
this.CurrentType = typeof(T);
}
private IEnumerable<PersistentEntity> GetChangedOrNewEntities()
{
const EntityState newOrModified = EntityState.Added | EntityState.Modified;
return this.ObjectContext.ObjectStateManager.GetObjectStateEntries(newOrModified)
.Where(x => x.Entity != null).Select(x => x.Entity as PersistentEntity);
}
public void BeforeSave(IEnumerable<PersistentEntity> entities)
{
foreach (var entity in entities)
{
entity.Updated = DateTime.Now;
entity.Created = !IsPersistent(entity) ? DateTime.Now : entity.Created;
}
}
private static bool IsPersistent(PersistentEntity entity)
{
return entity.Id != 0;
}
public virtual int SaveOrUpdate(T entity)
{
if (IsPersistent(entity))
this.Ctx.Set(this.CurrentType).Attach(entity);
else
this.Ctx.Set(typeof(T)).Add(entity);
return this.Ctx.SaveChanges();
}
public virtual T GetById(int id)
{
return this.Ctx.Set(this.CurrentType).Find(id) as T;
}
public virtual IEnumerable<T> GetAll()
{
return this.ObjectContext.CreateObjectSet<T>(this.EntitySetName);
}
public virtual bool Delete(T entity)
{
try
{
this.Ctx.Set(this.CurrentType).Remove(entity);
return true;
}
catch
{
return false;
}
}
}
The PersonRepository
[Export(typeof(IPersonRepository))]
public class PersonRepository : BaseRepository<Person>, IPersonRepository
{
}
public class PersonRepository : BaseRepository<Person>, IPersonRepository
{
}
[Export(typeof(ICarRepository))]
public class CarRepository : BaseRepository<Car>, ICarRepository
{
}
If needed you can override the baseclass methods in teh implementations since the baseclass methods is virtual…public class CarRepository : BaseRepository<Car>, ICarRepository
{
}
Create our class for handling MEF imports
In our repository we export interfaces, and in the servicelayer we want to import the exports… To be able to do this we will write a class that looks for exports in a predefined catalog (remember the postbuild event that copies the dll to our plugin catalog?)… We call the MEF class for Composable and make it abstract…public abstract class Composable
{
protected CompositionContainer CompositionContainer { get; set; }
protected AggregateCatalog AggregateCatalog { get; set; }
protected IList<string> DirectoryCatalogs { get; set; }
protected Composable()
{
this.DirectoryCatalogs = new List<string>();
this.AggregateCatalog = new AggregateCatalog();
//Create the catalog to look for plugins in...
this.AddDirectoryCatalog(ConfigurationManager.AppSettings.Get("MefDirectoryCatalog"));
//Add the catalog to the hostcatalog...
this.Compose();
}
protected void AddDirectoryCatalog(string catalog)
{
this.DirectoryCatalogs.Add(catalog);
}
public void Compose()
{
//Add Catalogs
foreach (var directoryCatalog in this.DirectoryCatalogs)
{
this.AggregateCatalog.Catalogs.Add(new DirectoryCatalog(directoryCatalog));
}
//Create compositioncontainer catalog export/import.
this.CompositionContainer = new CompositionContainer(this.AggregateCatalog);
//And... Compose it... :)
this.CompositionContainer.ComposeParts(this);
}
}
No need to say more about this class than that it will need a config-appsetting with the key “MefDirectoryCatalog” that points to the plugincatalog described in the beginning of the post.{
protected CompositionContainer CompositionContainer { get; set; }
protected AggregateCatalog AggregateCatalog { get; set; }
protected IList<string> DirectoryCatalogs { get; set; }
protected Composable()
{
this.DirectoryCatalogs = new List<string>();
this.AggregateCatalog = new AggregateCatalog();
//Create the catalog to look for plugins in...
this.AddDirectoryCatalog(ConfigurationManager.AppSettings.Get("MefDirectoryCatalog"));
//Add the catalog to the hostcatalog...
this.Compose();
}
protected void AddDirectoryCatalog(string catalog)
{
this.DirectoryCatalogs.Add(catalog);
}
public void Compose()
{
//Add Catalogs
foreach (var directoryCatalog in this.DirectoryCatalogs)
{
this.AggregateCatalog.Catalogs.Add(new DirectoryCatalog(directoryCatalog));
}
//Create compositioncontainer catalog export/import.
this.CompositionContainer = new CompositionContainer(this.AggregateCatalog);
//And... Compose it... :)
this.CompositionContainer.ComposeParts(this);
}
}
Implementing the generic ServiceInterfaces and import the exported repository with MEF
We do not want a project or dll referens to our datalayer from our servicelayer… This is where MEF does it´s magic. Since we export the repository interfaces we will tell our service to import the interfaces… In that way we do not need to reference the actuall datalayer but only the domainmodel in the Core project…. This gives us a plugable architecture and we can replace EFCodeFirst with a xml repository if we want… As long as the repository implements and exports the interfaces!!!The BaseService
Just like the BaseRepository we will have a generic abstract class for all services… Although much simpler than the repository class.public abstract class BaseService<T> : Composable, IService<T> where T : PersistentEntity
{
//MEF does not allow import on open generics.
//Workaround by setting this in Ctor of implementing classes.
//[Import(typeof(IRepository<>))]
protected IRepository<T> Repository;
public virtual IEnumerable<T> GetAll()
{
return this.Repository.GetAll();
}
public virtual T GetById(int id)
{
return this.Repository.GetById(id);
}
public virtual int SaveOrUpdate(T entity)
{
return this.Repository.SaveOrUpdate(entity);
}
public virtual bool Delete(T entity)
{
return this.Repository.Delete(entity);
}
}
Thats our baseservice hiding the repository… The servicelayer would be the place to add bussineslogic, would probably be done in the classes that inherit baseservice.{
//MEF does not allow import on open generics.
//Workaround by setting this in Ctor of implementing classes.
//[Import(typeof(IRepository<>))]
protected IRepository<T> Repository;
public virtual IEnumerable<T> GetAll()
{
return this.Repository.GetAll();
}
public virtual T GetById(int id)
{
return this.Repository.GetById(id);
}
public virtual int SaveOrUpdate(T entity)
{
return this.Repository.SaveOrUpdate(entity);
}
public virtual bool Delete(T entity)
{
return this.Repository.Delete(entity);
}
}
Now we add PersonService and CarService…
public class PersonService : BaseService<Person>, IPersonService
{
[Import(typeof(IPersonRepository))]
protected new IPersonRepository Repository;
public PersonService()
{
base.Repository = this.Repository;
}
}
{
[Import(typeof(IPersonRepository))]
protected new IPersonRepository Repository;
public PersonService()
{
base.Repository = this.Repository;
}
}
public class CarService : BaseService<Car>, ICarService
{
[Import(typeof(ICarRepository))]
protected new ICarRepository Repository;
public CarService()
{
base.Repository = this.Repository;
}
}
Just as the BaseRepository the BaseService has virtual methods if you want to override to do some specific logic…{
[Import(typeof(ICarRepository))]
protected new ICarRepository Repository;
public CarService()
{
base.Repository = this.Repository;
}
}
What have we created? An architectual overview!
Ok… I just want to show the loosely coupled architecture of this solution… All made possible by MEFAs you can see the Service-assmebly has no clue about the Data-assembly, the both just know about the Core and the Interfaces that they implement and export.
What´s next?
My idea was to also export the service interface and import the interfaces from a MVC controller, but I think this is to long already (have you been reading all thisTo be continued….
[EDIT 11-05-08] new post on the subject. Contains video and VSIX solutiontemplate.
_______________________________________________________
Thanks for reading
Regards
Uffe
This is a nice article..
ReplyDeleteIts very easy to understand ..
And this article is using to learn something about it..
c#, dot.net, php tutorial
Thanks a lot..!
What is composable in the base service class?
ReplyDeleterw: Hi there. I suppose that your question is why I inherit the Composable class in BaseService when there is no [Import] in BaseService? If so, the answer is that all classes/services that inherit BaseService has imports to satisfy IRepository (or the interface for example IPersonRepository)... So thats why the baseclass takes care of it...
ReplyDeleteEx: [Import(typeof(IPersonRepository))]
protected new IPersonRepository Repository;
If your question was "what is composable?" as in what class is it.. (but I do not think that was the question) you can see the class above BaseService.
Regards
Uffe
Article is a interesting. Good job. I hope soon see example
ReplyDeleteI Uffe hi!
ReplyDeleteI have little question. What do you say about create Context class how container and Entities how plug-in? It is possibly?
Hello Программист (programmer) :).
ReplyDeleteI´m not sure I follow you here... but please email me at codeplanner at gmail.com and describe what it is you want to achive. Everything is possible ;), but if you want to have plugins into the EF Context I think it will be hard.. Thought of it myself.
I´ll have been some changes in the code recently and will post a new version soon.
Hi Uffe. Thank you for answer. My name is Gragoryy or Greg. Yes I want plugins into the EF Context. But you're a right, it is hard.
ReplyDeleteBut I have two question now.
First Why you used ObjectContext class in
public ObjectContext ObjectContext()
{
return ((IObjectContextAdapter)this).ObjectContext;
}
And what you say about if used template int DemoContext?
public class DemoContext : DbContext where T : PersistentEntity
Nice article ...waiting for update! :)
ReplyDeleteLooking forward to download the demo of this very interesting project, especially to see how all this is going to glue to MVC. Thanks
ReplyDeleteSorry for the delay in the demo.. My dayjob is slowing me down ;)
ReplyDeleteBut you can download a VSIX (template) to get the project (so far) up and running. See the readme in the mvc project for info on howto get it running.
It has Scaffolding for entities and all repositories, services (it works but can be much improved) Link to template: http://bit.ly/mdHMgH
Your url not work
ReplyDeleteUffe you check what data save? Because I have problem in save date. Data only add, but update not work.
ReplyDeletepublic int Id { get; protected set; }
ReplyDeletechange to
public int Id { get; set; }
Because Id all time only 0 :)
And I do no know why. But code
this.Ctx.Set(this.CurrentType).Attach(entity);
data not save in DB
I found how fix. The problem is that in new version EF 4.1 for change you must uset type EntityState and other use method
ReplyDeletepublic virtual int SaveOrUpdate(T entity)
{
if (IsPersistent(entity))
this.Ctx.Entry(entity).State = EntityState.Modified;
else
this.Ctx.Entry(entity).State = EntityState.Added;
return this.Ctx.SaveChanges();
}
Hello Greg.
ReplyDeleteYou are rigth that there are some issues in the baserepository. I´ve made some changes since this post was released. Great that you´ve found a solution for your problem. My saveorupdate work however it´s now loking like this
public virtual int SaveOrUpdate(T entity)
{
if (IsPersistent(entity) && this.Ctx.Entry(entity).State == EntityState.Modified)
{
var internalEntity = this.Ctx.Set().Find(entity.Id); entity.Updated = DateTime.Now; this.Ctx.Entry(internalEntity).CurrentValues.SetValues(entity);
}
else
this.Ctx.Set().Add(entity);
this.Ctx.SaveChanges();
return entity.Id;
}
Will post a valid link to the template and a video showing a quick turorial this weekend.
Regards
Uffe
Greg... One more thing though. Do not remove the protected set on the primary key. It´s supposed to be protected so that only the repository can edit the value of the primary key! This is really important!
ReplyDeleteWill look into to your SaveOrUpdate, maybe I can use that instead.
Uffe hi. I glad what I can help you.
ReplyDeleteBut you wrong about protection property. If not do this, property Id all have 0
I am designing a similar solution with an extensible DAL-like Host. I have a few questions. Primarily, your library structure is different than what I've experienced. If I can determine how your structure relates to mine, I think I will be able to understand more of what you are doing. I have been trying to create my architecture on the basis of a shared library containing interfaces, etc.; the entity library (object context, POCO entities); and the Host/DAL. The Host then has the ability to provide the Business Logic Layer access to multiple contexts - e.g., an extensible data service provider. It seems that my problem exists in the BLL's ability to resolve a POCO entity. The POCO entities reside on the backend of the Host; and the enitity library cannot be referenced to resolve the POCO entity types. Is it likely that the architecture you demonstrate will assist in resolving this issue?
ReplyDeleteHi Greg.
ReplyDeleteIf your Id property is always 0 I´m afraid you´re doing something wrong :( My version works. And I´ve posted a video-demo and also the vsix template used in the video.
Maybe you can find the differences in the template?
Regards
Uffe
Hello D
ReplyDeleteI think I understand your problem, the way I see it my "Core" assembly will contain the domainmodel and also interfaces for data and services (a.k.a contracts). The BLL will have to have a reference to the Core (domainmodel). How are you going to perform BLL without the entity? Maybe I´m not understanding your problem correctly. As I said to greg: Try the template in my new post and watch the video. Maybe it will do it (and maybe not). Good Luck
Regards
Uffe
You are correct, this has been the issue - how will my BLL work without referencing the POCO entity objects? I have simply been attempting different methods to see if it possible for the BLL to pass a query function (Func<> Predicate) to my repository class for execution. I believe I will have to split my entity library so the Context is on the backend of the Host and the POCO entity objects in a separate library. I had hoped there would be a way for me to inject the Host repository mechanism with the POCO objects once the entity library is discovered using MEF.
ReplyDeleteOk. Interesting thoughts!!! Your BLL have to be aware of the entities, but there is no need for BLL to know about the context. You should always be able to replace your context without replacing anything else. It´s the interfaces that make this possible. Look at the demo and download the template in my latest post to see what I mean. You could have a generic BLL that passes the entities to the context.. But the question is then why have a BLL? And the context need to know of the entities at compiletime (when using entityframework). If you have a XML-repository (or similar) you could have unknown types and add them at runtime. It would be fun to try your idea though... Do post a note here if you find a solution. MEF is great and we use it in our websocketserver for plugin-functionality.
ReplyDeleteUffe hi! Maybe I not correct to write. When I get data from db Id not. But when I get data from page for edit Id = 0
ReplyDeleteHi greg. If thats the case i suspect that you have a problem in your view or actionmethod. Download the template and follow the video and it should work.
ReplyDeleteRegards
uffe
Hi .
ReplyDeleteI use mef in my asp.net application to make it plug-able.I have DAL which is created using Entity Framework CFT, I want every plug-in to use this DAL, but as you know every time the model changes, CFT need to re-create the database, I mean imagine I have created a Forum plug-in and it is obvious that it needs some tables and entities to work fine, But adding new entity to my DbContext Class would cause re-creation of database, Is there any way yo avoid it ?
well done !
ReplyDeleteI wish a was able to apply this with MEF but i finally ended with Spring.Net!
however, your implementation of repository pattern is the best i came across so far.
I actually hate the idea of one to one relation of an entity with repository and service. Like Person, PersonRepository and PersonService. Cheap design. I think we should have something like this http://www.asifashraf.com/search/?q=repository and for service, one service should cover one domain logic which can contact to a group of related tables.
ReplyDeletepublic interface IRepository[T] : IDisposable where T : class
ReplyDelete{
/// [summary]
/// Gets all objects from database
/// [/summary]
IQueryable[T] All();
/// [summary]
/// Gets objects from database by filter.
/// [/summary]
/// [param name="predicate"]Specified a filter[/param]
IQueryable[T] Filter(Expression[Func[T, bool]] predicate);
/// [summary]
/// Gets objects from database with filting and paging.
/// [/summary]
/// [typeparam name="Key"][/typeparam]
/// [param name="filter"]Specified a filter[/param]
/// [param name="total"]Returns the total records count of the filter.[/param]
/// [param name="index"]Specified the page index.[/param]
/// [param name="size"]Specified the page size[/param]
IQueryable[T] Filter(Expression[Func[T, bool]] filter, out int total, int index = 0, int size = 50);
/// [summary]
/// Gets the object(s) is exists in database by specified filter.
/// [/summary]
/// [param name="predicate"]Specified the filter expression[/param]
bool Contains(Expression[Func[T, bool]] predicate);
/// [summary]
/// Find object by keys.
/// [/summary]
/// [param name="keys"]Specified the search keys.[/param]
T Find(params object[] keys);
/// [summary]
/// Find object by specified expression.
/// [/summary]
/// [param name="predicate"][/param]
T Find(Expression[Func[T, bool]] predicate);
/// [summary]
/// Create a new object to database.
/// [/summary]
/// [param name="t"]Specified a new object to create.[/param]
T Create(T t);
/// [summary]
/// Delete the object from database.
/// [/summary]
/// [param name="t"]Specified a existing object to delete.[/param]
int Delete(T t);
/// [summary]
/// Delete objects from database by specified filter expression.
/// [/summary]
/// [param name="predicate"][/param]
int Delete(Expression[Func[T, bool]] predicate);
/// [summary]
/// Update object changes and save to database.
/// [/summary]
/// [param name="t"]Specified the object to save.[/param]
int Update(T t);
/// [summary]
/// Get the total objects count.
/// [/summary]
int Count { get; }
}
Muhammad Asif:
ReplyDeleteOk, if that works for you, then... good for you :)