Ted Graham on .NET

Interesting issues and tidbits about software development, generally using .NET

  • Transactional behavior when updating a DataTable

    Steve wrote, "I was checking out Transactional behavior on DataTables, and that's some good stuff.  Did you ever come up with a good workaround?"
     
    We already had our own DataViewWrapper class that inherited from DataView, which we were using to catch the Changed events and republish them with more information.  We then created a custom "Transaction" object, that keeps a static count of how many Transactions currently exist (it implements IDisposable for deterministic destruction).  Now the DataViewWrapper can check if we are in a transaction.  If so, we don't publish the changed events.  When the last Transaction is disposed, we publish any pending changed events.  As a later optimization, we could intelligently combine changed events.  It takes more code than I would like, but it works.

  • Getting WinCVS out of flat mode

    If you accidentally browse up to the root directory or some other level that is too large for WinCVS to initially deal with and you have the flat mode browser view toggle button of WinCVS on, WinCVS will freeze indefinitely.  If killing it in Task Manager and restarting doesn't fix your problem, you need to hack the registry.
     
    Launch regedit (Start -> Run   "regedit") and browse to:
     
            HKEY_CURRENT_USER/Software/WinCvs/wincvs/CVS settings
     
    In CVS settings, find the variable  P_DisplayRecursive in the right hand window.  Right-click on P_DisplayRecursive and select Modify from the right-click menu.  in the window that gets displayed, change the Value data set with only two numbers in it from the 01 to 00.  Then click Ok at the bottom of the window, shut down Registry Editor, start up WinCVS again, and you should now have the flat mode button toggled off.

  • Transactional behavior on DataTables

     
    DataTables publish a number of events that are useful for taking action on a data change.  However, sometimes you need to make transactional changes to a table, where the data is not valid until several changes have completed.  The canonical example is transferring money between accounts; observers should not display the account balances until both the credit and debit have completed.  DataTable.BeginLoadData offers a way to suspend notifications, but it only works for changes made through LoadDataRow. 
     
    The below test illustrates the problem:
     
      private int _updateCount = 0;
      /// <summary>
      /// Fails, because BeginLoadData only suspends update notifications from LoadDataRow
      /// </summary>
      [Test]
      public void SuspendUpdates()
      {
       DataTable table = new DataTable("Customers");
       table.Columns.Add("ID", typeof(Int32));
       table.Columns.Add("Name", typeof(string));
       table.Rows.Add(new object[] { 1, "Graham" } );
       table.Rows.Add(new object[] { 2, "Smith" } );
       table.RowChanged+=new DataRowChangeEventHandler(table_RowChanged);
       table.Rows[0]["Name"] = "Sargeant";  // got married, so change the name
       Assertion.AssertEquals("Didn't Update", 1, _updateCount);
       table.BeginLoadData();
       table.Rows[0]["Name"] = "Graham-Sargeant";  // moved to Berkeley, have to hypenate
       // this will fail, since we modified the data without using LoadDataRow
       Assertion.AssertEquals("Update shouldn't have fired again", 1, _updateCount);
      }
      
      // Update handler that increments the updateCount
      private void table_RowChanged(object sender, DataRowChangeEventArgs e)
      {
       _updateCount++;
      }

  • Delegate and Event signatures

    I'm designing the events and delegates that will be used in a real-time stock quoting application.  All my delegates and events will be operating in a single process, so I'm leaning towards the following:
     
    // Instrument is a stock, Tick Message has the details of the real-time quote
    public delegate void TickArrivedHandler(Instrument sender, TickMessage tick);
     
    // The Instrument publishes an OnTickArrived event so subscribers can ask to be notified when a new tick (quote) arrives
    public event TickArrivedHandler OnTickArrived;
     
    However, this conflicts with Microsoft's conventions for declaring events (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconeventnamingguidelines.asp):
     
    The guidelines say, "Specify two parameters named sender and e. The sender parameter represents the object that raised the event. The sender parameter is always of type object, even if it is possible to use a more specific type."   I'd rather have more specific types passed in to the delegate so the subscriber can interrogate the Instrument object without a downcast, or worse, a series of "is" checks followed by downcasts.  Having the check for the right type done at compile seems better than having the client doing a downcast or switch on the type at runtime.
     
    The MS guidelines also say, "The state associated with the event is encapsulated in an instance of an event class named e. Use an appropriate and specific event class for the e parameter type."  Juval Lowy contradicts this in his excellent "Programming .NET Components" book.  He suggests defining the delegate to take the base EventArgs class, and "the subscriber should downcast the generic EventArgs to the specific argument class" (p. 106)  I don't want my subscribers downcasting, so I'd prefer to pass the data the subscriber needs.
     
    Thoughts?

    Ted

  • C# code review checklist

     
    My current client uses PSP extensively, so I've been putting together a checklist for reviewing C# code for use on our new project.  Your comments or additions appreciated:
    1. Are exceptions used to indicate error rather than returning status or error codes?
    2. Are all classes and public methods commented with .NET style comments?  Note that <summary> comments should discuss the "what" of public methods.  Discussion of "how" should be in <remarks> blocks or in-line with the code in question.
    3. Are method arguments validated and rejected with an exception if they are invalid?
    4. Are Debug.Asserts used to verify assumptions about the functioning of the code?  Comments like, "j will be positive" should be rewritten as Asserts. 
    5. Do classes that should not be instantiated have a private constructor?
    6. Are classes declared as value types only infrequently used as method parameters, returned from methods or stored in Collections?
    7. Are classes, methods and events that are specific to an assembly marked as internal?
    8. Are singletons that may be accessed by multiple threads instantiated correctly?  See the Enterprise Solution Patterns book, p. 263.
    9. Are methods that must be overriden by derived classes marked as abstract?
    10. Are classes that should not be overriden marked as sealed?
    11. Is "as" used for possibly incorrect downcasts? 
    12. Do classes override ToString instead of defining a Dump method for outputting the object's state?
    13. Are log messages sent to the logging component instead of Console?
    14. Are finally blocks used for code that must execute following a try? 
    15. Is foreach used in preference to the for(int i...) construct?
    16. Are properties used instead of implementing getter and setter methods?
    17. Are readonly variables used in preference to properties without setters?
    18. Is the override keyword used on all methods that are overriden by derived classes?
    19. Are interface classes used in preference to abstract classes?
    20. Is code written against an interface rather than an implementing class?
    21. Do all objects that represent "real-world" or expensive resources implement the IDisposable pattern?
    22. Are all objects that implement IDisposable instantiated in a using block?
    23. Is the lock keyword used in preference to the Monitor.Enter construct?
    24. Are threads awakened from wait states by events or the Pulse construct, rather than "active" waiting such as Sleep()?
    25. If equals is overridden, is it done correctly?  The rules for overriding equals are complex, see Richter p153-160 for details.
    26. If == and != are overridden, so they redirect to Equals?
    27. Do all objects that override Equals also provide an overloaded version of GetHashCode that provides the same semantics as Equals?  Note that overrides to GetHashCode should take advantage of the object's member variables, and must return an unchanging hash code.
    28. Do all exception classes have a constructor that takes a string and and another constructor that takes a string and an exception?
    29. Do all exception classes derive from the base Matrix exceptions and fit correctly into the exception hierarchy?
    30. Are all classes that will be marshaled or remoted marked with the Serializable attribute?
    31. Do all classes marked with the Serializable attribute have a default constructor?  This includes Exception and EventArgs classes.
    32. Do all classes that explicitly implement ISerializable provide both the required GetObjectData and the implied constructor that takes a SerializationInfo and a StreamingContext?
    33. When doing floating point calculations, are all constants doubles rather than integers?
    34. Do all delegates have a void return type and avoid using output or ref parameters?
    35. Do all delegates send the sender (publisher) as the first argument?  This allows the subscriber to tell which publisher fired the event. 
    36. Are all members of derived EventArg classes read-only?  This prevents one subscriber from modifying the EventArgs, which would affect the other subscribers.
    37. Are delegates published as events?  This prevents the subscribers from firing the event, see Lowy, p. 102 for details.
    38. Is common setup and teardown nUnit code isolated in Setup and Teardown methods that are marked with the appropriate attribute?
    39. Do negative nUnit tests use the ExpectedException attribute to indicate that an exception must be thrown?
    References:
    Juval Lowy, "Programming .NET Components"
    Jeffrey Richter, "Applied Microsoft .NET Framework Programming"
    "Enterprise Solution Patterns using Microsoft .NET" - available in published form or as a free pdf

  • Defining Roles in one place?

    We are building a web site that use declaritive security at the business level.  So we currently have attributes similiar to the following on our business methods:
    [PrincipalPermissionAttribute( SecurityAction.Demand,Role="Save Customer" )]

  • Enterprise Instrumentation Framework vs the Exception Management AB


    My current project was planning on using the Exception Handling Application Block (EMAB) for logging and monitoring exceptions.  We are also planning on using the Enterprise Instrumentation Framework (EIF) for application monitoring.  Other than Sameer’s mention, resources on EIF are scarce.  From reviewing Microsoft’s NetOps Guide it looks to me that the EIF provides a superset of the EMAB’s functionality.  I’m leaning towards using the EIF to provide:
    1) Logging on exceptions – through ErrorMessageEvent.Raise() in exception handling blocks
    2) Application monitoring – by raising events when critical business events occur.

  • Is Your Problem Exceptional?

    Error handling is frequently ignored or postponed until “after the basic functionality is working”. However, designing a standard error handling strategy is critical to producing reliable software, especially if you are writing a software library that will be used by other developers. Even if you aren’t building a library for distribution, other developers on your team or project depend on your code.