An extension method for visualizing ObjectStateEntries

EntityStateObjects, to me, are one of the most important little pieces of the EF puzzle. IT is the EntityStateObject that maintains all of the critical info for change tracking. But it’s hard to get the big picture of what’s going on in there when debugging because all of the important stuff is delivered through methods, not properties.

I wanted so badly to write a debugger visualizer for them but they are not serializable (big pout) so instead, I wrote an extension method that uses a ConditionalAttribute to ensure it doesn’t pop up during run time. It’s for my book but I didn’t want to hold onto it until October when the book should be published.

Since it’s not a Debugger Visualiser, I refer to it as a DebugTime visualizer. 🙂

Here’s what my ObjectStateEntry Visualizer looks like in action:

All of the info is pulled from the ObjectStateEntry. At the top it tells the fully qualified name of the type of the object being inspected as well as the object’s EntityState.

Then I use the various methods of the ObjectStateENtry to get the Original Values, the Current Values, the names of the fields and a list of the names of the modified fields.

All of this data I feed into the grid.

If the object is detached, then there is no ObjectStateEntry and the visualizer shows this message when you try to run it:

So enough with the screenshots. Here’s the code. And here’s a collection of important punctuations for you C# programmers who feel a need to translate

[ ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;  ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;  ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;  ; ; ; ; ; ; ; ; ; ; ; ; ; ;]


Imports System.Runtime.CompilerServicesImports System.Data.Objects'NOTE: The objects in use here are not serializable so they can't be used'in debugger visualizers. Instead, you'll need to use them directly, but you can 'give them a debug attribute so they are only available during debug mode.<Extension()> _Public Module Visualizers<ConditionalAttribute("DEBUG")> _<Extension()> _Public Sub VisualizeObjectStateEntry(ByVal eKey As EntityKey, ByVal context As ObjectContext)Dim ose As ObjectStateEntry = NothingIf Not context.ObjectStateManager.TryGetObjectStateEntry(eKey, ose) ThenWindows.Forms.MessageBox.Show("Object is not currently being change tracked and no ObjectStateEntry exists.", _
"ObjectStateEntry Visualizer", Windows.Forms.MessageBoxButtons.OK, Windows.Forms.MessageBoxIcon.Warning)ElseDim currentValues = ose.CurrentValuesDim originalValues = ose.OriginalValuesDim valueArray As New ArrayListFor i = 0 To currentValues.FieldCount - 1

'you can get from the ObjectStateEntry into the MetaData which actually comes from the EDM
Dim sName = currentValues.DataRecordInfo.FieldMetadata.Item(i).FieldType.NameDim sCurrVal = currentValues.Item(i)Dim sOrigVal = originalValues.Item(i)
'nothing like a little LINQ query to find some infoDim changedProp = (From prop In ose.GetModifiedProperties Where prop = sName).FirstOrDefaultDim propModified As StringpropModified = If(changedProp = Nothing, "", "X")

'the funky property naming in this anonymous type is to get around a wierdness with
'LINQ databinding that only occurs in VB - it alphabetizes the fields
valueArray.Add(New With _
{._Index = i.ToString, ._Property = sName, .Current = sCurrVal, _
.Original = sOrigVal, .ValueModified = propModified})NextDim frm As New debuggerFormWith frm.DataGridView1.DataSource = valueArrayEnd Withfrm.lblState.Text = ose.State.ToStringfrm.lblType.Text = ose.Entity.ToStringfrm.ShowDialog()End IfEnd SubEnd Module

The form has no code, just a few controls:

I created an assembly for my Entity Framework extension methods and just reference the assembly anywhere I want to use it.

Then when I want to use it, I call it against the EntityKey of an Entity Object:

context = new AWModel.AWModel.AWEntities();cust = context.Customers.Where(c => c.CustomerID == 223).First();cust.CompanyName = "JULIE COMPANY";cust.EntityKey.VisualizeObjectStateEntry(context);

This has been an enormously useful tool for when I have been presenting as well as just working.


  Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!  

One thought on “An extension method for visualizing ObjectStateEntries

  1. Nice work!Here’s a ;;;;;;;;;;;;;;;;;; version with some minor changes.I added data type and removed the dependency on windows. public static class Visualizers { [ConditionalAttribute("DEBUG")] public static void VisualizeObjectStateEntry(this EntityKey eKey, ObjectContext context) { ObjectStateEntry ose = null; if( !context.ObjectStateManager.TryGetObjectStateEntry(eKey, out ose) ) { Debug.WriteLine("Object is not currently being change tracked and no ObjectStateEntry exists.", "ObjectStateEntry Visualizer"); } else { var currentValues = ose.CurrentValues; DbDataRecord originalValues = null; if( ose.State != EntityState.Added && ose.State != EntityState.Detached ) originalValues = ose.OriginalValues; DataTable valueTable = new DataTable(); valueTable.Columns.Add("_Index"); valueTable.Columns.Add("_Property"); valueTable.Columns.Add("Type"); valueTable.Columns.Add("Current"); valueTable.Columns.Add("Original"); valueTable.Columns.Add("ValueModified"); for( int i = 0; i <= currentValues.FieldCount – 1; i++ ) { //you can get from the ObjectStateEntry into the MetaData which actually comes from the EDM var sName = currentValues.DataRecordInfo.FieldMetadata.ElementAt(i).FieldType.Name; var sType = ((System.Data.Metadata.Edm.PrimitiveType)currentValues.DataRecordInfo.FieldMetadata.ElementAt(i).FieldType.TypeUsage.EdmType).Name; var sCurrVal = currentValues[i]; var sOrigVal = originalValues!=null ? originalValues[i] : string.Empty; //nothing like a little LINQ query to find some info var changedProp = (from prop in ose.GetModifiedProperties() where prop == sName select prop).FirstOrDefault(); string propModified = null; propModified = changedProp == null ? "" : "X"; //the funky property naming in this anonymous type is to get around a wierdness with //LINQ databinding that only occurs in VB – it alphabetizes the fields DataRow row = valueTable.NewRow(); row.ItemArray = new object[] { i.ToString(), sName, sType, sCurrVal, sOrigVal, propModified }; valueTable.Rows.Add(row); } Debug.WriteLine("Type: " + ose.Entity.ToString()); Debug.WriteLine("State: " + ose.State.ToString()); ShowData(valueTable); } } public static void ShowData(DataTable dt) { ShowData(dt, -1); } public static void ShowData(DataTable dt, int numberRows, params string[] colsToShow) { StringBuilder sbhdr = new StringBuilder(); StringBuilder sbline = new StringBuilder(); int[] colW = new int[dt.Columns.Count]; for( int i = 0; i < dt.Columns.Count; i++ ) { DataColumn col = dt.Columns[i]; if( colsToShow.Contains(col.ColumnName) || colsToShow.Length <= 0 ) { int max = 0; // determine width foreach( DataRow row in dt.Rows ) if( row[col].ToString().Length > max ) max = row[col].ToString().Length; if( col.ColumnName.Length > max ) max = col.ColumnName.Length; colW[i] = max; sbhdr.Append(col.ColumnName.PadRight(max + 1)); sbline.Append("-".PadRight(max, ‘-‘)); sbline.Append(" "); } } Debug.WriteLine(sbhdr); Debug.WriteLine(sbline); if( numberRows <= 0 ) numberRows = dt.Rows.Count; for( int i = 0; i < numberRows; i++ ) { DataRow row = dt.Rows[i]; StringBuilder sb = new StringBuilder(); for( int j = 0; j < dt.Columns.Count; j++ ) { DataColumn col = dt.Columns[j]; if( colsToShow.Contains(col.ColumnName) || colsToShow.Length <= 0 ) sb.Append(row[col].ToString().PadRight(colW[j] + 1)); } Debug.WriteLine(sb); } if( dt.Rows.Count > numberRows ) Debug.WriteLine(string.Format(&amp

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.