2

I'm attempting to create an Audit Log for my MVC, Entity Framework website project. I've been able to subscribe to SaveChanges() in my DBContext (and save to my database through another DBContext but same database).

My two questions in the end are:

  • What does if (!entry.IsRelationship) do exactly? I have a ViewModel that calculates this as True when Saving and another as False. I would expect this to move into the rest of my method to save in the Audit Log.
  • How can I get the full Namespace of my Object being modified? I was using this: entry.Entity.ToString() but doesn't seem to work when Saving/Editing from a View Model (details below)

Here is a basic setup that I have thus far (Album object/controller works, but AlbumView doesn't):

Ablum class:

public class Album : BaseObject //BaseObject has a few properties, one is Oid (Guid)
{
    public string Name { get; set; }

    [Column(TypeName = "varchar(MAX)")]
    [DataType(DataType.MultilineText)]
    public string Description { get; set; }

    [Display(Name="Genres")]
    public virtual ICollection<AlbumsGenres> AlbumGenres { get; set; }

    [Display(Name="Artists")]
    public virtual ICollection<AlbumsArtists> AlbumArtists { get; set; }
}

AblumView class:

public class AlbumView
{
    [ScaffoldColumn(false)]
    public Guid Oid { get; set; }

    [Required]
    public string Name { get; set; }

    [Column(TypeName = "varchar(MAX)")]
    [DataType(DataType.MultilineText)]
    public string Description { get; set; }

    [Display(Name = "Genres")]
    public virtual List<AlbumsGenres> AlbumGenres { get; set; }

    [Display(Name = "Artists")]
    public virtual List<AlbumsArtists> AlbumArtists { get; set; }

}

AlbumsController (Audit works with something like this):

public ActionResult Edit(Album album)
{
    if (ModelState.IsValid)
    {
        db.Entry(album).State = EntityState.Modified;
        db.SaveChanges(); //This is where SaveChanges() takes over (see below)
        return RedirectToAction("Index");
    }
    return View(album);
}

AlbumsViewController:

public ActionResult Edit(Guid id, AlbumView albumViewModel)
{
    //Omitting setup...

    //Album gets updated
    Album album = db.Albums.Find(id);
    album.Name = albumViewModel.Name;
    album.Description = albumViewModel.Description;

    //Other Objects are also updated, just an example:
    albumArtists = new AlbumsArtists();
    albumArtists.Oid = Guid.NewGuid();
    albumArtists.Album = db.Albums.Find(id);
    albumArtists.Artist = db.Artists.Find(item.Artist.Oid);

    //In the end it calls:
    db.SaveChanges();

    //Omitting other stuff...
}

On db.SaveChanges() within my DbContext:

public class ApplicationDBContext : DbContext
{
    public ApplicationDBContext() : base("name=DefaultConnection") { }

    public System.Data.Entity.DbSet<ContentPub.Models.Music.Album> Albums { get; set; }
    //Other DBSet objects...

    public DbSet Set(string name)
    {
        return base.Set(Type.GetType(name));
    }

    public override int SaveChanges()
    {
        ApplicationLogDBContext logDb = new ApplicationLogDBContext();
        ChangeTracker.DetectChanges();

        ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;

        List<ObjectStateEntry> objectStateEntryList =
            ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added
                                                       | EntityState.Modified
                                                       | EntityState.Deleted)
            .ToList();

        foreach (ObjectStateEntry entry in objectStateEntryList)
        {
            Guid oid = Guid.Empty;

            try
            {
                if (!entry.IsRelationship) //I don't understand this (first of my two questions)
                {
                    switch (entry.State)
                    {
                        //Removed other cases

                        case EntityState.Modified:
                        {
                            oid = (Guid)entry.EntityKey.EntityKeyValues[0].Value;

                            //This is the area that I am having issues (second of the two questions)

                            //Below will work when I call db.SaveChanges() from the AlbumsController, 
                            //'entry.Entity.ToString()' will get 'x.Models.Music.Albums' and begin a query

                                var query = this.Set(entry.Entity.ToString()).AsNoTracking().Where("Oid == @0", oid);

                            //The issue with the above is when I have a ViewModel, returns something like
                            // = System.Data.Entity.DynamicProxies.Album_AF81C390156ACC8283ECEC668AFB22C4AD621EF70F8F64641D56852D19755BF3

                            //If the proper Namespace is returned, the next line works and Audit continues
                            var query = this.Set(entry.EntitySet.ElementType.ToString()).AsNoTracking().Where("Oid == @0", oid);

                            //Does a bunch of AuditLog stuff if the above issue doesn't fail
                            break;
                        }
                    }
                }

            }
            catch (Exception ex)
            {
                throw new Exception("Log Error (" + entry.Entity.ToString() + ") - " + ex.ToString());
            }
        }
        return base.SaveChanges();
    }
}

entry.Entity.ToString() will return something like:

System.Data.Entity.DynamicProxies.Album_AF81C390156ACC8283ECEC668AFB22C4AD621EF70F8F64641D56852D19755BF3

In the AlbumView I am updating Album, and a bunch of other Objects. Not sure why it isn't returning x.Models.Music.Albums, is there a work-around, can someone explain or point me to other resources that I haven't found yet?

Derek
  • 653
  • 7
  • 20
  • Try with [Audit.EntityFramework](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.EntityFramework/README.md) library. It could help. – thepirat000 Oct 12 '17 at 04:50

1 Answers1

1

While it isn't the most efficient solution, it still is a solution for now.

I was able to do the following inside my db.SaveChanges() method:

//When AlbumView .BaseType was able to return x.Models.Music.Album
string strNamespace = entry.Entity.GetType().BaseType.ToString(); 

//Needed this if I was updating just an Object (ie: Album),
//would be nice to make something more concret
if (strNamespace == "x.Models.Core.BaseObject")
    strNamespace = entry.Entity.ToString();

//Continuing code
var query = this.Set(strNamespace).AsNoTracking().Where("Oid == @0", oid);

Found the answer here from another Question that I had not found before posting this question

Derek
  • 653
  • 7
  • 20