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?










share|improve this question




















  • 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















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?










share|improve this question




















  • 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













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?










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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














  • 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










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.






share|improve this answer





















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    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

























    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.






    share|improve this answer

























      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.






      share|improve this answer























        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.






        share|improve this answer












        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.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Sep 14 at 13:04









        ibubi

        7119




        7119






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            Сан-Квентин

            Алькесар

            Josef Freinademetz