My requirement is that i require to delete some rows from table and then insert some in the same table. I am using Unit of work and so both deletion and insertion are part of same transaction. But when i am trying to save the data, entity framework is duplicate key throwing error. Please find below an example and code:
example:Table name- Table1, Columns - col1(c.K), col2(C.K), col3
Row To delete- 78,1,1
RowTo Add- 78,1,1
78,2,2
My Unit of Work Class:
public class DataRepository<T> : IRepository<T> where T:class // IDisposable,
{
#region Variables
private readonly CWSEntities _context;
protected readonly IDbSet<T> _dbset;
#endregion
#region Constructors
public DataRepository()
{
_context = new CWSEntities();
_dbset = _context.Set<T>();
}
public DataRepository(CWSEntities context)
{
_context =context;
_dbset = _context.Set<T>();
}
#endregion
#region Methods
public IQueryable<T> All()
{
return _context.Set<T>();
}
// public IQueryable<T> AllInclude(params Expression<Func<T,object>>[] include)
public IQueryable<T> Include(params Expression<Func<T, object>>[] include)
{
IQueryable<T> retValue = _context.Set<T>();
foreach (var item in include)
{
retValue = retValue.Include(item);
}
return retValue;
}
public T GetById(object id)
{
return this._dbset.Find(id);
}
public IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
string includeProperties = "")
{
IQueryable<T> 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).ToList();
}
else
{
return query.ToList();
}
}
public T Add(T entity)
{
if (entity != null)
{
return _dbset.Add(entity);
}
return null;
}
public void Delete(object id)
{
T entityToDelete = _dbset.Find(id);
Delete(id);
}
public void Delete(T entityToDelete)
{
if (entityToDelete != null)
{
if (_context.Entry(entityToDelete).State == EntityState.Detached)
{
_dbset.Attach(entityToDelete);
}
_dbset.Remove(entityToDelete);
}
}
public void Update(T entityToUpdate)
{
if (entityToUpdate != null)
{
_dbset.Attach(entityToUpdate);
_context.Entry(entityToUpdate).State = EntityState.Modified;
// _context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
}
public virtual void Save()
{
try
{
_context.SaveChanges();
}
catch(DbEntityValidationException exception)
{
}
}
From what I can gather you are trying to add entities with the same primary key, as you said in your example
RowTo Add- 78,1,1 78,2,2
It doesn't look like your Add method is handling this correctly. You could first check if the entity exists by passing the primary key values of the entity and if it doesn't then do the add, if not then possibly an update?
public T Add(T entity, params object[] keys)
{
if (entity != null)
{
var existing = _dbset.Find(keys)
if (existing == null)
return _dbset.Add(entity);
else
Update(entity);
}
return null;
}
Related
I have tried using this generic functions to insert-update Entities but I always thought that maybe I am doing this totally wrong so therefore I would like to have your opinions/suggestions.
These are my Insert & Update functions:
public static bool Insert<T>(T item) where T : class
{
using (ApplicationDbContext ctx = new ApplicationDbContext())
{
try
{
ctx.Set<T>().Add(item);
ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// ...
}
}
}
public static bool Update<T>(T item) where T : class
{
using (ApplicationDbContext ctx = new ApplicationDbContext())
{
try
{
Type itemType = item.GetType();
// switch statement to perform actions according which type we are working on
ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// ...
}
}
}
I have learned that i can use ctx.Entry(item).State = EntityState.Modified; and I have seen so many ways of inserting-updating entities that I am very curious on what is the easiest most manageable way of performing CRUD actions ?
I know about the repository pattern and so on but i don't have much experience with interfaces or I don't seem to fully understand whats used so I prefer not to use it till I fully get it.
my approach for that is to use IRepository pattern to wrap CRUD and to make dependencies injection easier in my application, here an example on how i do it:
Define your contract like following:
(i am simplifying the example and admitting that all your tables have an integer id -i mean it is not guid or string or whatever- )
public interface IGenericRepository<TEntity> where TEntity : class
{
#region ReadOnlyRepository
TEntity GetById(int id);
ICollection<TEntity> GetAll();
ICollection<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includeProperties);
ICollection<TEntity> Query(Expression<Func<TEntity, bool>> expression, params Expression<Func<TEntity, object>>[] includeProperties);
PagedModel<TEntity> Query(Expression<Func<TEntity, bool>> expression, SortOptions sortOptions, PaginateOptions paginateOptions, params Expression<Func<TEntity, object>>[] includeProperties);
int Max(Expression<Func<TEntity, int>> expression);
#endregion
#region PersistRepository
bool Add(TEntity entity);
bool AddRange(IEnumerable<TEntity> items);
bool Update(TEntity entity);
bool Delete(TEntity entity);
bool DeleteById(int id);
#endregion
}
and then the implementation:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
#region Fields
protected DbContext CurrentContext { get; private set; }
protected DbSet<TEntity> EntitySet { get; private set; }
#endregion
#region Ctor
public GenericRepository(DbContext context)
{
CurrentContext = context;
EntitySet = CurrentContext.Set<TEntity>();
}
#endregion
#region IReadOnlyRepository Implementation
public virtual TEntity GetById(int id)
{
try
{
//use your logging method (log 4 net used here)
DomainEventSource.Log.Info(string.Format("getting entity {0} with id {1}", typeof(TEntity).Name, id));
return EntitySet.Find(id); //dbcontext manipulation
}
catch (Exception exception)
{
/// example of error handling
DomainEventSource.Log.Error(exception.Message);
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);// this is specific error formatting class you can do somthing like that to fit your needs
}
}
public virtual ICollection<TEntity> GetAll()
{
try
{
return EntitySet.ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
public virtual ICollection<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = LoadProperties(includeProperties);
return query.ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
public virtual ICollection<TEntity> Query(Expression<Func<TEntity, bool>> expression, params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = LoadProperties(includeProperties);
return query.Where(expression).ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
// returning paged results for example
public PagedModel<TEntity> Query(Expression<Func<TEntity, bool>> expression,SortOptions sortOptions, PaginateOptions paginateOptions, params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = EntitySet.AsQueryable().Where(expression);
var count = query.Count();
//Unfortunatly includes can't be covered with a UT and Mocked DbSets...
if (includeProperties.Length != 0)
query = includeProperties.Aggregate(query, (current, prop) => current.Include(prop));
if (paginateOptions == null || paginateOptions.PageSize <= 0 || paginateOptions.CurrentPage <= 0)
return new PagedModel<TEntity> // specific pagination model, you can define yours
{
Results = query.ToList(),
TotalNumberOfRecords = count
};
if (sortOptions != null)
query = query.OrderByPropertyOrField(sortOptions.OrderByProperty, sortOptions.IsAscending);
var skipAmount = paginateOptions.PageSize * (paginateOptions.CurrentPage - 1);
query = query.Skip(skipAmount).Take(paginateOptions.PageSize);
return new PagedModel<TEntity>
{
Results = query.ToList(),
TotalNumberOfRecords = count,
CurrentPage = paginateOptions.CurrentPage,
TotalNumberOfPages = (count / paginateOptions.PageSize) + (count % paginateOptions.PageSize == 0 ? 0 : 1)
};
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
}
#endregion
#region IPersistRepository Repository
public bool Add(TEntity entity)
{
try
{
// you can do some extention methods here to set up creation date when inserting or createdBy etc...
EntitySet.Add(entity);
return true;
}
catch (Exception exception)
{
//DomainEventSource.Log.Failure(ex.Message);
//or
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
}
public bool AddRange(IEnumerable<TEntity> items)
{
try
{
foreach (var entity in items)
{
Add(entity);
}
}
catch (Exception exception)
{
//DomainEventSource.Log.Failure(ex.Message);
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool Update(TEntity entity)
{
try
{
CurrentContext.Entry(entity).State = EntityState.Modified;
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool Delete(TEntity entity)
{
try
{
if (CurrentContext.Entry(entity).State == EntityState.Detached)
{
EntitySet.Attach(entity);
}
EntitySet.Remove(entity);
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool DeleteById(TKey id)
{
var entityToDelete = GetById(id);
return Delete(entityToDelete);
}
#endregion
#region Loading dependancies Utilities
private IQueryable<TEntity> LoadProperties(IEnumerable<Expression<Func<TEntity, object>>> includeProperties)
{
return includeProperties.Aggregate<Expression<Func<TEntity, object>>, IQueryable<TEntity>>(EntitySet, (current, includeProperty) => current.Include(includeProperty));
}
#endregion
}
I am admitting that your model classes are already created and decorated.
After this , you need to create your entityRepository like following : this is an example of managing entity called Ticket.cs
public class TicketRepository : GenericRepository<Ticket>, ITicketRepository
{
// the EntityRepository classes are made in case you have some ticket specific methods that doesn't
//have to be in generic repository
public TicketRepository(DbContext context)
: base(context)
{
}
// Add specific generic ticket methods here (not business methods-business methods will come later-)
}
After this comes the UnitOfWork class which allows us to unify entry to the database context and provides us an instance of repositories on demand using dependency injection
public class UnitOfwork : IUnitOfWork
{
#region Fields
protected DbContext CurrentContext { get; private set; }
private ITicketRepository _tickets;
#endregion
#region ctor
public UnitOfwork(DbContext context)
{
CurrentContext = context;
}
#endregion
#region UnitOfWorkBaseImplementation
public void Commit()
{
try
{
CurrentContext.SaveChanges();
}
catch (Exception e)
{
/// catch
}
}
public void Rollback()
{
foreach (var entry in CurrentContext.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Detached:
break;
case EntityState.Unchanged:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
#region complete RollBack()
private void RejectScalarChanges()
{
foreach (var entry in CurrentContext.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Detached:
break;
case EntityState.Unchanged:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private void RejectNavigationChanges()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var deletedRelationships = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).Where(e => e.IsRelationship && !this.RelationshipContainsKeyEntry(e));
var addedRelationships = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Where(e => e.IsRelationship);
foreach (var relationship in addedRelationships)
relationship.Delete();
foreach (var relationship in deletedRelationships)
relationship.ChangeState(EntityState.Unchanged);
}
private bool RelationshipContainsKeyEntry(System.Data.Entity.Core.Objects.ObjectStateEntry stateEntry)
{
//prevent exception: "Cannot change state of a relationship if one of the ends of the relationship is a KeyEntry"
//I haven't been able to find the conditions under which this happens, but it sometimes does.
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var keys = new[] { stateEntry.OriginalValues[0], stateEntry.OriginalValues[1] };
return keys.Any(key => objectContext.ObjectStateManager.GetObjectStateEntry(key).Entity == null);
}
#endregion
public void Dispose()
{
if (CurrentContext != null)
{
CurrentContext.Dispose();
}
}
#endregion
#region properties
public ITicketRepository Tickets
{
get { return _tickets ?? (_tickets = new TicketRepository(CurrentContext)); }
}
#endregion
}
Now for the last part we move to our business service layer and make a ServiceBase class which will be implemented by all business services
public class ServiceBase : IServiceBase
{
private bool _disposed;
#region IServiceBase Implementation
[Dependency]
public IUnitOfWork UnitOfWork { protected get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
var disposableUow = UnitOfWork as IDisposable;
if (disposableUow != null)
disposableUow.Dispose();
}
_disposed = true;
}
#endregion
}
and finally one example of business service class and how to use your CRUD and play with your business rules (i am using properties injection which is not the best to do so i suggest to change it and use constructor injection instead)
public class TicketService : ServiceBase, ITicketService
{
#region fields
private IUserService _userService;
private IAuthorizationService _authorizationService;
#endregion
#region Properties
[Dependency]
public IAuthorizationService AuthorizationService
{
set { _authorizationService = value; }
}
[Dependency]
public IUserService UserService
{
set { _userService = value; }
}
public List<ExceptionDetail> Errors { get; set; }
#endregion
#region Ctor
public TicketService()
{
Errors = new List<ExceptionDetail>();
}
#endregion
#region IServiceBase Implementation
/// <summary>
/// desc
/// </summary>
/// <returns>array of TicketAnomalie</returns>
public ICollection<Ticket> GetAll()
{
return UnitOfWork.Tickets.GetAll();
}
/// <summary>
/// desc
/// </summary>
/// <param name="id"></param>
/// <returns>TicketAnomalie</returns>
public Ticket GetTicketById(int id)
{
return UnitOfWork.Tickets.GetById(id);
}
/// <summary>
/// description here
/// </summary>
/// <returns>Collection of Ticket</returns>
public ICollection<Ticket> GetAllTicketsWithDependencies()
{
return UnitOfWork.Tickets.Query(tick => true, tick => tick.Anomalies);
}
/// <summary>
/// description here
/// </summary>
/// <param name="id"></param>
/// <returns>Ticket</returns>
public Ticket GetTicketWithDependencies(int id)
{
return UnitOfWork.Tickets.Query(tick => tick.Id == id, tick => tick.Anomalies).SingleOrDefault();
}
/// <summary>
/// Add new ticket to DB
/// </summary>
/// <param name="anomalieId"></param>
/// <returns>Boolean</returns>
public bool Add(int anomalieId)
{
var anomalie = UnitOfWork.Anomalies.Query(ano => ano.Id.Equals(anomalieId), ano => ano.Tickets).FirstOrDefault();
var currentUser = WacContext.Current;
var superv = _userService.GetSupervisorUserProfile();
var sup = superv.FirstOrDefault();
if (anomalie != null)
{
var anomalies = new List<Anomalie>();
var anom = UnitOfWork.Anomalies.GetById(anomalieId);
anomalies.Add(anom);
if (anomalie.Tickets.Count == 0 && sup != null)
{
var ticket = new Ticket
{
User = sup.Id,
CreatedBy = currentUser.GivenName,
Anomalies = anomalies,
Path = UnitOfWork.SearchCriterias.GetById(anom.ParcoursId),
ContactPoint = UnitOfWork.ContactPoints.GetById(anom.ContactPointId)
};
UnitOfWork.Tickets.Add(ticket);
UnitOfWork.Commit();
}
}
else
{
Errors.Add(AnomaliesExceptions.AnoNullException);
}
if (Errors.Count != 0) throw new BusinessException(Errors);
return true;
}
public bool Update(Ticket ticket)
{
if (ticket == null)
{
Errors.Add(AnomaliesExceptions.AnoNullException);
}
else
if (!Exists(ticket.Id))
{
Errors.Add(AnomaliesExceptions.AnoToUpdateNotExistException);
}
if (Errors.Count != 0) throw new BusinessException(Errors);
UnitOfWork.Tickets.Update(ticket);
UnitOfWork.Commit();
return true;
}
public bool Exists(int ticketId)
{
var operationDbEntity =
UnitOfWork.Tickets.Query(t => t.Id.Equals(ticketId)).ToList();
return operationDbEntity.Count != 0;
}
#endregion
#region Business Implementation
//play with your buiness :)
#endregion
}
Finally,
i suggest that you redo this using asynchronous methods (async await since it allows a better management of service pools in the web server)
Note that this is my own way of managing my CRUD with EF and Unity. you can find a lot of other implementations that can inspire you.
Hope this helps,
This is my IGenericRepository
public interface IGenericRepository<T>
{
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Func<T, bool> predicate);
void Insert(T obj);
void Save();
}
and here is its implementation
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private ApplicationDbContext db;
private DbSet<T> table = null;
public GenericRepository(ApplicationDbContext db)
{
this.db = db;
table = db.Set<T>();
}
public IEnumerable<T> GetAll()
{
return table.ToList();
}
public IEnumerable<T> GetMany(Func<T, bool> predicate)
{
var data=table.Where(predicate);
return data;
}
public void Insert(T obj)
{
table.Add(obj);
}
public void Save()
{
db.SaveChanges();
}
}
Here is the repository class for Department
public interface IDepartmentRepository : IGenericRepository<Department>
{
IEnumerable<Department> GetAlldepartment();
void Save1();
}
public class DepartmentRepository : GenericRepository<Department>, IDepartmentRepository
{
private ApplicationDbContext db;
public DepartmentRepository(ApplicationDbContext db):base(db)
{
this.db = db;
}
public IEnumerable<Department> GetAlldepartment()
{
var v= from c in db.Departments
select c;
return v;
}
public void Save1()
{
db.SaveChanges();
}
}
As same I have another repository for Customer
public interface ICustomerRepository : IGenericRepository<Customer>
{
IEnumerable<Customer> SelectAll();
void Update(Customer obj);
void Delete(string id);
}
public class CustomerRepository : GenericRepository<Customer>, ICustomerRepository
{
private ApplicationDbContext db;
//public CustomerRepository()
//{
// this.db = new ApplicationDbContext();
//}
public CustomerRepository(ApplicationDbContext db)
: base(db)
{
this.db = db;
}
public IEnumerable<Customer> SelectAll()
{
var data = this.GetMany(a => a.Id == 1);
return data;
}
public void Update(Customer obj)
{
db.Entry(obj).State = EntityState.Modified;
}
public void Delete(string id)
{
Customer existing = db.Customers.Find(id);
db.Customers.Remove(existing);
}
}
and finally my controller is
public class DepartmentController : Controller
{
DepartmentRepository _departmentRepository=null;
ICustomerRepository _customerRepository=null;
ApplicationDbContext _context = new ApplicationDbContext();
public DepartmentController()
{
this._departmentRepository = new DepartmentRepository(_context);
this._customerRepository = new CustomerRepository(_context);
}
public ActionResult Index()
{
var data = _departmentRepository.GetAlldepartment();
return View(data);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Department Department)
{
_departmentRepository.Insert(Department);
List<Customer> list = new List<Customer>();
for (int i = 0; i < 5; i++)
{
list.Add(new Customer
{
Id = i,
Name = i + " Hi"
});
_customerRepository.Insert(list[i]);
}
_departmentRepository.Save();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
If I want to save Department and Customer from DepartmentController, then I just create an instance of my DBcontext object and pass same object to both repository classes. Is there any problem? If so please help me how I can do this.
Create a TransactionScope in an ActionFilter and put it on your controller action
[AttributeUsage(AttributeTargets.Method)]
public class TransactionScopeAttribute : ActionFilterAttribute
{
private TransactionScope TransactionScope { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
TransactionScope =
new TransactionScope(TransactionScopeOption.Required, new TransactionOptions
{
IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted
});
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (TransactionScope == null)
return;
if (filterContext.Exception == null)
{
TransactionScope.Complete();
return;
}
TransactionScope.Dispose();
}
}
I'm pretty new to unit testing and I'm having some problems with regards, to unit testing a generic repository in my application. I've implemented the unit of work pattern in my ASP.NET MVC application. My classes look like this:
public class UnitOfWork : IUnitOfWork
{
private bool disposed = false;
private IGenericRepository<Shop> _shopRespository;
public UnitOfWork(PosContext context)
{
this.Context = context;
}
public PosContext Context { get; private set; }
public IGenericRepository<Shop> ShopRepository
{
get
{
return this._shopRespository ?? (this._shopRespository = new GenericRepository<Shop>(this.Context));
}
}
public void SaveChanges()
{
this.Context.SaveChanges();
}
public void Dispose()
{
this.Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.Context.Dispose();
}
this.disposed = true;
}
}
}
public class PosContext : DbContext, IPosContext
{
public DbSet<Shop> Shops { get; private set; }
}
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
private readonly PosContext context;
private readonly DbSet<T> dbSet;
public GenericRepository(PosContext context)
{
this.context = context;
this.dbSet = context.Set<T>();
}
public virtual IEnumerable<T> Get(
Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
string includeProperties = "")
{
IQueryable<T> query = this.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).ToList();
}
else
{
return query.ToList();
}
}
public virtual T Find(object id)
{
return this.dbSet.Find(id);
}
public virtual void Add(T entity)
{
this.dbSet.Add(entity);
}
public virtual void Remove(object id)
{
T entityToDelete = this.dbSet.Find(id);
this.Remove(entityToDelete);
}
public virtual void Remove(T entityToDelete)
{
if (this.context.Entry(entityToDelete).State == EntityState.Detached)
{
this.dbSet.Attach(entityToDelete);
}
this.dbSet.Remove(entityToDelete);
}
public virtual void Update(T entityToUpdate)
{
this.dbSet.Attach(entityToUpdate);
this.context.Entry(entityToUpdate).State = EntityState.Modified;
}
I'm using NUnit and FakeItEasy to write my unit tests. In my set up function, I create a UnitIfWork object with a fake PosContext object. I then populate the context with a few Shop objects.
[SetUp]
public void SetUp()
{
this.unitOfWork = new UnitOfWork(A.Fake<PosContext>());
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 1, Name = "Test name1" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 2, Name = "Test name2" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 3, Name = "Test name3" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 4, Name = "Test name4" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 5, Name = "Test name5" });
this.Controller = new ShopController(this.unitOfWork);
}
It works fine when I test the Find-method of the GenericRepository. The correct Shop object is returned and I can assert that it works fine:
[TestCase]
public void DetailsReturnsCorrectShop()
{
// Arrange
int testId = 1;
// Act
Shop shop = this.unitOfWork.ShopRepository.Find(testId);
ViewResult result = this.Controller.Details(testId) as ViewResult;
// Assert
Shop returnedShop = (Shop)result.Model;
Assert.AreEqual(testId, returnedShop.Id);
}
But when I want to test that the Get-method returns all shops from the repository, if I do not give any filter params, I get an empty list back. I can't figure out why?
[TestCase]
public void IndexReturnsListOfShops()
{
// Arrange
// Act
ViewResult result = this.Controller.Index() as ViewResult;
// Assert
List<Shop> returnedShops = (List<Shop>)result.Model;
Assert.AreEqual(5, returnedShops.Count);
}
The ShopController looks like this:
public class ShopController : Controller
{
private readonly IUnitOfWork unitOfWork;
public ShopController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
// GET: /Shop/
public ActionResult Index()
{
return View(this.unitOfWork.ShopRepository.Get());
}
// GET: /Shop/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Shop shop = this.unitOfWork.ShopRepository.Find(id);
if (shop == null)
{
return HttpNotFound();
}
return View(shop);
}
}
Can you help me figure out why I get an empty list back from the Get-method?
Have been implementing Generic Repository, Unit of Work pattern with EF5 Code First from a number of resources and have come up with the following assemblies.
Interfaces, Contexts, Model, Repositories, UnitsOfWork
In the Context assembly I have my migrations folder which contains Configuration.cs
internal sealed class Configuration : DbMigrationsConfiguration<Context.SportsContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(Context.SportsContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
As you can see this DbMigrationsConfiguration takes in my SportsContext which is also defined in the contexts assembly (Contexts folder)
public class SportsContext : IDbContext
{
private readonly DbContext _context;
public SportsContext()
{
_context = new DbContext("SportsContext");
}
public void Dispose()
{
_context.Dispose();
}
public IDbSet<T> GetEntitySet<T>() where T : class
{
return _context.Set<T>();
}
public void ChangeState<T>(T entity, EntityState state) where T : class
{
_context.Entry(entity).State = state;
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
This implements IDbContext which is defined in the Interfaces assembly
public interface IDbContext : IDisposable
{
IDbSet<T> GetEntitySet<T>() where T : class;
void ChangeState<T>(T entity, EntityState state) where T : class;
void SaveChanges();
}
In my UnitsOfWork assembly I have the following class
public class SportUnitOfWork : IUnitofWork
{
private readonly IDbContext _context;
public SportUnitOfWork()
{
_context = new SportsContext();
}
private GenericRepository<Team> _teamRepository;
private GenericRepository<Fixture> _fixtureRepository;
public GenericRepository<Team> TeamRepository
{
get { return _teamRepository ?? (_teamRepository = new GenericRepository<Team>(_context)); }
}
public GenericRepository<Fixture> FixtureRepository
{
get { return _fixtureRepository ?? (_fixtureRepository = new GenericRepository<Fixture>(_context)); }
}
public void Save()
{
_context.SaveChanges();
}
public IDbContext Context
{
get { return _context; }
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
For examples sake I have added the GenericRepository class in the Repositories assembly
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private IDbContext _context;
public GenericRepository(IDbContext context)
{
_context = context;
}
public GenericRepository(IUnitofWork uow)
{
_context = uow.Context;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_context == null) return;
_context.Dispose();
_context = null;
}
public void Add(T entity)
{
_context.GetEntitySet<T>().Add(entity);
}
public void Update(T entity)
{
_context.ChangeState(entity, EntityState.Modified);
}
public void Remove(T entity)
{
_context.ChangeState(entity, EntityState.Deleted);
}
public T FindSingle(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
{
var set = FindIncluding(includes);
return (predicate == null) ? set.FirstOrDefault() : set.FirstOrDefault(predicate);
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
{
var set = FindIncluding(includes);
return (predicate == null) ? set : set.Where(predicate);
}
public IQueryable<T> FindIncluding(params Expression<Func<T, object>>[] includeProperties)
{
var set = _context.GetEntitySet<T>();
if (includeProperties != null)
{
foreach (var include in includeProperties)
{
set.Include(include);
}
}
return set.AsQueryable();
}
public int Count(Expression<Func<T, bool>> predicate = null)
{
var set = _context.GetEntitySet<T>();
return (predicate == null) ? set.Count() : set.Count(predicate);
}
public bool Exist(Expression<Func<T, bool>> predicate = null)
{
var set = _context.GetEntitySet<T>();
return (predicate == null) ? set.Any() : set.Any(predicate);
}
}
The problem I have is in the Configuration class which inherits from DbMigrationsConfiguration is expecting a DbContext parameter.
Error is Error 1 The type 'Contexts.Context.SportsContext' cannot be used as type parameter 'TContext' in the generic type or method 'System.Data.Entity.Migrations.DbMigrationsConfiguration'. There is no implicit reference conversion from 'Contexts.Context.SportsContext' to 'System.Data.Entity.DbContext'.
I can change the SportsContext to also inherit from DbContext but then I need to add a reference to EntityFramework 5 in the UnitsOfWork assembly as we want to possibly change or take out each layer without any reference to underlying models which is why i went with this pattern.
As we are looking at adding further contexts and models in the future so wanted to setup a architecture in that we could just add the context, model and then implement the relevant interfaces as and when needed.
A WebAPI Restful Web Service will be interacting with our data via the SportUnitOfWork, if I have understood the patterns correctly.
If anyone has any ideas on how I could do this or anything that I am doing wrong please let me know
thanks in advance Mark
Resolved this by doing the following
Changed my SportsContext class to a BaseContext which is abstract
public abstract class BaseContext : IDbContext
{
protected DbContext Context;
public void Dispose()
{
Context.Dispose();
}
public IDbSet<T> GetEntitySet<T>() where T : class
{
return Context.Set<T>();
}
public void Add<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Added;
}
public void Update<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Modified;
}
public void Delete<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Deleted;
}
public void SaveChanges()
{
// At the moment we are conforming to server wins when handling concurrency issues
// http://msdn.microsoft.com/en-us/data/jj592904
try
{
Context.SaveChanges();
}
catch (DbUpdateConcurrencyException e)
{
//Refresh using ServerWins
var objcontext = ((IObjectContextAdapter) Context).ObjectContext;
var entry = e.Entries;
objcontext.Refresh(RefreshMode.StoreWins, entry);
SaveChanges();
}
}
private DbEntityEntry GetDbEntityEntrySafely<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = Context.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
// Set Entity Key
var objcontext = ((IObjectContextAdapter) Context).ObjectContext;
if (objcontext.TryGetObjectByKey(dbEntityEntry.Entity))
Context.Set<T>().Attach(entity);
}
return dbEntityEntry;
}
}
created in the Context folder a new class called FootballContext which inherits from BaseContext.
public class FootballContext : BaseContext
{
public FootballContext(string connectionstringName)
{
Context = new BaseFootballContext(connectionstringName);
}
}
Created a new folder called DbContexts
In here created the following classes,
public class BaseFootballContext : DbContext
{
public BaseFootballContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
public IDbSet<Fixture> Fixtures { get; set; }
public IDbSet<Team> Teams { get; set; }
}
public class MigrationsContextFactory : IDbContextFactory<BaseFootballContext>
{
public BaseFootballContext Create()
{
return new BaseFootballContext("FootballContext");
}
}
now my Configuration class can take in the BaseFootballContext as this is a DbContext.
My UnitOfWork class can now set the context to be FootballContext so does not have to reference EntityFramework.
This works with Migrations as well.
Only problem I have now is to figure out how to get this to work in a disconnected environment as I am having a problem reattaching entities and applying updates.
I am using MVC2 with Entity Framework 4 and am trying to implement a Repository and UnitofWork pattern. My Adds and Deletes work fine, however when the edit is called _context.SaveChanges() does save the new changes to the database. I have stepped through the code in debug and see the new values are in the object being passed to the Edit function in the controller, but when the commit is called, nothing is updated.See my code below: Thanks for your help.
Here is my IRepository
namespace EventScheduling.DataModel.Custom
{
public interface IRepository<T>
{
void Add(T newEntity);
void Remove(T entity);
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
IQueryable<T> FindAll();
}
}
SQLRepository Implementation
namespace EventScheduling.DataModel.Custom
{
public class SQLRepository<T>:IRepository<T> where T : class
{
protected ObjectSet<T> _objectSet;
public SQLRepository(ObjectContext context)
{
_objectSet = context.CreateObjectSet<T>();
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate){
return _objectSet.Where(predicate);
}
public void Add(T newEntity)
{
_objectSet.AddObject(newEntity);
}
public void Remove(T entity)
{
_objectSet.DeleteObject(entity);
}
public IQueryable<T> FindAll()
{
return _objectSet;
}
}
}
Unit of Work Implementation
namespace EventScheduling.DataModel.Custom
{
public interface IUnitOfWork
{
IRepository<utility_event> utility_event { get; }
IRepository<event_activity> event_activity { get; }
IRepository<employee> employee{ get; }
IRepository<activity_resource> activity_resource { get; }
IRepository<Elmah_Error> Elmah_Error { get; }
IRepository< location> location { get; }
IRepository<location_station> location_station { get; }
IRepository<registration_type> registration_type { get; }
IRepository< resource> resource { get; }
IRepository<shift> shift { get; }
IRepository<shift_person> shift_person{ get; }
IRepository<event_type> event_type { get; }
IRepository<status> status { get; }
void Commit();
}
}
SqlUnitOfWork Implementation
namespace EventScheduling.DataModel.Custom
{
public class SqlUnitOfWork: IUnitOfWork
{
readonly ObjectContext _context;
const String ConnectionStringName = "EventEntities";
public SqlUnitOfWork()
{
var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
_context = new ObjectContext(connectionString);
_context.ContextOptions.LazyLoadingEnabled = true;
}
public IRepository<utility_event> utility_event
{
get {
if (_utilityEvent == null)
{
_utilityEvent = new SQLRepository<utility_event>(_context);
}
return _utilityEvent;
}
}
public IRepository<event_activity> event_activity
{
get
{
if (_eventActivities == null)
{
_eventActivities = new SQLRepository<event_activity>(_context);
}
return _eventActivities;
}
}
public IRepository<employee> employee
{
get
{
if (_employees == null)
{
_employees = new SQLRepository<employee>(_context);
}
return _employees;
}
}
public IRepository<activity_resource> activity_resource
{
get
{
if (_activityResources == null)
{
_activityResources = new SQLRepository<activity_resource>(_context);
}
return _activityResources;
}
}
public IRepository<location> location
{
get
{
if (_locations == null)
{
_locations = new SQLRepository<location>(_context);
}
return _locations;
}
}
public IRepository<location_station> location_station
{
get
{
if (_locationStations == null)
{
_locationStations = new SQLRepository<location_station>(_context);
}
return _locationStations;
}
}
public IRepository<registration_type> registration_type
{
get
{
if (_registrationTypes == null)
{
_registrationTypes = new SQLRepository<registration_type>(_context);
}
return _registrationTypes;
}
}
public IRepository<resource> resource
{
get
{
if (_resources == null)
{
_resources = new SQLRepository<resource>(_context);
}
return _resources;
}
}
public IRepository<shift> shift
{
get
{
if (_shifts == null)
{
_shifts = new SQLRepository<shift>(_context);
}
return _shifts;
}
}
public IRepository<shift_person> shift_person
{
get
{
if (_shiftPersons == null)
{
_shiftPersons = new SQLRepository<shift_person>(_context);
}
return _shiftPersons;
}
}
public IRepository<Elmah_Error> Elmah_Error
{
get
{
if (_ElmahError == null)
{
_ElmahError = new SQLRepository<Elmah_Error>(_context);
}
return _ElmahError;
}
}
public IRepository<event_type> event_type
{
get
{
if (_eventTypes == null)
{
_eventTypes = new SQLRepository<event_type>(_context);
}
return _eventTypes;
}
}
public IRepository<status> status
{
get
{
if (_status == null)
{
_status = new SQLRepository<status>(_context);
}
return _status;
}
}
public void Commit()
{
_context.SaveChanges();
}
SQLRepository<utility_event> _utilityEvent = null;
SQLRepository<event_activity> _eventActivities = null;
SQLRepository<employee> _employees = null;
SQLRepository<activity_resource> _activityResources = null;
SQLRepository<Elmah_Error> _ElmahError = null;
SQLRepository<location> _locations = null;
SQLRepository<location_station> _locationStations = null;
SQLRepository<registration_type> _registrationTypes = null;
SQLRepository<resource> _resources = null;
SQLRepository<shift> _shifts = null;
SQLRepository<shift_person> _shiftPersons = null;
SQLRepository<event_type> _eventTypes = null;
SQLRepository<status> _status = null;
}
}
Controller Edit Implementation
public ActionResult Edit(int id, shift e)
{
if (!ModelState.IsValid)
{
return View(e);
//return to view
}
else
{
e.shift_begin = (DateTime)e.shift_date.Value.Add(e.shift_begin.Value.TimeOfDay);
e.shift_end = (DateTime)e.shift_date.Value.Add(e.shift_end.Value.TimeOfDay);
_unitOfWork.Commit();
return RedirectToAction("Details", "EventActivity", new { id = e.activity_id });
}
}
When you want to update entity in EF you must first Attach entity to ObjectContext instance or instance of related ObjectSet and use ObjectStateManager.ChangeObjectState to set state of entity to EntityState.Modified.
Other possibility is to load entity first from database and merge changes directly into this entity.
Check link in comment for examples or try to use search box. This question is one of the most frequent in EF tag.