C# database abstraction class with Entity Framework - c#

I want to create a common database abstraction in order to expose the same interface without worrying about the type of the DbContext that the database manage.
Here there is an example in order to explain well the concept:
public interface IDatabase<T> where T : DbContext {
void Add<T>(T entity);
void Remove<T>(T entity);
void SaveChanges();
}
The implementation can be:
public MyDatabase<T> : IDatabase<T> where T : MyContext {
public T Context { get; private set; }
//singleton contructor ...
public void Add<TC>(TC entity) {
Context.Set<TC>().Add(entity);
}
public void Remove<TC>(TC entity) {
Context.Set<TC>().Add(entity);
}
public void SaveChanges {
Context.SaveChanges();
}
}
The goals of this design are different: expose the same interface in order to decouple logic from database, change quickly the database (context), create one time the context and reuse during all the application lifetime (lock mechanism are required).
The problem is that the interface hides all the types of the sets in the context.
Context.Set<TC>().Add(entity); //this line don't compile
I'm not sure that this design is the best practise. How can I implement a design that offers these features?

Why you does not use the standard pattern for example Repository pattern you can also combine it with UnitOfWork.
You make it complicated for your self, just check this.
public interface IMyClassRepository : IDisposable
{
IQueryable<MyClass> All { get; }
IQueryable<MyClass> AllIncluding(params Expression<Func<MyClass, object>>[] includeProperties);
MyClass Find(int id);
void InsertOrUpdate(MyClass myClass);
void Delete(int id);
void Save();
}
public class MyClassRepository : IMyClassRepository
{
DBContext context = new DBContext();
public IQueryable<MyClass> All
{
get { return context.Employees; }
}
public IQueryable<MyClass> AllIncluding(params Expression<Func<MyClass, object>>[] includeProperties)
{
IQueryable<MyClass> query = context.MyClasses;
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
}
public MyClass Find(int id)
{
return context.MyClasses.Find(id);
}
public void InsertOrUpdate(MyClass myClass)
{
if (myClass.Id == default(int))
{
// New entity
context.MyClasses.Add(myClass);
}
else
{
// Existing entity
context.Entry(myClass).State = EntityState.Modified;
}
}
public void Delete(int id)
{
var employee = context.Employees.Find(id);
context.Employees.Remove(employee);
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
public class MyClass
{
}

Related

Data is not saving in db using generic repository and Entity Framework 5.0

I've created application asp.net using a generic repository and Entity Framework.
The abstract class has this code:
public interface IGenericRepository<T> where T : class
{
IQueryable<T> GetAll();
IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
void Add(T entity);
void Delete(T entity);
void Edit(T entity);
void Save();
}
and the GenericRepository class contains the following code:
public abstract class GenericRepository<C, T> : IGenericRepository<T> where T : class where C : DbContext, new()
{
private C _entities = new C();
public C Context {
get { return _entities; }
set { _entities = value; }
}
public virtual IQueryable<T> GetAll() {
IQueryable<T> query = _entities.Set<T>();
return query;
}
public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate) {
IQueryable<T> query = _entities.Set<T>().Where(predicate);
return query;
}
public virtual void Add(T entity) {
_entities.Set<T>().Add(entity);
}
public virtual void Delete(T entity) {
_entities.Set<T>().Remove(entity);
}
public virtual void Edit(T entity) {
_entities.Entry(entity).State = System.Data.EntityState.Modified;
}
public virtual void Save() {
_entities.SaveChanges();
}
}
Now I want to create class for each of my database tables to save the data.
Here is the code for one of my database tables:
public class AmountDLL : GenericRepository<HMSEntities, Tbl_Amount>
{
//:Base<Tbl_Amount>
public override void Add(Tbl_Amount entity)
{
base.Add(entity);
}
}
At last I have tried to add data through front end on button click
protected void btnsave_Click(object sender, EventArgs e)
{
AmountDLL amtdll = new AmountDLL();
Tbl_Amount tblamt = new Tbl_Amount();
tblamt.Amt_Type = txt_amt_type.Text;
tblamt.UserId = User.Identity.GetUserId();
if (CheckBox1.Checked == true)
{
tblamt.IsActive = true;
}
else
{
tblamt.IsActive = false;
}
amtdll.Add(tblamt);
}
When I run the application, it worked fine, but it did not save the data in the table.
Please check and suggest any changes
In Entity Framework for every entity to save the data either it's insert, update or delete, we call the SaveChanges() on instance of that entity.
In your case you need to call the Save() method for persisting to database which you have defined in your repository class for that which abstracts the SaveChanges() call to your respective Entity type:
amtdll.Add(tblamt);
amtdll.Save(); // this needs to be called.

How to add multi DbContext With UnitOfWork & DatabaseFactory & Generic Repository

I want to add TWO DbContext in My ASP.NET MVC 5 App, One DbContext For ASPIdentity and The Other For My APP DB. I am Using Repository Pattern.
m y problem is, How to To specify the Entity of each DbContext in BaseRepository ?
Here Is What I did.
1- DatabaseFactory & IDatabaseFactory
public class DatabaseFactory<T> where T : DbContext,new()
{
private T dbContext;
public T Init()
{
return dbContext ?? (dbContext = new T());
}
}
public interface IDatabaseFactory<T> where T : DbContext
{
T Init();
}
2- IUnitOfWork & UnitOfWork
public class UnitOfWork<T> : IUnitOfWork<T> where T : DbContext
{
private readonly IDatabaseFactory<T> dbFactory;
private T dbContext;
public UnitOfWork(IDatabaseFactory<T> dbFactory)
{
this.dbFactory = dbFactory;
}
protected T DbContext
{
get { return dbContext ?? (dbContext = dbFactory.Init()); }
}
public void Commit()
{
DbContext.SaveChanges();
}
}
public interface IUnitOfWork<T> where T : DbContext, IDisposable
{
void Commit();
}
3- BaseRepository.cs
public abstract class BaseRepository<T> where T : class
{
#region Properties
private DbContext dataContext;
private readonly IDbSet<T> dbSet;
protected IDatabaseFactory DbFactory
{
get;
private set;
}
protected DbContext dbContext
{
get { return dataContext ?? (dataContext = DbFactory.Init()); }
}
#endregion
protected BaseRepository(IDatabaseFactory dbFactory)
{
this.DbFactory = dbFactory;
this.dbSet = this.DbContext.Set<T>();
}
#region Implementation
public virtual void Add(T entity)
{
dbSet.Add(entity);
}
public virtual void Update(T entity)
{
dbSet.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = dbSet.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
dbSet.Remove(obj);
}
public virtual T GetById(int id)
{
return dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return dbSet.Where(where).FirstOrDefault<T>();
}
#endregion
}
I'm also trying to implement generic repository pattern but without UOW.
For creating two DbContext you should add one more type in Base Repository.
Also creation logic for DbFactory should be in UOW only not in BaseRepository.
Here's simplified code for you. And be more specific about what you tried.
2- IUnitOfWork & UnitOfWork
public class UnitOfWork<T1, T2> : IUnitOfWork<T1, T2> where T1 : DbContext where T2 : DbContext {
// FOr DbFactories
private readonly IDatabaseFactory<T1> _dbFactory1;
private readonly IDatabaseFactory<T2> _dbFactory2;
//For Seperate DbContexes
private T _dbContext1;
private T _dbContext2;
public UnitOfWork () {
_dbFactory1 = new DatabaseFactory<T1> ();
_dbFactory2 = new DatabaseFactory<T2> ();
}
//For Accessiong DbContext Objects in Base Repository
protected T DbContext1 {
get { return _dbContext1 ?? (_dbContext1 = _dbFactory1.Init ()); }
}
protected T DbContext2 {
get { return _dbContext2 ?? (_dbContext2 = _dbFactory2.Init ()); }
}
public void Commit () {
DbContext1.SaveChanges ();
DbContext2.SaveChanges ();
}
}
public interface IUnitOfWork<T1, T2> where T1 : DbContext where T2 : DbContext, IDisposable {
void Commit ();
}
}
3 - BaseRepository and Example
public abstract class BaseRepository<T1,T2,T> : IUnitOfWork<T1, T2> where T : class where T1 : DbContext where T2 : DbContext {
#region Properties
// private DbContext _dataContext1; //for first DbContext
// private DbContext _dataContext1; //for second DbContext
private readonly IDbSet<T> _dbSet1; //Going to Perform Operations using Dbsets
private readonly IDbSet<T> _dbSet2;
//For Exposing DbContext to respective Implementing Repositories This is Optional
protected DbContext DataContext1 {
get { return DbContext1; } //retuning DbCOntext Object Created in UOW class
}
protected DbContext DataContext2 {
get { return DbContext2; }
}
//For Exposing DbSets to respective Implementing Repositories This is Optional
protected IDbSet<T> DbSet1 => _dbSet1;
protected IDbSet<T> DbSet2 => _dbSet2;
protected BaseRepository () {
_dbSet1 = DataContext1.Set<T> ();
//OR
_dbSet2 = DataContext2.Set<T> ();
}
#endregion
#region Implementation
#endregion
}
//SPecific Repository Example using Person Class
public class PersonRepository:BaseRepository<AspIdentityDbContext,AppDbContext,Person> {
//can use DbContexes from BaseRepository to write Additional Methods/Queries On dbSets
}
Try this and give feedback.

IQueryable does not return Count property

public interface IRepository<T> : IDisposable
{
void Add(T newEntity);
void Delete(T entity);
T Find(int id);
IQueryable<T> FindAll();
int Commit();
}
public class SqlRepository<T> : IRepository<T> where T : class
{
DbContext context;
DbSet<T> set;
public SqlRepository(DbContext context)
{
this.context = context;
this.set = context.Set<T>();
}
public void Add(T newEntity)
{
this.set.Add(newEntity);
}
public void Delete(T entity)
{
}
public T Find(int id)
{
throw new Exception("todo");
}
public IQueryable<T> FindAll()
{
return this.set;
}
public int Commit()
{
return this.context.SaveChanges();
}
public void Dispose()
{
this.context.Dispose();
}
}
using (IRepository<Contact> e = new SqlRepository<Contact>(new AppointmentReminderDb()))
{
e.Add(new Contact() { Active = true });
e.Add(new Contact() { Active = true });
e.Commit();
var count = await e.FindAll().Count(); // do not get Count property
}
In the above line of code, I don't understand why I am not getting Count property. Instead I get CountAsynch. I really want to simply get Count property.
My IQueryable for FindAll is correctly defined in the interface and in the class method.
You may have forgotten to include the right namespace.
The method you're seeing in IntelliSense is named QueryableExtensions.CountAsync<TSource> defined in the System.Data.Entity namespace, which returns Task<int> and, as such, should be awaited.
The method (not property) you're looking for is named Queryable.Count<T>() and is defined in the System.Linq namespace. It returns an int and should not be awaited.
If the operation involves IO, which it probably does, you want to use CountAsync.

Task<TEntity> preferred way to do repository

public async Task<TEntity> GetByIdAsync(int id) {
return await Context.FindByAsync(id);
}
and
public IQueryable<TEntity> GetById(Expression<Func<TEntity, int>> predicate) {
return Context.Where(predicate).FirstOrDefault;
}
I'm new to repositories and looking for tutorials and now kinda know what it does but the Task struck me. Between the two which is more preferred? I know it's a stupid question but it would help me understand them better. Any help would be much appreciated. Thanks!
First to your question:
GetByIdAsync: is used to load the data asynchrone from the database, If you have a lot of data for example you have to load about 10000 entries from database then this method it will be the right choice(You can also use bulk operation).
GetById: synchrone load the data from the DB this method is good if your query take just a few milliseconds and does not be called a lot of times from the same thread.
How to use them:
var employee= await new EmployeeRepository.GetByIdAsync(1);---> your method(caller) must be here also Async otherwise, you have to use task.
var employee= new EmployeeRepository.GetById(1);
If your Repository class return IQueryable then you have to do ToList.First();
You need Entity framework 6 or later =>> support of Async.,
otherwise you have to do it by your self!
Example: Let say your business object:
public class Employee
{
public int Id { get; set; }
public string FullName { get; set; }
}
// Now using this we will have a simple context class.
public class HRContext : DbContext
{
public DbSet<DomainClasses.Employee> Employees { get; set; }
}
// After that, define the repository interface IEmployeeRepository.
public interface IEmployeeRepository : IDisposable
{
IQueryable<Employee> All { get; }
IQueryable<Employee> AllAsync { get; }
IQueryable<Employee> AllIncluding(params Expression<Func<Employee, object>>[] includeProperties);
Employee Find(int id);
Employee FindAsync(int id);
void InsertOrUpdate(Employee employee);
void Delete(int id);
void Save();
}
// Then the Repository class called EmployeeRepository.
public class EmployeeRepository : IEmployeeRepository
{
HRContext context = new HRContext();
public IQueryable<Employee> All
{
get { return context.Employees; }
}
public IQueryable<Employee> AllIncluding(params Expression<Func<Employee, object>>[] includeProperties)
{
IQueryable<Employee> query = context.Employees;
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
}
public Employee Find(int id)
{
return context.Employees.Find(id);
}
public void InsertOrUpdate(Employee employee)
{
if (employee.Id == default(int)) {
// New entity
context.Employees.Add(employee);
} else {
// Existing entity
context.Entry(employee).State = EntityState.Modified;
}
}
public void Delete(int id)
{
var employee = context.Employees.Find(id);
context.Employees.Remove(employee);
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
I get the soruce code from :
http://blogs.msdn.com/b/wriju/archive/2013/08/23/using-repository-pattern-in-entity-framework.aspx
For example for a generic repository:
public interface IGenericRepository<T> where T : class {
IQueryable<T> GetAll();
IQueryable<T> GetAllAsync();
IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
IQueryable<T> FindByAsync(Expression<Func<T, bool>> predicate);
void Add(T entity);
void Delete(T entity);
void Edit(T entity);
void Save();
}
Where T is the base entity for all your entities.
here is the complete generic example:
http://www.tugberkugurlu.com/archive/generic-repository-pattern-entity-framework-asp-net-mvc-and-unit-testing-triangle
For better separation of concern you can also combine the repository pattern with unit of work as described by Martin Fowler book:
http://martinfowler.com/eaaCatalog/unitOfWork.html
Those two pieces of code do different things.
First piece finds a single entity.
var user = await new Repo<User>.GetByIdAsync(12);
Second piece executes the where clause and returns the first element or null. It is badly named because it does not actually force searching by Id.
var user = new Repo<User>.GetById(u => u.Username=="Bob");
The task is there to support .NET 4.5 await and async commands.

How to Implement Repository FindAll() Method?

I have the following Repository Pattern. Requirement is to “Find All accounts whose owner’s name is Lijo”. So, I need to write a FindAll function. How to write this function?
Constraints are:
1) The client “BankAccountService” should not use classes from 'DBML_Project'.
2) We should NOT use GetAll method to retireve complete list of accounts and then do a filter.
Note: I confronted this problem while working on the question Polymorphism: Is ORM entity a Domain Entity or Data Entity?
CODE
namespace ApplicationService_Bank
{
public class BankAccountService
{
RepositoryLayer.ILijosBankRepository accountRepository = new RepositoryLayer.LijosSimpleBankRepository();
public void FreezeAllAccountsForUser(string userName)
{
//Should not use assembly 'DBML_Project'.
IEnumerable<DomainEntitiesForBank.IBankAccount> accountsForUserWithNameLIJO = null;
//accountsForUserWithNameLIJO = accountRepository.FindAll(p => p.BankUser.Name == "Lijo");
}
}
}
namespace RepositoryLayer
{
public interface ILijosBankRepository
{
List<DomainEntitiesForBank.IBankAccount> GetAll();
IEnumerable<DBML_Project.BankAccount> FindAll(System.Func<DBML_Project.BankAccount, bool> predicate);
void SubmitChanges();
}
public class LijosSimpleBankRepository : ILijosBankRepository
{
private IBankAccountFactory bankFactory = new MySimpleBankAccountFactory();
public System.Data.Linq.DataContext Context
{
get;
set;
}
public virtual List<DomainEntitiesForBank.IBankAccount> GetAll()
{
List<DBML_Project.BankAccount> allItems = Context.GetTable<DBML_Project.BankAccount>().ToList();
List<DomainEntitiesForBank.IBankAccount> bankAccounts = new List<DomainEntitiesForBank.IBankAccount>();
foreach (DBML_Project.BankAccount acc in allItems)
{
DomainEntitiesForBank.IBankAccount theAccount = bankFactory.CreateAccount(acc.AccountType, acc.BankAccountID, acc.Status, acc.OpenedDate, acc.AccountOwnerID);
bankAccounts.Add(theAccount);
}
return bankAccounts;
}
public IEnumerable<DBML_Project.BankAccount> FindAll(System.Func<DBML_Project.BankAccount, bool> predicate)
{
//Where
var results = Context.GetTable<DBML_Project.BankAccount>().Where(predicate);
return results;
}
public virtual void SubmitChanges()
{
Context.SubmitChanges();
}
}
}
READING:
Returning IEnumerable<T> vs. IQueryable<T>
how to design Repository pattern to be easy switch to another ORM later?
A simple approach is to just build the query by hand:
public class SearchCriteria
{
public string Name { get; set; }
// ...more
}
public IEnumerable<Entity> FindAll(SearchCriteria criteria)
{
IQueryable<Entity> entities = _datasource.Entities; // replace with your L2S equivalent
if (criteria.Name != null)
entities = entities.Where(e => e.Name == criteria.Name);
// ...more
return entities;
}
If you don't want to return the generated objects directly, map to something else before you return:
return Map(entities); // IEnumerable<CustomObject> Map(IEnumerable<Entity> entities)

Categories