Search code examples
c#-4.0dynamic-datadetailsviewasp.net-dynamic-data

Retrieving values of ReadOnly fields from DynamicData DetailsView in Edit Mode on Updating using LinqDataSource


I have several tables in my database that have read-only fields that get set on Inserting and Updating, namely: AddDate (DateTime), AddUserName (string), LastModDate (DateTime), LastModUserName (string).

All of the tables that have these values have been set to inherit from the following interface:

public interface IUserTrackTable
{
    string AddUserName { get; set; }
    DateTime AddDate { get; set; }
    string LastModUserName { get; set; }
    DateTime LastModDate { get; set; }
}

As such, I have the following method on the Edit.aspx page:

protected void DetailsDataSource_Updating(object sender, LinqDataSourceUpdateEventArgs e)
{
    IUserTrackTable newObject = e.NewObject as IUserTrackTable;
    if (newObject != null)
    {
        newObject.LastModUserName = User.Identity.Name;
        newObject.LastModDate = DateTime.Now;
    }
}

However, by the time it hits this method, the e.OriginalObject has already lost the values for all four fields, so a ChangeConflictException gets thrown during the actual Update. I have tried adding the four column names to the DetailsView1.DataKeyNames array in the Init event handler:

protected void Page_Init(object sender, EventArgs e)
{
    // other things happen before this
    var readOnlyColumns = table.Columns.Where(c => c.Attributes.SingleOrDefaultOfType<ReadOnlyAttribute>(ReadOnlyAttribute.Default).IsReadOnly).Select(c => c.Name);
    DetailsView1.DataKeyNames = DetailsView1.DataKeyNames.Union<string>(readOnlyColumns).ToArray<string>();

    DetailsView1.RowsGenerator = new CustomFieldGenerator(table, PageTemplates.Edit, false);
    // other things happen after this
}

I've tried making that code only happen on PostBack, and still nothing. I'm at a lose for how to get the values for all of the columns to make the round-trip.

The only thing the CustomFieldGenerator is handling the ReadOnlyAttribute, following the details on C# Bits.

UPDATE: After further investigation, the values make the round trip to the DetailsView_ItemUpdating event. All of the values are present in the e.OldValues dictionary. However, they are lost by the time it gets to the LinqDataSource_Updating event.

Obviously, there are the "solutions" of making those columns not participate in Concurrency Checks or other ways that involve hard-coding, but the ideal solution would dynamically add the appropriate information where needed so that this stays as a Dynamic solution.


Solution

  • i Drovani, I assume you want data auditing (see Steve Sheldon's A Method to Handle Audit Fields in LINQ to SQL), I would do this in the model in EF4 you can do it like this:

    partial void OnContextCreated()
    {
        // Register the handler for the SavingChanges event. 
        this.SavingChanges += new EventHandler(context_SavingChanges);
    }
    
    private static void context_SavingChanges(object sender, EventArgs e)
    {
        // handle auditing
        AuditingHelperUtility.ProcessAuditFields(objects.GetObjectStateEntries(EntityState.Added));
        AuditingHelperUtility.ProcessAuditFields(objects.GetObjectStateEntries(EntityState.Modified), InsertMode: false);
    }
    
    internal static class AuditingHelperUtility
    {
        internal static void ProcessAuditFields(IEnumerable<Object> list, bool InsertMode = true)
        {
            foreach (var item in list)
            {
                IAuditable entity = item as IAuditable;
                if (entity != null)
                {
                    if (InsertMode)
                    {
                        entity.InsertedBy = GetUserId();
                        entity.InsertedOn = DateTime.Now;
                    }
    
                    entity.UpdatedBy = GetUserId();
                    entity.UpdatedOn = DateTime.Now;
                }
            }
        }
    }
    

    Sadly this is not possible with EF v1