Bring a bit of the Subsonic power to Entity Framework by adding automatic audit and logical delete fields

Posted November 4th, 2011 in .NET, ADO.NET, Productivity, SubSonic, Uncategorized by admin

Here is the code to do the same thing than in my previous article “Bring a bit of the Subsonic power to Linq to sql by adding automatic audit and logical delete fields”


namespace YouNamespace.DAL
{
 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Data;
 using System.Data.Common;
 using System.Data.Objects;
 using System.Linq;

 public partial class YOURCONTEXTEntities
 {
 /// <summary>
 /// System fields for automatic audit and logical delete
 /// </summary>
 private struct SystemFields
 {
 public const string CreatedOn = "CREATEDON";
 public const string ModifiedOn = "MODIFIEDON";
 public const string CreatedBy = "CREATEDBY";
 public const string ModifiedBy = "MODIFIEDBY";
 public const string IsDeleted = "ISDELETED";
 }

 /// <summary>
 /// Overriding the SaveChanges method to automaticaly set system fields if any.
 /// </summary>
 /// <param name="options"></param>
 /// <returns></returns>
 public override int SaveChanges(System.Data.Objects.SaveOptions options)
 {
 IEnumerable<ObjectStateEntry> newEntries = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added);

 foreach (ObjectStateEntry entry in newEntries)
 {
 ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
 .DataRecordInfo.FieldMetadata;

 FieldMetadata createdOnField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.CreatedOn, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdOnField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdOnField.Ordinal, DateTime.Now);
 }

 FieldMetadata createdByField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.CreatedBy, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdByField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdByField.Ordinal, "Sam");
 }

 FieldMetadata deletedField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.IsDeleted, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (deletedField.FieldType != null)
 {
 entry.CurrentValues.SetValue(deletedField.Ordinal, false);
 }
 }

 IEnumerable<ObjectStateEntry> modifiedEntries = this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
 foreach (ObjectStateEntry entry in modifiedEntries)
 {
 ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
 .DataRecordInfo.FieldMetadata;

 FieldMetadata createdOnField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.ModifiedOn, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdOnField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdOnField.Ordinal, DateTime.Now);
 }

 FieldMetadata createdByField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.ModifiedBy, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdByField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdByField.Ordinal, "Sam");
 }
 }

 IEnumerable<ObjectStateEntry> deletedEntries = this.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted);
 foreach (ObjectStateEntry entry in deletedEntries)
 {
 // change from deleted to modified (!important)
 this.ObjectStateManager.ChangeObjectState(entry.Entity, EntityState.Modified);

 ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
 .DataRecordInfo.FieldMetadata;

 FieldMetadata deletedField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.IsDeleted, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (deletedField.FieldType != null)
 {
 entry.CurrentValues.SetValue(deletedField.Ordinal, true);
 }
else
 {
 // change back from modified to deleted (!important)
 this.ObjectStateManager.ChangeObjectState(entry.Entity, EntityState.Deleted);
 }
 }

 return base.SaveChanges(options);
 }
 }
}

Debugging ADO.NET Datatables

Posted May 14th, 2010 in .NET, ADO.NET, Debug, Development by Sam Beauvois

Do you ever had the famous error message :

“Failed to enable constraints. One or more rows  contain values violating non-null, unique, or foreign-key  constraints.”

when using ADO.NET Datasets ?

Tthis is an incredibly useful message isn’t it ?

So what can you do to spare you some headaches ? Retrieve a more useful message sure !

You have to know that a Datatable object have a “HasError” property, this property allows you to know if the datatable has one ore more rows in error.

When HasError is true, you can retrieve the rows havin an error with the DataTable’s “GetErrors()” method wich return an array with all datarow having an error.

The “in errror” rows can inform you about errors with the “RowError” property.

Here is a simple helper class which can be used to debug a single DataTable or an entire DataSet:

using System;
using System.Data;

public class DataSetErrorRevealer
{

   /// <summary>
   /// Get errors on rows of a DataTable
   /// </summary>
   /// <param name="errorDataTable">The DataTable to be checked</param>
   public static void RevealTableErrors(DataTable errorDataTable)
   {
      if (errorDataTable == null)
      {
         Console.WriteLine("The provided DataTable object is null");
         return;
      }

      if (errorDataTable.HasErrors)
      {
         DataRow[] rowsInError = errorDataTable.GetErrors();
         if (rowsInError.Length <= 0)
         {
            Console.WriteLine("There is no problem with table {0}", errorDataTable.TableName);
         }
         else
         {
            foreach (DataRow errorRow in rowsInError)
            {
               Console.WriteLine(errorRow.RowError);
            }
         }
      }
   }

   /// <summary>
   /// Get errors on row for all tables of a DataSet
   /// </summary>
   /// <param name="errorDataSet">The Dataset to be checked</param>
   public static void RevealDataSetErrors(DataSet errorDataSet)
   {
         if (errorDataSet == null)
         {
            Console.WriteLine("The provided DataSet is null");
            return;
         }

         foreach (DataTable errorTable in errorDataSet.Tables)
         {
            RevealTableErrors(errorTable);
         }
      }
   }

Use it this way :


   TestDataSet dataset = new TestDataSet();
   try
   {
      using (SqlConnection sqlCon = new SqlConnection("MyConnectionString"))
      {
         SqlCommand sqlComm = sqlCon.CreateCommand();
         sqlComm.CommandType = CommandType.StoredProcedure;
         sqlComm.CommandText = "Select Stored procedure";
         SqlDataAdapter sqlDa = new SqlDataAdapter(sqlComm);
         sqlDa.Fill(dataset);
      }
   }
   catch (Exception ex)
   {
   // Will provide the error message :
   // "Failed to enable constraints. One or more rows
   // contain values violating non-null, unique,
   // or foreign-key  constraints."
   Console.WriteLine("Error : {0}", ex);

   // will provide a more helpful message
   DataSetErrorRevealer.RevealDataSetErrors(dataset);
   }

If you prefer, you can use an extension method :


using System.Data;
using System;

public static class DataSetExtensions
{
   public static void RevealErrors(this DataSet errorDataSet)
   {
      if (errorDataSet == null)
      {
         Console.WriteLine("The provided DataSet is null");
         return;
      }

      foreach (DataTable errorTable in errorDataSet.Tables)
      {
         errorTable.RevealErrors();
      }
   }

   public static void RevealErrors(this DataTable errorDataTable)
   {
      if (errorDataTable == null)
      {
         Console.WriteLine("The provided DataTable object is null");
         return;
      }

      if (errorDataTable.HasErrors)
      {
         DataRow[] rowsInError = errorDataTable.GetErrors();
         if (rowsInError.Length <= 0)
         {
            Console.WriteLine("There is no problem with table {0}", errorDataTable.TableName);
         }
         else
         {
            foreach (DataRow errorRow in rowsInError)
            {
               Console.WriteLine(errorRow.RowError);
            }
         }
      }
   }
}

use :


catch (Exception ex)
{
   // will provide an helpful message
   dataset.RevealErrors();
}

Hope this helps !

using System;
using System.Data;public class DataSetErrorRevealer
{

/// <summary>
/// Get errors on rows of a DataTable
/// </summary>
/// <param name=”errorDataTable”>The DataTable to be checked</param>
public static void RevealTableErrors(DataTable errorDataTable)
{
if (errorDataTable == null)
{
Console.WriteLine(“The provided DataTable object is null”);
return;
}

if (errorDataTable.HasErrors)
{
DataRow[] rowsInError = errorDataTable.GetErrors();
if (rowsInError.Length <= 0)
{
Console.WriteLine(“There is no problem with table {0}”, errorDataTable.TableName);
}
else
{
foreach (DataRow errorRow in rowsInError)
{
Console.WriteLine(errorRow.RowError);
}
}
}
}

/// <summary>
/// Get errors on row for all tables of a DataSet
/// </summary>
/// <param name=”errorDataSet”>The Dataset to be checked</param>
public static void RevealDataSetErrors(DataSet errorDataSet)
{
if (errorDataSet == null)
{
Console.WriteLine(“The provided DataSet is null”);
return;
}

foreach (DataTable errorTable in errorDataSet.Tables)
{
RevealTableErrors(errorTable);
}
}
}