user3519976
user3519976

Reputation:

Best way to override SaveChanges()

We have worked on a project for 1 month and there are 6 entities without any relationship with other entities. They are all simple entities.

We have created 6 different classes for operations on each entity. SaveOrUpdateEntity() methods of classes are almost same as you think. It is something like that:

public static ErrorType SaveOrUpdateEntity(Entity entity, int userID)
{
    try
    {
        using (DataEntities ctx = new DataEntities())
        {
            if (entity != null)
            {
                if (entity.entityID == 0)
                {
                    entity.CreateDate = DateTime.Now;
                    entity.CreatedBy = userID;

                    ctx.Entry(entity).State = EntityState.Added;
                }
                else
                {
                    entity.ModifyDate = DateTime.Now;
                    entity.ModifiedBy = userID;

                    ctx.Entry(entity).State = EntityState.Modified;
                }
            }

            ctx.SaveChanges();
        }

        return ErrorType.NoError;
    }
    catch (Exception ex)
    {
        return ErrorType.SaveError;
    }
}

It would be very helpful, if SaveOrUpdateEntity() method is shorter and more generic by overriding SaveChanges() method. According to other questions, articles, and posts about overriding SaveChanges() method, implementing interface that stores state of entities is a good solution but I am also wondering other solutions.

Because I am newbie, all answers would be very appreciated.

Thank you.

Upvotes: 17

Views: 24697

Answers (3)

ypelissier
ypelissier

Reputation: 55

This code will be better with :

    var added = this.ChangeTracker.Entries()
            .Where(t => t.Entity is ITrack && t.State == EntityState.Added)
            .Select(t => t.Entity)
            .ToArray();

The same for modified :

    var modified = this.ChangeTracker.Entries()
            .Where(t => t.Entity is ITrack && t.State == EntityState.Modified)
            .Select(t => t.Entity)
            .ToArray();

And remove if conditions in foreach loop...

Upvotes: 6

Stuart Aitken
Stuart Aitken

Reputation: 1012

To 'override' SaveChanges without needing to create all that extra code, I wrapped it in its own extension method. i.e.

public static int SaveChangesWrapped(this MyDbContext db)
{
    // do other things

    return db.SaveChanges();
}

or async...

public static Task<int> SaveChangesAsyncWrapped(this MyDbContext db)
{
    // do other things

    return await db.SaveChangesAsync();
}

You can tweak it however you need, i.e. add other parameters, make it generic, etc. I may be missing something but this was enough for me to 'override' SaveChanges and cut down a whole lot of repeated code.

Then anywhere in your code when you want to save, it's just:

db.SaveChangesWrapped();

//or

await db.SaveChangedAsyncWrapped();

Upvotes: 0

Monah
Monah

Reputation: 6784

you can do the following

1- create an Interface in your application that all the classes that has the following properties will implement this interface: Id, CreatedDate,CreatedBy, ModifiedDate,ModifiedBy

public interface ITrack
{
      int Id{get; set;}
      int CreatedBy{get; set;}
      DateTime CreatedDate{get; set;}
      int? ModifiedBy{get; set;} // int? because at first add, there is no modification
      DateTime? ModifiedBy {get; set;}
}

Best practices Define the CreatedBy and ModifiedBy as string which will be good for performance and maintenance

2- Add a class TrackableEntry which implements the interface ITrack

public abstract class TrackableEntry : ITrack
{
      public int Id{get; set;}
      public int CreatedBy{get; set;}
      public DateTime CreatedDate{get; set;}
      public int? ModifiedBy{get; set;} 
      public DateTime? ModifiedBy {get; set;}
}

3- remove the properties mentioned in the interface from all of your classes and let these classes to implement directly from TrackableEntry

public class A: TrackableEntry
{
    //public int Id{get; set;}
    //public int CreatedBy{get; set;}
    //public DateTime CreatedDate{get; set;}
    //public int? ModifiedBy{get; set;}
    //public DateTime? ModifiedBy {get; set;}
}

4- In your DbContext file override your SaveChanges and add property UserId or UserName if you followed the *Best practices* part

public int UserId{get; set;}

public override int SaveChanges()
{
    this.ChangeTracker.DetectChanges();
    var added = this.ChangeTracker.Entries()
                .Where(t => t.State == EntityState.Added)
                .Select(t => t.Entity)
                .ToArray();

    foreach (var entity in added)
    {
        if (entity is ITrack)
        {
            var track = entity as ITrack;
            track.CreatedDate = DateTime.Now;
            track.CreatedBy = UserId;
        }
    }

    var modified = this.ChangeTracker.Entries()
                .Where(t => t.State == EntityState.Modified)
                .Select(t => t.Entity)
                .ToArray();

    foreach (var entity in modified)
    {
        if (entity is ITrack)
        {
            var track = entity as ITrack;
            track.ModifiedDate = DateTime.Now;
            track.ModifiedBy = UserId;
        }
    }
    return base.SaveChanges();
}

finally in your forms when you want to call SaveChanges method, ensure you set the UserId or UserName value

var entities=new Entities(); // assuming that your DbContext file called Entities
// code for adding or deletion or modification here
entities.As.Add(new A(){...});

// ....

entities.UserId=MyUser;
entities.SaveChanges();

hope this will help you

Upvotes: 34

Related Questions