I have the following situation.
I'm building a course management MVC site where I have courses but also course iterations I call them as it happens sometime that a new student will join just for one iteration.
I have a model courseIterations which possess a ICollection of students. When starting the course I want to generate the next nine iterations with default students which subscribed to the course. The problem I have is that when I list the iterations the students where not saved. What am I doing wrong? Or is there a better approach in general?
public class CourseIteration : AuditableEntity<int>
{
public Course Course { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Date")]
public DateTime Date { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
}
My attempt to fill
private void FillIterations(Course course, int coursePeriod)
{
var defaultStudents = _studentRepository.GetAllByCourse(course.CourseID).ToList(); // load the existing Items
course.Iterations = new List<CourseIteration>();
for (int i = 0; i < coursePeriod; i++)
{
var item = new CourseIteration
{
Course = course,
Date = course.StartDate.AddDays(i * 7),
Instructors = course.Instructors,
Students = new List<Student>()
};
foreach (var student in defaultStudents)
{
item.Students.Add(student);
}
_courseIterationRepository.Add(item);
course.Iterations.Add(item);
_courseIterationRepository.Save();
_courseRepository.Save();
}
}
I use a Generic Repository
public abstract class GenericRepository : IGenericRepository
where T : BaseEntity
{
protected DbContext _entities;
protected readonly IDbSet _dbset;
public GenericRepository(DbContext context)
{
_entities = context;
_dbset = context.Set<T>();
}
public virtual IEnumerable<T> GetAll()
{
return _dbset.AsEnumerable<T>();
}
public IEnumerable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IEnumerable<T> query = _dbset.Where(predicate).AsEnumerable();
return query;
}
public virtual T Add(T entity)
{
return _dbset.Add(entity);
}
public virtual T Attach(T entity)
{
return _dbset.Attach(entity);
}
public virtual T Delete(T entity)
{
return _dbset.Remove(entity);
}
public virtual void Edit(T entity)
{
_entities.Set<T>().Attach(entity);
_entities.Entry(entity).State = EntityState.Modified;
Save();
}
public virtual void Save()
{
_entities.SaveChanges();
}
Custom Save function could be the problem?
public override int SaveChanges()
{
var modifiedEntries = ChangeTracker.Entries()
.Where(x => x.Entity is IAuditableEntity
&& (x.State == System.Data.Entity.EntityState.Added || x.State == System.Data.Entity.EntityState.Modified));
foreach (var entry in modifiedEntries)
{
IAuditableEntity entity = entry.Entity as IAuditableEntity;
if (entity != null)
{
string identityName = Thread.CurrentPrincipal.Identity.Name;
DateTime now = DateTime.UtcNow;
if (entry.State == System.Data.Entity.EntityState.Added)
{
entity.CreatedBy = identityName;
entity.CreatedDate = now;
}
else
{
base.Entry(entity).Property(x => x.CreatedBy).IsModified = false;
base.Entry(entity).Property(x => x.CreatedDate).IsModified = false;
}
entity.UpdatedBy = identityName;
entity.UpdatedDate = now;
}
}
return base.SaveChanges();
}
Related
I have this repository implementation:
using mediere_API.DataLayer.Entities;
using mediere_API.DataLayer.Repository.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace mediere_API.DataLayer.Repository.Implementations
{
public class RepositoryBase<T> : IDisposable, IRepositoryBase<T> where T : BaseEntity, new()
{
private readonly DbContext _db;
private readonly DbSet<T> _dbSet;
protected RepositoryBase(EfDbContext context)
{
_db = context;
_dbSet = context.Set<T>();
}
public async Task<IList<T>> GetAll(bool asNoTracking = false, bool includeDeleted = false)
{
var query = includeDeleted
? _dbSet
: _dbSet.Where(entity => entity.DeletedAt == null);
return asNoTracking
? await query.AsNoTracking().ToListAsync()
: await query.ToListAsync();
}
public async Task<T> GetById(int id, bool asNoTracking = false, bool includeDeleted = false)
{
return includeDeleted
? await GetRecords(asNoTracking).FirstOrDefaultAsync(e => e.Id == id)
: await GetRecords(asNoTracking).FirstOrDefaultAsync(e => e.Id == id && e.DeletedAt == null);
}
public virtual void Insert(T record)
{
if (_db.Entry(record).State == EntityState.Detached)
{
_db.Attach(record);
_db.Entry(record).State = EntityState.Added;
}
}
public virtual void Update(T record)
{
if (_db.Entry(record).State == EntityState.Detached)
_db.Attach(record);
_db.Entry(record).State = EntityState.Modified;
}
public void Delete(T record)
{
if (record != null)
{
record.DeletedAt = DateTime.UtcNow;
Update(record);
}
}
public void Dispose()
{
_db?.Dispose();
}
protected IQueryable<T> GetRecords(bool asNoTracking = false, bool includeDeleted = false)
{
var result = includeDeleted ?
_dbSet
:
_dbSet.Where(e => e.DeletedAt == null);
return asNoTracking ? result.AsNoTracking() : result;
}
}
}
Let's say I have this Entity
using mediere_API.Dtos;
using System.ComponentModel.DataAnnotations;
namespace mediere_API.DataLayer.Entities
{
public class Localitate : BaseEntity
{
[Required]
public string Nume { get; set; }
[Required]
public int JudetId { get; set; }
public virtual Judet judet { get; set; }
public Localitate() { }
}
}
I observed that I don't get the foreign keys in my objects.
In order to get all the objects with their FKs, I decided to add this this method in the repository for that collection:
public async Task<List<Localitate>> GetLocalitatiBasicAsync()
{
return await base.GetRecords(true).Select(l => new Localitate()
{
Id = l.Id,
Nume = l.Nume,
judet = l.judet
}).ToListAsync();
}
Is this the best solution? And what would be the difference between using this select with judet too vs using the .include() method to include judet?
Thanks.
I have the below entity which has a navigation property.
public class Member
{
public Member()
{
Id = 0;
IsActive = true;
BillDetails = new List<BillDetail>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsActive { get; set; }
public int? OrgId { get; set; }
public IList<BillDetail> BillDetails { get; set; }
}
And I have a generic repository which is:
public abstract class BaseRepository<TEntity, TId> : IRepository<TEntity, TId> where TEntity : class
{
private readonly IApplicationDbContext _db;
public BaseRepository(IApplicationDbContext db)
{
_db = db;
}
public virtual void Add(TEntity entity)
{
_db.Set<TEntity>().Add(entity);
_db.Entry(entity).State = EntityState.Added;
}
public virtual bool Update(TEntity entity)
{
_db.Set<TEntity>().Attach(entity);
_db.Entry(entity).State = EntityState.Modified;
return true;
}
public virtual bool Delete(TEntity entity)
{
_db.Set<TEntity>().Remove(entity);
_db.Entry(entity).State = EntityState.Deleted;
return true;
}
public virtual IQueryable<TEntity> Query()
{
return _db.Set<TEntity>().AsQueryable();
}
public virtual EntityEntry<TEntity> Entry(TEntity entity)
{
return _db.Entry(entity);
}
public void Attach(TEntity entity)
{
if (entity == null || _db.Entry(entity).State != EntityState.Detached)
return;
_db.Set<TEntity>().Attach(entity);
}
public void Detach(TEntity entity)
{
if (entity == null)
return;
_db.Entry(entity).State = EntityState.Detached;
}
public abstract Task<TEntity> FindAsync(TId id);
}
From my service class I want to get collection of objects of below type
public class MemberBill
{
public int MemberId {get; set;}
public decimal BillAmount {get; set;}
}
In my service class I have the following query. Now How can I get the list of objects (MemberBill) from the below query?
var query = (_memberRepository.Query().Where(x => x.IsActive == true && x.OrgId == orgId)
.Include(x => x.BillDetails.Where(y => y.IsActive == true))).AsNoTracking().AsQueryable();
List<IList<BillDetail>> memberBills = query.Select(x => x.BillDetails).ToList();
memberBills.Select(x => new MemberBill
{
MemberId = x.
}).ToList();
Please help me to find the solution.
try this
var query = _memberRepository.Query()
.Where(x => x.IsActive == true && x.OrgId == orgId)
.Include(x => x.BillDetails.Where(y => y.IsActive == true))
.AsNoTracking()
.ToList();
var memberBills=new List<MemberBill>();
foreach(var m in query)
{
memberBills.Add( new MemberBill
{
MemberId= m.Id,
BillAmount=m.BillDetails.Sum(i=> i.Amount)
});
}
Leaving here performant solution. Current accepted answer uses a lot of memory and server resources.
var query =
from m in _memberRepository.Query()
where m => m.IsActive == true && m.OrgId == orgId
from bd in m.BillDetails.Where(bd => bd.IsActive == true)
group bd by new { m.Id } into g
select new MemberBill
{
MemberId = g.Key.Id,
BillAmount = g.Sum(x => x.Amount
};
var memberBills = query.ToList();
I have a Generic Repository like below which handles my CRUD, for a single entity its easy to use, problem starts when i try to join my POCOs.
Lets say I have these POCO, they are mapped using fluent api (many to many and One to many relation) :
public class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentId { get; set; }
public string StudentName { get; set; }
//FKs
public virtual Standard Standard { get; set; }
public int StdandardRefId { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class Standard
{
public Standard()
{
Students = new List<Student>();
}
public int StandardId { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Many-To-Many
modelBuilder.Entity<Student>()
.HasMany<Course>(s => s.Courses)
.WithMany(c => c.Students)
.Map(cs =>
{
cs.MapLeftKey("StudentRefId");
cs.MapRightKey("CourseRefId");
cs.ToTable("StudentCourse");
});
//One-To-Many
modelBuilder.Entity<Student>()
.HasRequired<Standard>(s => s.Standard)
.WithMany(s => s.Students)
.HasForeignKey(s => s.StandardId);
}
Generic Repository:
public class Repository<T> : IRepository<T>
where T : class, IDisposable
{
internal MyDbContext context;
internal DbSet<T> dbSet;
public Repository()
{
context = new MyDbContext();
this.dbSet = context.Set<T>();
}
public bool Add(T entity)
{
var query = dbSet.Add(entity);
if (query != null)
return true;
return false;
}
public bool Update(T entity)
{
dbSet.Attach(entity);
var query = context.Entry(entity).State = EntityState.Modified;
if (query == EntityState.Modified)
return true;
return false;
}
public bool Delete(T entity)
{
var query = dbSet.Remove(entity);
if (query != null)
return true;
return false;
}
public bool Delete(Guid id)
{
var query = dbSet.Remove(dbSet.Find(id));
if (query != null)
return true;
return false;
}
public T GetById(Guid id)
{
var query = dbSet.Find(id);
if (query != null)
return query;
else
return null;
}
public ICollection<T> GetAll()
{
return dbSet.AsEnumerable<T>().ToList();
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
if (context != null)
{
context.Dispose();
context = null;
}
}
}
Now if i want to join Standard to Many-to-Many Table how may i be able to do this?
So based on your edit, I'm assuming you would like to join Students and Standards.
The first thing you have to do is change the repository so that it doesn't instantiate the context. You should pass that in as a parameter and store a reference to it:
public Repository(MyDbContext myCtx)
{
context = myCtx;
this.dbSet = context.Set<T>();
}
The second thing you have to do is change your repository to change the GetAll() method to return IQueryable<T> instead of ICollection<T>.
Then change the implementation of GetAll():
return dbSet;
This way you only get back a query and not the evaluated list of all the entities. And then you can do the join with the GetAll() method of the repositories just like you would do it with the db sets:
using (MyDbContext ctx = new MyDbContext())
{
var studentRep = new Repository<Student>(ctx);
var standardRep = new Repository<Standard>(ctx);
var studentToStandard = studentRep.GetAll().Join(standardRep.GetAll(),
student => student.StandardRefId,
standard => standard.StandardId,
(stud, stand) => new { Student=stud, Standard=stand }).ToList();
}
With this you get an IQueryable<T> in studentToStandard, which will run in the database once you call ToList() on it. Note that you have to pass in the same context to both of the repositories in order for this to work.
I recommend that you check out the Unit Of Work design pattern as well. It helps a lot when dealing with multiple repositories.
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
This is a more structured and better maintainable way of handling transactions when it comes to multiple entity sets, and promotes better separation of concerns.
Hope I understood your problem correctly and this helps.
I took this query where I was using Entity Framework
var result = (from metattachType in _dbContext.METATTACH_TYPE
join lineItemMetattachType in _dbContext.LINE_ITEM_METATTACH_TYPE on metattachType.ID equals lineItemMetattachType.METATTACH_TYPE_ID
where (lineItemMetattachType.LINE_ITEM_ID == lineItemId && lineItemMetattachType.IS_DELETED == false
&& metattachType.IS_DELETED == false)
select new MetattachTypeDto()
{
Id = metattachType.ID,
Name = metattachType.NAME
}).ToList();
and changed it into this where I'm using the repository pattern
Linq
return await _attachmentTypeRepository.GetAll().Where(x => !x.IsDeleted)
.Join(_lineItemAttachmentTypeRepository.GetAll().Where(x => x.LineItemId == lineItemId && !x.IsDeleted),
attachmentType => attachmentType.Id,
lineItemAttachmentType => lineItemAttachmentType.MetattachTypeId,
(attachmentType, lineItemAttachmentType) => new AttachmentTypeDto
{
Id = attachmentType.Id,
Name = attachmentType.Name
}).ToListAsync().ConfigureAwait(false);
Linq-to-sql
return (from attachmentType in _attachmentTypeRepository.GetAll()
join lineItemAttachmentType in _lineItemAttachmentTypeRepository.GetAll() on attachmentType.Id equals lineItemAttachmentType.MetattachTypeId
where (lineItemAttachmentType.LineItemId == lineItemId && !lineItemAttachmentType.IsDeleted && !attachmentType.IsDeleted)
select new AttachmentTypeDto()
{
Id = attachmentType.Id,
Name = attachmentType.Name
}).ToList();
Also, please know that Linq-to-Sql is 14x faster than Linq...
I have create a Generic Repository (Using EF 6.1.1), which I am using in several projects and it works very well.
I have implemented a 'soft' delete feature, where we mark data as deleted, but do not actually remove it from the database.
I can then filter all my queries as all entities inherit from a base entity which has the IsDeleted property. This is all works nicely, but it obviously does not filter out any of the 'soft deleted' child entities.
I am unsure how to go about doing this in a generic way, as I dont want to have to over code a solution into every respoitory, that really defeats the reason for having a generic repo.
this is an example of my current Generic Repo
public sealed class MyRepository<T> : IRepository<T> where T : BaseEntity
{
public String CurrentUser { get; set; }
private readonly MyObjectContext context;
private readonly Configuration configuration = ConfigurationManager.GetConfiguration();
private IDbSet<T> entities;
private IDbSet<T> Entities
{
get { return entities ?? (entities = context.Set<T>()); }
}
public MyRepository(MyObjectContext context, String userName = null)
{
this.context = context;
var providerManager = new DataProviderManager(configuration);
var dataProvider = (IDataProvider)providerManager.LoadDataProvider();
dataProvider.InitDatabase();
CurrentUser = userName;
}
public void Dispose()
{
//do nothing at the moment
}
public T GetById(Guid id)
{
return Entities.FirstOrDefault(x => x.Id == id && !x.IsDeleted);
}
public IQueryable<T> GetAll()
{
return Entities.Where(x => !x.IsDeleted);
}
public IQueryable<T> Query(Expression<Func<T, bool>> filter)
{
return Entities.Where(filter).Where(x => !x.IsDeleted);
}
public void Delete(T entity)
{
if (configuration.HardDelete)
{
HardDelete(entity);
}
else
{
SoftDelete(entity);
}
}
private void HardDelete(T entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
Entities.Attach(entity);
Entities.Remove(entity);
}
catch (DbEntityValidationException ex)
{
var msg = string.Empty;
foreach (var validationErrors in ex.EntityValidationErrors)
foreach (var validationError in validationErrors.ValidationErrors)
msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
var fail = new Exception(msg, ex);
throw fail;
}
}
private void SoftDelete(T entity)
{
entity.IsDeleted = true;
Update(entity);
}
}
Any help on this would be great.
Thanks
Someone has built a global filter, you can try it and install it from nuget EntityFramework.Filters.
https://github.com/jbogard/EntityFramework.Filters
Here is an example how to use it.
public abstract class BaseEntity
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
public class Foo : BaseEntity
{
public string Name { get; set; }
public ICollection<Bar> Bars { get; set; }
}
public class Bar : BaseEntity
{
public string Name { get; set; }
public int FooId { get; set; }
public Foo Foo { get; set; }
}
public class AppContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Registers and configures it first.
DbInterception.Add(new FilterInterceptor());
var softDeleteFilter = FilterConvention.Create<BaseEntity>("SoftDelete",
e => e.IsDeleted == false); // don't change it into e => !e.IsDeleted
modelBuilder.Conventions.Add(softDeleteFilter);
}
}
Then you can enable it in your repository constructor or somewhere after db context instance is created because the filters are disabled by default.
using (var db = new AppContext())
{
db.EnableFilter("SoftDelete");
var foos = db.Foos.Include(f => f.Bars).ToArray(); // works on Include
}
using (var db = new AppContext())
{
db.EnableFilter("SoftDelete");
var foos = db.Foos.ToArray();
foreach (var foo in foos)
{
var bars = foo.Bars; // works on lazy loading
}
}
using (var db = new AppContext())
{
db.EnableFilter("SoftDelete");
var foos = db.Foos.ToArray();
foreach (var foo in foos)
{
db.Entry(foo).Collection(f => f.Bars).Load(); // works on manual loading
}
}
This filter is not needed anymore.
public IQueryable<T> Query(Expression<Func<T, bool>> filter)
{
return Entities.Where(filter);//.Where(x => !x.IsDeleted);
}
As long as you have enabled it.
public MyRepository(MyObjectContext context, String userName = null)
{
this.context = context;
if (!configuration.HardDelete)
{
this.context.EnableFilter("SoftDelete");
}
}
I was having the same problem but my method to solve was little different i used a genric base interface IGenricInterface with IsDeleted as property
public int DeletebyId(string Id)
{
var Ent = (IGenricInterface)_sitecontext.Set<TEntity>().Find(Id);
Ent.IsDeleted = 1;
}
and for hardDelete
_sitecontext.Set<TEntity>().Remove(Ent);
This is on ID but offcourse you can do it on EnTity as well
I am using EF 5.0 Code First in a C# project. I have a base clase which the majority of my domain models derive from.
public abstract class AuditableModelBase
{
public Int32 CreatedByUserId { get; set; }
public DateTime CreatedDate { get; set; }
public virtual UserProfile CreatedByUserProfile { get; set; }
public Int32 UpdatedByUserId { get; set; }
public DateTime UpdatedDate { get; set; }
public virtual UserProfile UpdatedByUserProfile { get; set; }
public AuditableModelBase()
{
CreatedByUserId = 1;
CreatedDate = DateTime.UtcNow;
UpdatedByUserId = 1;
UpdatedDate = DateTime.UtcNow;
}
}
However, for every single entity I have to define the specific configurations to wire these relationships together.
// Relationships
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
I'm looking for a way to just declare one Configuration similar to the one directly above for the abstract base class instead of having to create an individual configuration file for each of my entities. I'd love to just have one file named "AuditableModelBaseMap.cs" which will have my configuration instead of "Entity1Map.cs", "Entity2Map.cs", "Entity3Map.cs", etc. especially since all of those files have the exact same code inside.
Any advice?
Thanks.
Try it like below but I didnt test it .However if I were you, I wouldnt design Audit tables this way
class AuditableModelBaseMap : EntityTypeConfiguration<AuditableModelBase>
{
public AuditableModelBaseMap ()
{
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
}
}
THIS IS MY WAY OF Doing AUDITING
public interface IEntity
{
int Id { get; set; }
}
public interface IAuditable : IEntity
{
string UpdatedBy { get; set; }
string CreatedBy { get; set; }
DateTime CreatedDate { get; set; }
DateTime UpdateDate { get; set; }
}
Now any entity which is auditable will implement this class your context will look the following
public class MYContext : DbContext, ILicensingContext
{
private readonly IAuditLogBuilder _auditLogBuilder;
public LicensingContext()
: this(new AuditLogBuilder())
{
}
private IDbSet<Device> Devices { get; set; }
private IDbSet<AuditLog> AuditLogs { get; set; }
public MyContext(IAuditLogBuilder auditLogBuilder)
{
_auditLogBuilder = auditLogBuilder;
}
/// <summary>
/// 1. Constructs the AuditLog objects from the context
/// 2. Calls SaveChanges to save the actual object modified
/// 3. It updates the Log objects constructed in step 1 to populate the IDs returned from the Db
/// 4. Saves the AuditLogs
/// </summary>
/// <returns></returns>
public override int SaveChanges()
{
var entries = ChangeTracker.Entries<IAuditable>().ToList();
_auditLogBuilder.UpdateAuditables(entries);
IEnumerable<AuditLog> auditLogEntities = _auditLogBuilder.ConstructAuditLogs(entries).ToList();
int countOfAffectedRecords = base.SaveChanges();
_auditLogBuilder.UpdateAuditLogs(auditLogEntities);
foreach (AuditLog auditLogEntity in auditLogEntities)
{
GetDbSet<AuditLog>().Add(auditLogEntity);
}
base.SaveChanges();
return countOfAffectedRecords;
}
public IDbSet<TEntity> GetDbSet<TEntity>() where TEntity : class
{
return Set<TEntity>();
}
}
public class AuditLogBuilder : IAuditLogBuilder
{
private string _username;
private string Username
{
get
{
if (HttpContext.Current != null && HttpContext.Current.User != null)
{
_username = HttpContext.Current.User.Identity.Name;
}
if (String.IsNullOrWhiteSpace(_username))
{
_username = "Service Consumer";
}
return _username;
}
}
public IEnumerable<AuditLog> ConstructAuditLogs(IEnumerable<DbEntityEntry<IAuditable>> auditableEntities)
{
var audits = new List<AuditLog>();
if (auditableEntities != null)
{
audits.AddRange(auditableEntities
.Where(
e =>
e.State == EntityState.Modified || e.State == EntityState.Added ||
e.State == EntityState.Deleted)
.SelectMany(GetAuditLogs));
}
return audits;
}
public void UpdateAuditLogs(IEnumerable<AuditLog> auditLogEntities)
{
foreach (AuditLog auditLog in auditLogEntities)
{
auditLog.RecordId = auditLog.Entity.Id;
auditLog.UpdatedBy = auditLog.Entity.UpdatedBy;
if (String.Equals(auditLog.PropertyName, "id", StringComparison.CurrentCultureIgnoreCase))
{
auditLog.NewValue = auditLog.Entity.Id.ToString(CultureInfo.CurrentCulture);
}
}
}
public void UpdateAuditables(IEnumerable<DbEntityEntry<IAuditable>> entries)
{
if (entries != null)
{
foreach (var entry in entries)
{
entry.Entity.UpdateDate = DateTime.UtcNow;
entry.Entity.UpdatedBy = Username;
if (entry.Entity.Id == 0)
{
entry.Entity.CreatedDate = DateTime.UtcNow;
entry.Entity.CreatedBy = Username;
}
}
}
}
private static IEnumerable<AuditLog> GetAuditLogs(DbEntityEntry<IAuditable> entry)
{
var audits = new List<AuditLog>();
string entityName = ObjectContext.GetObjectType(entry.Entity.GetType()).Name;
switch (entry.State)
{
case EntityState.Added:
audits.AddRange(entry.CurrentValues.PropertyNames.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue =
entry.CurrentValues[
propertyName] != null
? entry.CurrentValues[
propertyName].ToString()
: String.Empty,
PreviousValue = String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Create.ToString()
}));
break;
case EntityState.Deleted:
audits.AddRange(entry.OriginalValues.PropertyNames.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue = String.Empty,
PreviousValue =
entry.OriginalValues[
propertyName] != null
? entry.OriginalValues[
propertyName].ToString
()
: String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Delete.ToString()
}));
break;
case EntityState.Modified:
audits.AddRange(entry.OriginalValues.PropertyNames.
Where(
propertyName =>
!Equals(entry.OriginalValues[propertyName],
entry.CurrentValues[propertyName]))
.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue =
entry.CurrentValues[propertyName] != null
? entry.CurrentValues[propertyName].ToString()
: String.Empty,
PreviousValue =
entry.OriginalValues[propertyName] != null
? entry.OriginalValues[propertyName].ToString()
: String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Update.ToString()
}));
break;
}
return audits;
}
}
Have you tried this?
public class AuditableModelBaseMap : EntityTypeConfiguration<AuditableModelBase>
{
public AuditableModelBaseMap()
{
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
}
}