Question

I am working with Linq To Sql and am trying to keep a history of changes and store these database.

I know there are frameworks for this out there such as DoddleAudit but it feels too buggy and bloaty to me so I'm trying to create my own.

This is what I have this far. I am looking for a way to make the repetitive code more reuseable:

protected void InsertAuditRecordToDatabase(ModifiedMemberInfo[] changes, object entity) 
{
    Type type = entity.GetType();
    PropertyInfo key;
    key = type.GetProperties()
        .Where(o => 
            o.GetCustomAttributes(typeof(ColumnAttribute), true)
                .Any(a=>((ColumnAttribute)a).IsPrimaryKey)).SingleOrDefault();

    AuditRecord audit = new AuditRecord();
    audit.Action = (byte)AuditAction.Update;
    audit.AuditDate = DateTime.Now;
    audit.AssociationTable = null;
    audit.AssociationTableKey = null;
    audit.EntityTable = type.Name;
    audit.EntityTableKey = int.Parse(key.GetValue(entity, null).ToString());

    audit.UserName = HttpContext.Current.User.Identity.Name;
    if (string.IsNullOrEmpty(audit.UserName))
        audit.UserName = "Anonymous";

    foreach (ModifiedMemberInfo mmi in changes)
    {
        AuditRecordField field = new AuditRecordField();
        if (!excludedFieldNamesFromAudit.Any(x => x.Equals(mmi.Member.Name, StringComparison.OrdinalIgnoreCase)))
        {
            field.MemberName = mmi.Member.Name;

            field.OldValue = (mmi.OriginalValue != null ? mmi.OriginalValue.ToString() : string.Empty);
            field.NewValue = (mmi.CurrentValue != null ? mmi.CurrentValue.ToString() : string.Empty);

            if ((field.OldValue != null && !field.OldValue.Equals(field.NewValue)) ||
                (field.OldValue == null && field.NewValue != null))
            {
                // Special handling
                if (field.MemberName.Equals("EUAMemberTypeId"))
                {
                    int oldInt;
                    OrganisationSubType oldValue = null;
                    if(int.TryParse(field.OldValue, out oldInt))
                        oldValue = this.OrganisationSubTypes.SingleOrDefault(m => m.Id == oldInt);
                    field.OldValue = oldValue != null ? oldValue.Name : string.Empty;

                    int newInt;
                    OrganisationSubType newValue = null;
                    if(int.TryParse(field.NewValue, out newInt))
                        newValue = this.OrganisationSubTypes.SingleOrDefault(m => m.Id == newInt);
                    field.NewValue = newValue != null ? newValue.Name : string.Empty;
                }

                if (field.MemberName.Equals("ContactPersonStaffId"))
                {
                    int oldInt;
                    OrganisationStaff oldValue = null;
                    if (int.TryParse(field.OldValue, out oldInt))
                        oldValue = this.OrganisationStaffs.SingleOrDefault(m => m.Id == oldInt);
                    field.OldValue = oldValue != null ? oldValue.Contact.FullName : string.Empty;

                    int newInt;
                    OrganisationStaff newValue = null;
                    if (int.TryParse(field.NewValue, out newInt))
                        newValue = this.OrganisationStaffs.SingleOrDefault(m => m.Id == newInt);
                    field.NewValue = newValue != null ? newValue.Contact.FullName : string.Empty;
                }

                if (field.MemberName.Equals("CountryId"))
                {
                    int oldInt;
                    Country oldValue = null;
                    if (int.TryParse(field.OldValue, out oldInt))
                        oldValue = this.Countries.SingleOrDefault(m => m.Id == oldInt);
                    field.OldValue = oldValue != null ? oldValue.Name : string.Empty;

                    int newInt;
                    Country newValue = null;
                    if (int.TryParse(field.NewValue, out newInt))
                        newValue = this.Countries.SingleOrDefault(m => m.Id == newInt);
                    field.NewValue = newValue != null ? newValue.Name : string.Empty;
                }

                // Save it to the DB
                audit.AuditRecordFields.Add(field);
            }
        }
    }

    if (audit.AuditRecordFields.Count > 0)
        this.AuditRecords.InsertOnSubmit(audit);
}

As you can see, this block of code is being repeated:

if (field.MemberName.Equals("CountryId"))
{
    int oldInt;
    Country oldValue = null;
    if (int.TryParse(field.OldValue, out oldInt))
        oldValue = this.Countries.SingleOrDefault(m => m.Id == oldInt);
    field.OldValue = oldValue != null ? oldValue.Name : string.Empty;

    int newInt;
    Country newValue = null;
    if (int.TryParse(field.NewValue, out newInt))
        newValue = this.Countries.SingleOrDefault(m => m.Id == newInt);
    field.NewValue = newValue != null ? newValue.Name : string.Empty;
}

The repeated pattern is:

  • A lookup on a certain table: Countries
  • Looking for a certain entity: Country
  • Using a certain expression: m => m.ID == oldInt
  • And another expression to convert the entity to a string: oldValue.Name

I was hoping that this could be done with some generic expression magic but I can't seem to figure it out.

Était-ce utile?

La solution

This should work as you intend.

One of the challenges you face is that you expect to have the expression for the int parameter before the variable is created as you parse the string. If you already had the variable created at the time you created the expression, it could reference the variable and your process would be simpler.

I got around that by dynamically building an expression after the string is parsed into the int variable.

    protected void InsertAuditRecordToDatabase(ModifiedMemberInfo[] changes, object entity)
    {
        Type type = entity.GetType();
        PropertyInfo key;
        key = type.GetProperties()
            .Where(o =>
                o.GetCustomAttributes(typeof(ColumnAttribute), true)
                    .Any(a => ((ColumnAttribute)a).IsPrimaryKey)).SingleOrDefault();

        AuditRecord audit = new AuditRecord();
        audit.Action = (byte)AuditAction.Update;
        audit.AuditDate = DateTime.Now;
        audit.AssociationTable = null;
        audit.AssociationTableKey = null;
        audit.EntityTable = type.Name;
        audit.EntityTableKey = int.Parse(key.GetValue(entity, null).ToString());

        audit.UserName = HttpContext.Current.User.Identity.Name;
        if (string.IsNullOrEmpty(audit.UserName))
            audit.UserName = "Anonymous";

        foreach (ModifiedMemberInfo mmi in changes)
        {
            AuditRecordField field = new AuditRecordField();
            if (!excludedFieldNamesFromAudit.Any(x => x.Equals(mmi.Member.Name, StringComparison.OrdinalIgnoreCase)))
            {
                field.MemberName = mmi.Member.Name;

                field.OldValue = (mmi.OriginalValue != null ? mmi.OriginalValue.ToString() : string.Empty);
                field.NewValue = (mmi.CurrentValue != null ? mmi.CurrentValue.ToString() : string.Empty);

                if ((field.OldValue != null && !field.OldValue.Equals(field.NewValue)) ||
                    (field.OldValue == null && field.NewValue != null))
                {
                    // Special handling
                    if (field.MemberName.Equals("EUAMemberTypeId"))
                    {
                        field.OldValue = GetDescription(this.OrganisationSubTypes, field.OldValue, m => m.Id, m => m != null ? m.Name : string.Empty);
                        field.NewValue = GetDescription(this.OrganisationSubTypes, field.NewValue, m => m.Id, m => m != null ? m.Name : string.Empty);
                    }

                    if (field.MemberName.Equals("ContactPersonStaffId"))
                    {
                        field.OldValue = GetDescription(this.OrganisationStaffs, field.OldValue, m => m.Id, m => m != null ? m.Contact.FullName : string.Empty);
                        field.NewValue = GetDescription(this.OrganisationStaffs, field.NewValue, m => m.Id, m => m != null ? m.Contact.FullName : string.Empty);
                    }

                    if (field.MemberName.Equals("CountryId"))
                    {
                        field.OldValue = GetDescription(this.Countries, field.OldValue, m => m.Id, m => m != null ? m.Name : string.Empty);
                        field.NewValue = GetDescription(this.Countries, field.NewValue, m => m.Id, m => m != null ? m.Name : string.Empty);
                    }

                    // Save it to the DB
                    audit.AuditRecordFields.Add(field);
                }
            }
        }

        if (audit.AuditRecordFields.Count > 0)
            this.AuditRecords.InsertOnSubmit(audit);
    }

    public static string GetDescription<T, TProp>(Table<T> thisTable, string searchParam, Expression<Func<T, TProp>> searchExpression, Expression<Func<T, string>> descriptionExpression)
        where T : class
    {
        if (!(searchExpression.Body is MemberExpression))
        {
            throw new ArgumentException("Search Expression must be a MemberExpression (i.e v => v.Id)", "searchExpression");
        }
        else
        {
            int searchValue;
            if (int.TryParse(searchParam, out searchValue))
            {
                var equalityExpression = Expression.Equal(searchExpression.Body, Expression.Constant(searchValue));
                var lambdaExpression = Expression.Lambda<Func<T, bool>>(equalityExpression, searchExpression.Parameters);

                // the passed-in expression must resemble v => v.Id
                // the generated expression will resemble v => v.Id == 5

                var value = thisTable.SingleOrDefault(lambdaExpression);
                return descriptionExpression.Compile()(value);
            }

            return string.Empty;
        }
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top