Reputation: 92
We use an UnitOfWork approach for an application based on EF 6.2.0 in C# with a SQL Server 2017 Express (14.0.1000) database backend.
Our OwnContext.cs
inherited from DbContext
should fill the fields Id
, ChangedDate
and ChangedUser
automatically, which is part of every entity in our db model:
public partial class OwnContext : DbContext {
public override int SaveChanges()
{
bool hasChanges = false;
this.ChangeTracker.DetectChanges();
#region Fill Created, Changed
foreach (var item in this.ChangeTracker.Entries()
.Where(x => (x.State == EntityState.Added || x.State == EntityState.Modified || x.State == EntityState.Deleted)))
{
if (item.State == EntityState.Deleted)
{
hasChanges = true;
}
else
{
// Id
PropertyInfo pI = item.Entity.GetType().GetProperty("Id");
if (pI != null)
{
Guid id = (Guid)pI.GetValue(item.Entity, null);
if ((Guid)id == Guid.Empty)
{
id = Guid.NewGuid();
pI.SetValue(item.Entity, id);
}
}
DateTime now = DateTime.Now;
// Timestamp & User
pI = item.Entity.GetType().GetProperty("CreatedDate");
if (pI != null)
{
var date = pI.GetValue(item.Entity, null);
// Only if empty on new records
if (date == null || date.Equals(DateTime.MinValue))
{
pI.SetValue(item.Entity, now);
pI = item.Entity.GetType().GetProperty("CreatedUser");
if (pI != null)
{
pI.SetValue(item.Entity, Environment.UserName);
}
}
pI = item.Entity.GetType().GetProperty("ChangedDate");
if (pI != null)
{
pI.SetValue(item.Entity, now);
}
pI = item.Entity.GetType().GetProperty("ChangedUser");
if (pI != null)
{
pI.SetValue(item.Entity, Environment.UserName);
}
}
hasChanges = true;
}
}
#endregion
int countChanges = 0;
if (hasChanges)
{
// Call SaveChanges Method from Context;
try
{
countChanges = base.SaveChanges();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
return countChanges;
}
}
For new records that works perfectly, but not for modified ones.
The new datetime value in the ChangedDate
field appears only in the context but does not write to database.
If I step through the code, I see item.State
is EntityState.Modified
and the countChanges
return value is 1 and the value in item.Entity
everything seems correct.
If query the object the new value is visible but the value in database does not change, so if I dispose my context the old value will reappear.
Really confusing is the fact that the field value I change in the ui to test it is changed in the db, but not the field ChangedDate
.
Why?
Here is a example of one entity class and the configuration, created by Devarts Entity Devloper:
public partial class Abo {
public Abo()
{
OnCreated();
}
#region Properties
public virtual global::System.Guid Id
{
get;
set;
}
// ...
public virtual global::System.Nullable<System.DateTime> ChangedDate
{
get;
set;
}
public virtual global::System.Nullable<System.DateTime> CreatedDate
{
get;
set;
}
public virtual string CreatedUser
{
get;
set;
}
public virtual string ChangedUser
{
get;
set;
}
}
public partial class AboConfiguration : EntityTypeConfiguration<Abo>
{
public AboConfiguration()
{
this
.HasKey(p => p.Id)
.ToTable("Abos", "dbo");
// Properties:
this
.Property(p => p.Id)
.IsRequired()
.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)
.HasColumnType("uniqueidentifier");
// ...
this
.Property(p => p.CreatedDate)
.HasColumnType("datetime");
this
.Property(p => p.ChangedDate)
.HasColumnType("datetime");
this
.Property(p => p.CreatedUser)
.HasMaxLength(32)
.HasColumnType("varchar");
this
.Property(p => p.ChangedUser)
.HasMaxLength(32)
.HasColumnType("varchar");
OnCreated();
}
partial void OnCreated();
}
The SQL DDL of the corresponding table:
CREATE TABLE [dbo].[Abos](
[Id] [uniqueidentifier] NOT NULL,
/* .. */
[ChangedDate] [datetime] NULL,
[CreatedDate] [datetime] NULL,
[ChangesUser] [varchar](32) NULL,
[CreatedUser] [varchar](32) NULL
CONSTRAINT [PK_dbo.Abos] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Upvotes: 1
Views: 561
Reputation: 668
At this point, the property changes on the entity object received by the change tracker are not DTO relevant. You need to apply the changes through the DbEntityEntry.CurrentValues
Property instead.
item.CurrentValues["ChangedDate"] = DateTime.Now;
Upvotes: 1