Transaction integrity in asp.net core with entity framework and generic repository
up vote
1
down vote
favorite
I am not very experienced in Entity Framework, I previously used ADO.Net and Dapper for data persistence. For now, I only read Microsoft docs and watched a few video tutorials. I tried to stick to well accepted advice and have built a generic repository infrastructure with the help of from here and there.
IEntity
public interface IEntity
{
object Id { get; set; }
string CreatedBy { get; set; }
DateTime CreatedOn { get; set; }
string ModifiedBy { get; set; }
DateTime? ModifiedOn { get; set; }
}
public interface IEntity<T> : IEntity
{
new T Id { get; set; }
}
BaseEntity
public abstract class BaseEntity<T> : IEntity<T>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public T Id { get; set; }
object IEntity.Id { get { return Id; } set { } }
public string CreatedBy { get; set; }
private DateTime? createdOn;
[DataType(DataType.DateTime)]
public DateTime CreatedOn
{
get { return createdOn ?? DateTime.UtcNow; }
set { createdOn = value; }
}
public string ModifiedBy { get; set; }
[DataType(DataType.DateTime)]
public DateTime? ModifiedOn { get; set; }
}
User Entity
public class User : BaseEntity<int>
{
public string FullName { get; set; }
public string Email { get; set; }
public UserType UserType { get; set; }
public string Password { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
IRepository
public interface IRepository<TEntity> where TEntity: IEntity
{
IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null);
TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null);
TEntity GetById(object id);
TEntity GetOne(Expression<Func<TEntity, bool>> filter = null,
string includeProperties = null);
void Create(TEntity entity, string createdBy = null);
void Update(TEntity entity, string modifiedBy = null);
void Delete(TEntity entity);
void Delete(object id);
void Save();
}
EFRepository
public class EFRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private DbContext _context;
public EFRepository(BlogContext context)
{
_context = context;
}
protected virtual IEnumerable<TEntity> GetQueryable(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null)
{
includeProperties = includeProperties ?? string.Empty;
IQueryable<TEntity> query = _context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
query = orderBy(query);
}
if (skip.HasValue)
{
query = query.Skip(skip.Value);
}
if (take.HasValue)
{
query = query.Take(take.Value);
}
return query.AsEnumerable();
}
public IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null, int? skip = null, int? take = null)
{
return GetQueryable(orderBy: orderBy, includeProperties: includeProperties, skip: skip, take: take);
}
public TEntity GetById(object id)
{
return _context.Set<TEntity>().Find(id);
}
public TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null)
{
return GetQueryable(filter: filter, orderBy: orderBy, includeProperties: includeProperties).FirstOrDefault();
}
public TEntity GetOne(Expression<Func<TEntity, bool>> filter = null, string includeProperties = null)
{
return GetQueryable(filter: filter, includeProperties: includeProperties).SingleOrDefault();
}
public void Save()
{
try
{
_context.SaveChanges();
}
catch (Exception e)
{
}
}
public void Create(TEntity entity, string createdBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = createdBy;
_context.Add<TEntity>(entity);
}
public void Update(TEntity entity, string modifiedBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = modifiedBy;
_context.Set<TEntity>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
var dbSet = _context.Set<TEntity>();
if (_context.Entry(entity).State == EntityState.Detached)
dbSet.Attach(entity);
dbSet.Remove(entity);
}
public void Delete(object id)
{
TEntity entity = _context.Set<TEntity>().Find(id);
Delete(entity);
}
}
IService
public interface IService
{
IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity;
}
BaseService
public abstract class BaseService : IService
{
public BaseService(BlogContext context, ILogger<BaseService> logger)
{
_context = context;
_logger = logger;
}
protected DbContext _context { get; set; }
protected readonly ILogger<BaseService> _logger;
public IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity
{
return new EFRepository<T>((BlogContext)context);
}
public IRepository<T> GetRepository<T>() where T : class, IEntity
{
return GetRepository<T>(_context);
}
}
IUserService
public interface IAuthorService
{
bool DeleteAuthorWithEntireStuff(int userId);
}
UserService
public class UserService : BaseService, IAuthorService
{
public UserService(BlogContext context, ILogger<UserService> logger) : base(context, logger) { }
public bool DeleteAuthorWithEntireStuff(int id)
{
try
{
var userRepo = GetRepository<User>();
var commentRepo = GetRepository<Comment>();
var postRepo = GetRepository<Post>();
var user = userRepo.GetAll().Where(u => u.Id == id).FirstOrDefault();
if (user != null)
{
var commentList = commentRepo.GetAll().Where(c => c.UserId == id).ToList();
var postList = postRepo.GetAll().Where(p => p.AuthorId == id).ToList();
foreach (var item in commentList)
commentRepo.Delete(item);
foreach (var item in postList)
postRepo.Delete(item);
userRepo.Delete(user);
_context.SaveChanges();
}
return true;
}
catch (Exception)
{
throw;
}
}
}
And finally controller
public class UserController : Controller
{
private readonly IRepository<User> _repository;
private readonly IUserService _authorService;
public UserController(IRepository<User> repository, IUserService authorService)
{
_repository = repository;
_authorService = authorService;
}
[HttpPost]
public IActionResult Create([FromBody]User model)
{
try
{
_repository.Create(model);
_repository.Save();
return Ok(model);
}
catch(Exception ex)
{
return BadRequest(ex);
}
}
[HttpPost]
[Route("DeletUserEntirely")]
public IActionResult DeleteUserEntirely([FromBody]User model)
{
try
{
if (_authorService.DeleteUserEntirely(model.Id))
return Ok();
else
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
}
As you see in the UserService code when I need to run multiple commands in one transaction, I make use of the ef's unit of work (_context.SaveChanges()
).
I did not create any extra unit of work classes as suggested at few places or wrap my repos in transaction scope. As discussed here, some people defend unit of work implementations for transaction integrity that I do not still get it why while ef implementing uow pattern already.
I haven't tested above code so much but till now it seems ok.
Btw, I used Chris Pratt's Generic Repository approach.
Any problems with this approach, especially with the perspective of transactions in generic repository use entity framework?
c# entity-framework repository transactions
add a comment |
up vote
1
down vote
favorite
I am not very experienced in Entity Framework, I previously used ADO.Net and Dapper for data persistence. For now, I only read Microsoft docs and watched a few video tutorials. I tried to stick to well accepted advice and have built a generic repository infrastructure with the help of from here and there.
IEntity
public interface IEntity
{
object Id { get; set; }
string CreatedBy { get; set; }
DateTime CreatedOn { get; set; }
string ModifiedBy { get; set; }
DateTime? ModifiedOn { get; set; }
}
public interface IEntity<T> : IEntity
{
new T Id { get; set; }
}
BaseEntity
public abstract class BaseEntity<T> : IEntity<T>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public T Id { get; set; }
object IEntity.Id { get { return Id; } set { } }
public string CreatedBy { get; set; }
private DateTime? createdOn;
[DataType(DataType.DateTime)]
public DateTime CreatedOn
{
get { return createdOn ?? DateTime.UtcNow; }
set { createdOn = value; }
}
public string ModifiedBy { get; set; }
[DataType(DataType.DateTime)]
public DateTime? ModifiedOn { get; set; }
}
User Entity
public class User : BaseEntity<int>
{
public string FullName { get; set; }
public string Email { get; set; }
public UserType UserType { get; set; }
public string Password { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
IRepository
public interface IRepository<TEntity> where TEntity: IEntity
{
IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null);
TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null);
TEntity GetById(object id);
TEntity GetOne(Expression<Func<TEntity, bool>> filter = null,
string includeProperties = null);
void Create(TEntity entity, string createdBy = null);
void Update(TEntity entity, string modifiedBy = null);
void Delete(TEntity entity);
void Delete(object id);
void Save();
}
EFRepository
public class EFRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private DbContext _context;
public EFRepository(BlogContext context)
{
_context = context;
}
protected virtual IEnumerable<TEntity> GetQueryable(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null)
{
includeProperties = includeProperties ?? string.Empty;
IQueryable<TEntity> query = _context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
query = orderBy(query);
}
if (skip.HasValue)
{
query = query.Skip(skip.Value);
}
if (take.HasValue)
{
query = query.Take(take.Value);
}
return query.AsEnumerable();
}
public IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null, int? skip = null, int? take = null)
{
return GetQueryable(orderBy: orderBy, includeProperties: includeProperties, skip: skip, take: take);
}
public TEntity GetById(object id)
{
return _context.Set<TEntity>().Find(id);
}
public TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null)
{
return GetQueryable(filter: filter, orderBy: orderBy, includeProperties: includeProperties).FirstOrDefault();
}
public TEntity GetOne(Expression<Func<TEntity, bool>> filter = null, string includeProperties = null)
{
return GetQueryable(filter: filter, includeProperties: includeProperties).SingleOrDefault();
}
public void Save()
{
try
{
_context.SaveChanges();
}
catch (Exception e)
{
}
}
public void Create(TEntity entity, string createdBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = createdBy;
_context.Add<TEntity>(entity);
}
public void Update(TEntity entity, string modifiedBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = modifiedBy;
_context.Set<TEntity>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
var dbSet = _context.Set<TEntity>();
if (_context.Entry(entity).State == EntityState.Detached)
dbSet.Attach(entity);
dbSet.Remove(entity);
}
public void Delete(object id)
{
TEntity entity = _context.Set<TEntity>().Find(id);
Delete(entity);
}
}
IService
public interface IService
{
IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity;
}
BaseService
public abstract class BaseService : IService
{
public BaseService(BlogContext context, ILogger<BaseService> logger)
{
_context = context;
_logger = logger;
}
protected DbContext _context { get; set; }
protected readonly ILogger<BaseService> _logger;
public IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity
{
return new EFRepository<T>((BlogContext)context);
}
public IRepository<T> GetRepository<T>() where T : class, IEntity
{
return GetRepository<T>(_context);
}
}
IUserService
public interface IAuthorService
{
bool DeleteAuthorWithEntireStuff(int userId);
}
UserService
public class UserService : BaseService, IAuthorService
{
public UserService(BlogContext context, ILogger<UserService> logger) : base(context, logger) { }
public bool DeleteAuthorWithEntireStuff(int id)
{
try
{
var userRepo = GetRepository<User>();
var commentRepo = GetRepository<Comment>();
var postRepo = GetRepository<Post>();
var user = userRepo.GetAll().Where(u => u.Id == id).FirstOrDefault();
if (user != null)
{
var commentList = commentRepo.GetAll().Where(c => c.UserId == id).ToList();
var postList = postRepo.GetAll().Where(p => p.AuthorId == id).ToList();
foreach (var item in commentList)
commentRepo.Delete(item);
foreach (var item in postList)
postRepo.Delete(item);
userRepo.Delete(user);
_context.SaveChanges();
}
return true;
}
catch (Exception)
{
throw;
}
}
}
And finally controller
public class UserController : Controller
{
private readonly IRepository<User> _repository;
private readonly IUserService _authorService;
public UserController(IRepository<User> repository, IUserService authorService)
{
_repository = repository;
_authorService = authorService;
}
[HttpPost]
public IActionResult Create([FromBody]User model)
{
try
{
_repository.Create(model);
_repository.Save();
return Ok(model);
}
catch(Exception ex)
{
return BadRequest(ex);
}
}
[HttpPost]
[Route("DeletUserEntirely")]
public IActionResult DeleteUserEntirely([FromBody]User model)
{
try
{
if (_authorService.DeleteUserEntirely(model.Id))
return Ok();
else
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
}
As you see in the UserService code when I need to run multiple commands in one transaction, I make use of the ef's unit of work (_context.SaveChanges()
).
I did not create any extra unit of work classes as suggested at few places or wrap my repos in transaction scope. As discussed here, some people defend unit of work implementations for transaction integrity that I do not still get it why while ef implementing uow pattern already.
I haven't tested above code so much but till now it seems ok.
Btw, I used Chris Pratt's Generic Repository approach.
Any problems with this approach, especially with the perspective of transactions in generic repository use entity framework?
c# entity-framework repository transactions
2
The repository-pattern is like a virus. It's spreading everywhere and causes only harm ;-)
– t3chb0t
Sep 14 at 13:47
You should really avoid the repository approach, and instead look at something like mehdi.me/ambient-dbcontext-in-ef6 .
– BCdotWEB
Nov 13 at 15:38
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I am not very experienced in Entity Framework, I previously used ADO.Net and Dapper for data persistence. For now, I only read Microsoft docs and watched a few video tutorials. I tried to stick to well accepted advice and have built a generic repository infrastructure with the help of from here and there.
IEntity
public interface IEntity
{
object Id { get; set; }
string CreatedBy { get; set; }
DateTime CreatedOn { get; set; }
string ModifiedBy { get; set; }
DateTime? ModifiedOn { get; set; }
}
public interface IEntity<T> : IEntity
{
new T Id { get; set; }
}
BaseEntity
public abstract class BaseEntity<T> : IEntity<T>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public T Id { get; set; }
object IEntity.Id { get { return Id; } set { } }
public string CreatedBy { get; set; }
private DateTime? createdOn;
[DataType(DataType.DateTime)]
public DateTime CreatedOn
{
get { return createdOn ?? DateTime.UtcNow; }
set { createdOn = value; }
}
public string ModifiedBy { get; set; }
[DataType(DataType.DateTime)]
public DateTime? ModifiedOn { get; set; }
}
User Entity
public class User : BaseEntity<int>
{
public string FullName { get; set; }
public string Email { get; set; }
public UserType UserType { get; set; }
public string Password { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
IRepository
public interface IRepository<TEntity> where TEntity: IEntity
{
IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null);
TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null);
TEntity GetById(object id);
TEntity GetOne(Expression<Func<TEntity, bool>> filter = null,
string includeProperties = null);
void Create(TEntity entity, string createdBy = null);
void Update(TEntity entity, string modifiedBy = null);
void Delete(TEntity entity);
void Delete(object id);
void Save();
}
EFRepository
public class EFRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private DbContext _context;
public EFRepository(BlogContext context)
{
_context = context;
}
protected virtual IEnumerable<TEntity> GetQueryable(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null)
{
includeProperties = includeProperties ?? string.Empty;
IQueryable<TEntity> query = _context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
query = orderBy(query);
}
if (skip.HasValue)
{
query = query.Skip(skip.Value);
}
if (take.HasValue)
{
query = query.Take(take.Value);
}
return query.AsEnumerable();
}
public IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null, int? skip = null, int? take = null)
{
return GetQueryable(orderBy: orderBy, includeProperties: includeProperties, skip: skip, take: take);
}
public TEntity GetById(object id)
{
return _context.Set<TEntity>().Find(id);
}
public TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null)
{
return GetQueryable(filter: filter, orderBy: orderBy, includeProperties: includeProperties).FirstOrDefault();
}
public TEntity GetOne(Expression<Func<TEntity, bool>> filter = null, string includeProperties = null)
{
return GetQueryable(filter: filter, includeProperties: includeProperties).SingleOrDefault();
}
public void Save()
{
try
{
_context.SaveChanges();
}
catch (Exception e)
{
}
}
public void Create(TEntity entity, string createdBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = createdBy;
_context.Add<TEntity>(entity);
}
public void Update(TEntity entity, string modifiedBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = modifiedBy;
_context.Set<TEntity>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
var dbSet = _context.Set<TEntity>();
if (_context.Entry(entity).State == EntityState.Detached)
dbSet.Attach(entity);
dbSet.Remove(entity);
}
public void Delete(object id)
{
TEntity entity = _context.Set<TEntity>().Find(id);
Delete(entity);
}
}
IService
public interface IService
{
IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity;
}
BaseService
public abstract class BaseService : IService
{
public BaseService(BlogContext context, ILogger<BaseService> logger)
{
_context = context;
_logger = logger;
}
protected DbContext _context { get; set; }
protected readonly ILogger<BaseService> _logger;
public IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity
{
return new EFRepository<T>((BlogContext)context);
}
public IRepository<T> GetRepository<T>() where T : class, IEntity
{
return GetRepository<T>(_context);
}
}
IUserService
public interface IAuthorService
{
bool DeleteAuthorWithEntireStuff(int userId);
}
UserService
public class UserService : BaseService, IAuthorService
{
public UserService(BlogContext context, ILogger<UserService> logger) : base(context, logger) { }
public bool DeleteAuthorWithEntireStuff(int id)
{
try
{
var userRepo = GetRepository<User>();
var commentRepo = GetRepository<Comment>();
var postRepo = GetRepository<Post>();
var user = userRepo.GetAll().Where(u => u.Id == id).FirstOrDefault();
if (user != null)
{
var commentList = commentRepo.GetAll().Where(c => c.UserId == id).ToList();
var postList = postRepo.GetAll().Where(p => p.AuthorId == id).ToList();
foreach (var item in commentList)
commentRepo.Delete(item);
foreach (var item in postList)
postRepo.Delete(item);
userRepo.Delete(user);
_context.SaveChanges();
}
return true;
}
catch (Exception)
{
throw;
}
}
}
And finally controller
public class UserController : Controller
{
private readonly IRepository<User> _repository;
private readonly IUserService _authorService;
public UserController(IRepository<User> repository, IUserService authorService)
{
_repository = repository;
_authorService = authorService;
}
[HttpPost]
public IActionResult Create([FromBody]User model)
{
try
{
_repository.Create(model);
_repository.Save();
return Ok(model);
}
catch(Exception ex)
{
return BadRequest(ex);
}
}
[HttpPost]
[Route("DeletUserEntirely")]
public IActionResult DeleteUserEntirely([FromBody]User model)
{
try
{
if (_authorService.DeleteUserEntirely(model.Id))
return Ok();
else
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
}
As you see in the UserService code when I need to run multiple commands in one transaction, I make use of the ef's unit of work (_context.SaveChanges()
).
I did not create any extra unit of work classes as suggested at few places or wrap my repos in transaction scope. As discussed here, some people defend unit of work implementations for transaction integrity that I do not still get it why while ef implementing uow pattern already.
I haven't tested above code so much but till now it seems ok.
Btw, I used Chris Pratt's Generic Repository approach.
Any problems with this approach, especially with the perspective of transactions in generic repository use entity framework?
c# entity-framework repository transactions
I am not very experienced in Entity Framework, I previously used ADO.Net and Dapper for data persistence. For now, I only read Microsoft docs and watched a few video tutorials. I tried to stick to well accepted advice and have built a generic repository infrastructure with the help of from here and there.
IEntity
public interface IEntity
{
object Id { get; set; }
string CreatedBy { get; set; }
DateTime CreatedOn { get; set; }
string ModifiedBy { get; set; }
DateTime? ModifiedOn { get; set; }
}
public interface IEntity<T> : IEntity
{
new T Id { get; set; }
}
BaseEntity
public abstract class BaseEntity<T> : IEntity<T>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public T Id { get; set; }
object IEntity.Id { get { return Id; } set { } }
public string CreatedBy { get; set; }
private DateTime? createdOn;
[DataType(DataType.DateTime)]
public DateTime CreatedOn
{
get { return createdOn ?? DateTime.UtcNow; }
set { createdOn = value; }
}
public string ModifiedBy { get; set; }
[DataType(DataType.DateTime)]
public DateTime? ModifiedOn { get; set; }
}
User Entity
public class User : BaseEntity<int>
{
public string FullName { get; set; }
public string Email { get; set; }
public UserType UserType { get; set; }
public string Password { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
IRepository
public interface IRepository<TEntity> where TEntity: IEntity
{
IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null);
TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null);
TEntity GetById(object id);
TEntity GetOne(Expression<Func<TEntity, bool>> filter = null,
string includeProperties = null);
void Create(TEntity entity, string createdBy = null);
void Update(TEntity entity, string modifiedBy = null);
void Delete(TEntity entity);
void Delete(object id);
void Save();
}
EFRepository
public class EFRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private DbContext _context;
public EFRepository(BlogContext context)
{
_context = context;
}
protected virtual IEnumerable<TEntity> GetQueryable(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = null,
int? skip = null,
int? take = null)
{
includeProperties = includeProperties ?? string.Empty;
IQueryable<TEntity> query = _context.Set<TEntity>();
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
query = orderBy(query);
}
if (skip.HasValue)
{
query = query.Skip(skip.Value);
}
if (take.HasValue)
{
query = query.Take(take.Value);
}
return query.AsEnumerable();
}
public IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null, int? skip = null, int? take = null)
{
return GetQueryable(orderBy: orderBy, includeProperties: includeProperties, skip: skip, take: take);
}
public TEntity GetById(object id)
{
return _context.Set<TEntity>().Find(id);
}
public TEntity GetFirst(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = null)
{
return GetQueryable(filter: filter, orderBy: orderBy, includeProperties: includeProperties).FirstOrDefault();
}
public TEntity GetOne(Expression<Func<TEntity, bool>> filter = null, string includeProperties = null)
{
return GetQueryable(filter: filter, includeProperties: includeProperties).SingleOrDefault();
}
public void Save()
{
try
{
_context.SaveChanges();
}
catch (Exception e)
{
}
}
public void Create(TEntity entity, string createdBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = createdBy;
_context.Add<TEntity>(entity);
}
public void Update(TEntity entity, string modifiedBy = null)
{
entity.CreatedOn = DateTime.UtcNow;
entity.CreatedBy = modifiedBy;
_context.Set<TEntity>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
var dbSet = _context.Set<TEntity>();
if (_context.Entry(entity).State == EntityState.Detached)
dbSet.Attach(entity);
dbSet.Remove(entity);
}
public void Delete(object id)
{
TEntity entity = _context.Set<TEntity>().Find(id);
Delete(entity);
}
}
IService
public interface IService
{
IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity;
}
BaseService
public abstract class BaseService : IService
{
public BaseService(BlogContext context, ILogger<BaseService> logger)
{
_context = context;
_logger = logger;
}
protected DbContext _context { get; set; }
protected readonly ILogger<BaseService> _logger;
public IRepository<T> GetRepository<T>(DbContext context) where T : class, IEntity
{
return new EFRepository<T>((BlogContext)context);
}
public IRepository<T> GetRepository<T>() where T : class, IEntity
{
return GetRepository<T>(_context);
}
}
IUserService
public interface IAuthorService
{
bool DeleteAuthorWithEntireStuff(int userId);
}
UserService
public class UserService : BaseService, IAuthorService
{
public UserService(BlogContext context, ILogger<UserService> logger) : base(context, logger) { }
public bool DeleteAuthorWithEntireStuff(int id)
{
try
{
var userRepo = GetRepository<User>();
var commentRepo = GetRepository<Comment>();
var postRepo = GetRepository<Post>();
var user = userRepo.GetAll().Where(u => u.Id == id).FirstOrDefault();
if (user != null)
{
var commentList = commentRepo.GetAll().Where(c => c.UserId == id).ToList();
var postList = postRepo.GetAll().Where(p => p.AuthorId == id).ToList();
foreach (var item in commentList)
commentRepo.Delete(item);
foreach (var item in postList)
postRepo.Delete(item);
userRepo.Delete(user);
_context.SaveChanges();
}
return true;
}
catch (Exception)
{
throw;
}
}
}
And finally controller
public class UserController : Controller
{
private readonly IRepository<User> _repository;
private readonly IUserService _authorService;
public UserController(IRepository<User> repository, IUserService authorService)
{
_repository = repository;
_authorService = authorService;
}
[HttpPost]
public IActionResult Create([FromBody]User model)
{
try
{
_repository.Create(model);
_repository.Save();
return Ok(model);
}
catch(Exception ex)
{
return BadRequest(ex);
}
}
[HttpPost]
[Route("DeletUserEntirely")]
public IActionResult DeleteUserEntirely([FromBody]User model)
{
try
{
if (_authorService.DeleteUserEntirely(model.Id))
return Ok();
else
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
}
As you see in the UserService code when I need to run multiple commands in one transaction, I make use of the ef's unit of work (_context.SaveChanges()
).
I did not create any extra unit of work classes as suggested at few places or wrap my repos in transaction scope. As discussed here, some people defend unit of work implementations for transaction integrity that I do not still get it why while ef implementing uow pattern already.
I haven't tested above code so much but till now it seems ok.
Btw, I used Chris Pratt's Generic Repository approach.
Any problems with this approach, especially with the perspective of transactions in generic repository use entity framework?
c# entity-framework repository transactions
c# entity-framework repository transactions
edited Nov 13 at 15:29
BCdotWEB
8,47511638
8,47511638
asked Sep 4 at 12:13
ibubi
7119
7119
2
The repository-pattern is like a virus. It's spreading everywhere and causes only harm ;-)
– t3chb0t
Sep 14 at 13:47
You should really avoid the repository approach, and instead look at something like mehdi.me/ambient-dbcontext-in-ef6 .
– BCdotWEB
Nov 13 at 15:38
add a comment |
2
The repository-pattern is like a virus. It's spreading everywhere and causes only harm ;-)
– t3chb0t
Sep 14 at 13:47
You should really avoid the repository approach, and instead look at something like mehdi.me/ambient-dbcontext-in-ef6 .
– BCdotWEB
Nov 13 at 15:38
2
2
The repository-pattern is like a virus. It's spreading everywhere and causes only harm ;-)
– t3chb0t
Sep 14 at 13:47
The repository-pattern is like a virus. It's spreading everywhere and causes only harm ;-)
– t3chb0t
Sep 14 at 13:47
You should really avoid the repository approach, and instead look at something like mehdi.me/ambient-dbcontext-in-ef6 .
– BCdotWEB
Nov 13 at 15:38
You should really avoid the repository approach, and instead look at something like mehdi.me/ambient-dbcontext-in-ef6 .
– BCdotWEB
Nov 13 at 15:38
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
For whom interested in this subject, my searching on this has ended up with the clean architecture repository and its implemented sample reference project eshoponweb.
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
For whom interested in this subject, my searching on this has ended up with the clean architecture repository and its implemented sample reference project eshoponweb.
add a comment |
up vote
0
down vote
For whom interested in this subject, my searching on this has ended up with the clean architecture repository and its implemented sample reference project eshoponweb.
add a comment |
up vote
0
down vote
up vote
0
down vote
For whom interested in this subject, my searching on this has ended up with the clean architecture repository and its implemented sample reference project eshoponweb.
For whom interested in this subject, my searching on this has ended up with the clean architecture repository and its implemented sample reference project eshoponweb.
answered Sep 14 at 13:04
ibubi
7119
7119
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f203096%2ftransaction-integrity-in-asp-net-core-with-entity-framework-and-generic-reposito%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
The repository-pattern is like a virus. It's spreading everywhere and causes only harm ;-)
– t3chb0t
Sep 14 at 13:47
You should really avoid the repository approach, and instead look at something like mehdi.me/ambient-dbcontext-in-ef6 .
– BCdotWEB
Nov 13 at 15:38