-1

I have this Complex Type included in my entity class, such as:

public class Logbook
{
  public string CreatedBy { get; set; }
  public DateTimeOffset DateCreated { get; set; }
  public string ModifiedBy { get; set; }
  public DateTimeOffset DateModified { get; set; }
}

Then my main class:

public class Customer
{
  public int Id { get; set; }
  public string Name { get; set; }
  public Logbook Logbook { get; set; }
}

I then set class Logbook as a complex type. My question is, I'm wondering on how to set the values in Logbook as soon as Customer entity is inserted/modified? Say, set the DateCreated to DateTime.UtcNow and set CreatedBy to a user's name.. And so on. Any help would be much appreciated. Thanks!

EDIT:

I'm using Entity Framework for my data access. And this is how I save my Customer

public ActionResult Create(Customer customer)
{
  if ( Model.IsValid() )
  {
    _customerRepository.Insert(customer);
    return View("Index");
  }

  return View(customer);
}
Boy Pasmo
  • 8,021
  • 13
  • 42
  • 67

2 Answers2

2

I do not konw if it possible to do it automatically. In my case I've prepared a special, generic method that handles entity update in log (it's IoC frendly in every case :) ):

public T PrepareLogbookProperty<T>(T model)
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }

    var dynamicModel = (dynamic)model;

    Entity value;

    try
    {
        value = dynamicModel.Logbook as Logbook;
    }
    catch (RuntimeBinderException)
    {
        throw new NoLogbookPropertyFound();
    }

    value = this.PassLog(value); // THAT METHOD PASSES CURRENT USER OR DATE TIME.

    dynamicModel.Logbook= value;

    return model;
}

It has one drawback - it's unfortunately dynamic. In order to handle other cases, I've preprared other overloaded function:

public TEntity PrepareLogbookProperty<TEntity>(TEntity model, Expression<Func<TEntity, Logbook>> entityExpression)
{
    if (model == null)
    {
        throw new ArgumentNullException("model");
    }

    var memberExpression = (MemberExpression)entityExpression.Body;
    var property = (PropertyInfo)memberExpression.Member;

    var value = property.GetValue(model) as Logbook;
    value = this.PassLog(value);

    property.SetValue(model, value);

    return model;
}

Usage:

this.helper.PrepareLogbookProperty(customer, cust => custom.MyOtherLogBook); // expression version

or

this.helper.PrepareLogbookProperty(product); // dynamic version (I assume that product has Logbook property

Method is called manually before every SaveChanges().

Unfortuantely, I couldn't change DB schema and the way that it was designed - so that solution was suitable for me.

Sample pass log:

private Logbook PassLog(Logbook entity)
{
    if (entity == null)
    {
        entity = this.NewLogbook();
    }

    entity.EditedDate = this.dateTimeProvider.Now;
    entity.EditorID = this.services.CurrentUser.ID;

    return entity;
}
1

Are you after something like this?

public class Logbook
{
  public Logbook(string username)
  {
    this.CreatedBy = username;
    this.DateCreated = DateTime.UtcNow; //NB: ideally your database would provide this to ensure if deployed on multiple servers you'd have one date source
    this.ModifiedBy = username;
    this.DateModified = DateTime.UtcNow; //as above
  }
  public void Modify(string username)
  {
    this.ModifiedBy = username;
    this.DateModified = DateTime.UtcNow; //as above
  }
  public string CreatedBy { get; set; }
  public DateTimeOffset DateCreated { get; set; }
  public string ModifiedBy { get; set; }
  public DateTimeOffset DateModified { get; set; }
}

public class Customer
{
  private int id;
  public int Id { 
    get { return this.id; } 
    set { this.id = value; this.OnCreateOrModify(); }
  }

  private string name;
  public string Name { 
    get { return this.name; }
    set { this.name = value; this.OnCreateOrModify(); }
  }
  public Logbook Logbook { get; private set; } //presumably you don't want other classes to amend this
  private void OnCreateOrModify()
  {
    var username = System.Environment.UserName;  //or pass something into your class contructor to provide a username
    if (this.Logbook == null) //create
        this.Logbook = new LogBook(username);
    else //modify
        this.Logbook.Modify(username);
  }
}
JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
  • 1
    Hmm and what about Database First? Every model update from DB I'd to manually edit generated entity classes what is not recommended I suppose. –  Jul 14 '14 at 12:15
  • 1
    Hi. Thanks. But can you explain this line? `set { this.id = value; this,OnCreateOrModify(); }` – Boy Pasmo Jul 14 '14 at 12:19
  • @BoyPasmo typo; the comma should have been a dot. This assigns a value to a property (in this case id) and ensures that the related audit entries are updated accordingly. – JohnLBevan Jul 14 '14 at 12:23
  • @pwas not sure tbh; EF's pretty new to me, so basing my answer on what I did in pre-EF days. – JohnLBevan Jul 14 '14 at 12:25