var blog = new ThoughtStream(me); RSS 2.0
 Saturday, April 05, 2008

I've been hearing this a lot lately, and finally started reading up on the roots of the Agile movement. It all goes back to Lean Manufacturing. Oddly enough, I've had exposure to Lean Manufacturing at a previous job. So the concepts and names are all very familiar to me. Back in those days, I had heard of the concept of Lean Software Development, but it never really made any impact on how I wrote software. These days, though, I'm diving into as much Agile as possible and the further down the road that I travel, the more and more parallels I see, to the lean manufacturing world.

If you are at all interested in the roots of agile, here's some recommend reading (mostly Wikipedia) to get you started.

Wikipedia:

Books:

I'm hoping to absorb more of this information, quickly. It seems the more I learn about lean manufacturing, the more I understand about software development. I certainly don't claim to be an expert on either subject... hopefully I can change that.

Saturday, April 05, 2008 11:58:24 AM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: Behavior Driven Development | General | Lean Systems | Management | Philosophy of Software

 Wednesday, April 02, 2008

Way back in the day (somewhere around 1994 or 1995, I think), I saw the movie, "Search for Bobby Fischer". One of the lines that has always stuck in my head is from the teacher, Vinnie:

He didn't teach you how to win, he taught you how not to lose.

At the time, I didn't like this line - I didn't understand why playing "not to lose" was different than playing to win. Over the years, I've seen variations of this statement and had a little more understanding of what it actually means. Last night, though, I was involved in various philosophy-of-software discussions with a friend and this quote popped up in my mind again, in the context of project failure and the fear of failure. I think I finally understand what this quote is saying, now - and it all comes down to the fear of failure vs. embracing failure as an opportunity to learn and find new ways to succeed.

Knowing Success Through Failure

Looking back at my life, I have two very distinct memories of catastrophic failure - the kind of failure where I was so emotionally invested in the project or goal, that the failure was truly devastating to the point where I would have a complete breakdown and be entirely lost for a while.

The first of these memories was back in College. I have an associates degree (AA) in music, from a junior college back in my hometown in Kansas. After graduating with that degree, I found another school in Denver that had a sound recording / engineering track in their music school and I signed up for it with all the passion and motivation that I now have for software development. When I moved on to campus at that school and started figuring out what classes I would be taking, etc., I found out that I had slipped through the cracks in the school of music. As it turns out, I was supposed to have interviewed for the school of music and done some performance in front of various professors, to be accepted. With that in mind, I worked with the director of the program and did my performances - and completely failed. As it turns out, I had a general skill with musical instruments and could play just about anything at a very mediocre level, but I was not terribly good at any instrument. The end is result is that I was told (in very kind, helpful terms) that I would need a few years of lessons before I could be accepted into the school of music. Needless to say, this was a very devastating experience for me. My goal and my dream, at the time, was to be a musician and work in a recording studio. Shortly after the realization that my goal was unattainable, I had various talks with various people close to me and some talks with the director of the music school. We quickly realized that I could still attain the primary goal in my dream - to work in a sound recording studio and be in the sound recording / engineering program at the school - but my specified major would not be music. I ended up in the Management Information System department, with that being my major, while still taking the sound recording / engineering classes.

The second of these memories is significantly less important in the grand scheme of my life. A few years ago, I was attempting to install a new car stereo system in my car. I had done stereo and speaker installation before, but had never done an amp and subwoofer. Over a 4 day weekend, I ended up with a car that had no working speakers, a blown amp, and a subwoofer with holes punched in it. At the time, this was another very devastating outcome for me - I had a very specific goal in mind and had spent a significant amount of time and money on the project, with no usable results. Two days later, I had my car in a professional car audio shop with professional audio installation personnel fixing the problems and installing a new amp and subwoofer. The end result was that I had a great system in my car.

Looking back at both of these experiences, I see a pattern: a project or goal, a significant personal investment, a catastrophic failure and finally, an outcome that still satisfied my criteria for success.

There's an old cliche line that I've heard many times:

You can't know true success unless you know true failure.

I believe I've been as close to true failure as I can get, without giving up on a project or goal. True failure, though, can only come from giving up.

Playing To Win vs. Playing Not To Lose

My career as a software developer has been very interesting, so far. I've had a lot of great success and a few fairly large failures. What's really interesting to me, though, is looking back at those failures and seeing the risk that I was willing take. I've jumped into some very difficult, new, demanding situations with little or no knowledge and experience in those situations. Many times, I was able to find the experience quickly and succeed, but some times I was not able to find the experience quickly enough and I failed. These successes and failures are why I believe I understand the issue of playing to win vs. playing not to lose, now. As I said before, it all comes down to the fear of failure vs. embracing failure as an opportunity to learn and find new ways to succeed. With this in mind, I have some questions that may help you understand playing to win vs. playing not to lose.

Are you willing to take risks in your project? Are you willing to put forth the effort involved in trying to create what could be a better you, or a better project, knowing that you could fail if you try? Are you willing to open yourself for failure so that you can learn, and have a greater chance of greater success?

Are you playing to win?

Or are you only willing to do the work that you are comfortable with now, and only willing to learn a little bit of something new, keeping a safety net around yourself? Are you willing to keep yourself from taking risk so that you don't have any significant chance of failure, and no significant growth? Are you afraid of change or afraid that you will fail?

Are you playing not to lose?

Good Judgement and Experience

Another tired old cliche that I've heard a thousand times:

Good judgement comes from experience. Experience comes from bad judgement.

I make a dozen bad decisions every day. I also make a dozen good decisions every day, because I am willing to honestly look at the decisions I've made and admit that they were bad decisions. By allowing myself to understand that these decisions were bad decisions, and admitting that to not only myself, but to my managers and team members as well, I set myself up for success. I'm able to look at these decisions, understand why there wrong, and correct the mistakes. And by doing this on a daily basis, if not more often, I can mitigate the failure that any one decision may cause.

Embracing Failure Without Fearing It

One of the reasons I am so in love with Test Driven Development and Behavior Driven Development is that it accelerates the failure-to-success process. I'm not unit testing to prove that I have not failed. I'm unit testing to prove that I have succeeded and created the API that I want.

I expect failure when I unit test. I expect to write horrible APIs that are unusable, ugly, stupid, and obnoxious. I WANT to realize that I am doing this, through my unit tests, because I can accelerate the process of realization and correction by using my unit tests as a design tool.

And I challenge you to do the same - to stop fearing failure and not be afraid to make mistakes. If you believe you have made a mistake, expose it for peer review. Ask for others to validate or invalidate that mistake and learn from the experience. Find the tools, techniques, coworkers and peers that will help you accelerate the realization of failure so that you can correct the failure and succeed, faster.

A Personal Rant: My Success As A Developer

I believe I am a very successful software developer and leader. I have a lot of experience under my belt and a lot of knowledge that I want to share with others.

So, why am I successful? Why was I responsible for the public face of a 300 million dollar multi-national corporation, through it's web sites, when I was 22 years old? Why was I made the lead developer in a team of 10, when I was the new guy in the office? Why am I now diving into the cutting edge of Behavior Driven Development and actively involved in the Agile community? Why am I making a name for myself not only in my office, but also in the development world outside of my office?

Because I take risk and embrace my failures. I openly admit my failures at the first possible moment, so that I can learn. I actively seek the voice of dissent and the views that I don't agree with, so that I can be proven to be wrong and learn how to be right.

Why am I successful? Because I play to win.

Wednesday, April 02, 2008 8:38:07 AM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: General | Management | Philosophy of Software

 Monday, March 31, 2008

A while back, I posted a stream of thoughts concerning programming in triples. The basic idea is that we have 2 developers doing pair programming and a third developer doing automated acceptance test development, on the same story at the same time.

Triples Programming Terms

I've been discussing this idea with various people since then, and I'm finding myself referring to this team organization with a specific term, now:

Pair+1 Programming

I don't know if this term has been used previously, or if there is already a term for this. However, I find this to be a good name for the organization that I am talking about.  It really is pair programming, as defined by many common sources, plus one acceptance test developer. The really fun part is that all three of the developers in this unit will switch roles throughout the day. The end result is that we have a very well cross-trained team - each member of each unit will be responsible for writing unit tests, writing production code, writing acceptance tests, and thinking ahead of the current code, throughout any given day.

An Agile Team includes more than just the developers - the customers, the test lead, the technical writers, the project manager, etc., are all part of the team. Pair+1 Programming is not into teams. A "team" implies that they the persons involved are fairly static - they don't move between teams, etc. In order to distinguish the Pair+1 organization from the Team as a whole, I am also going to refer to this group with another name:

Development Unit (Update: based on further discussion, renaming this)

WorkCell

Just as a developer will rotate rolls within the Unit, I believe Pair+1 programmers should move between WorkCells on a regular basis. This will further the cross-training that occurs and help to pollinate the knowledge of one WorkCell into the thoughts and goals of other WorkCells. The migration is likely to be less volatile than the role switching that happens within a WorkCell. For example, developers rotate positions every 2 or 3 hours, within the WorkCell, whereas a developer may only rotate between WorkCells once a day or once a week.

Development Systems

Within each Development Unit, there will be two development systems.

  1. The primary system of that Unit will be the pair programming system. This system should have a very large monitor - 30" is preferable - to allow the Pair to easy see everything on the screen.
  2. The secondary system will be used for Acceptance Tests and is likely to only need a 22" or 24" monitor, as it will be primarily used by a single developer.

Each of these systems, aside from monitor size, should be a mirror of each other - the same system specs with the same software development packages installed. Every machine used for development should be able to execute any of the tests - unit tests or acceptance tests - no matter the primary use of that development machine. This is required so that the Pair working on the Development machine can execute the Acceptance Tests being created on the Acceptance Test machine; thus ensuring that they are coding appropriately, and passing the expected tests. The Acceptance Test developer should also be able to execute the code being written by the Pair, to ensure that the Acceptance Tests are being written against the API correctly.

Following standard Agile practices, these machines should not be concerned with Email, IM, or other non-development tasks. They should be dedicated entirely to development work. If a person needs Email, IM, etc. then the office space should provide access via shared systems and/or a developer should have their own system (likely a laptop) that they can take with them wherever they go. The point is, though, to remove distraction from the Development Unit. Keep the non-development software off the Unit's systems, as much as possible, to ensure that the Unit can focus on development and not be distracted by 50 different flashing icons and windows on the machine.

Disclaimer

A lot of my thoughts are purely academic, at this point. I am hopefully going to have some actual experience with this environment, soon. As such, I am trying to organize my thoughts so that I can have some direction to start with. I reserve the right to be wrong and change my mind whenever I want, likely re-defining the terms and processes involved in Pair+1 Programming.

Monday, March 31, 2008 2:26:19 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: Acceptance Testing | Agile | Behavior Driven Development | Management | Pair+1 Programming | Unit Testing

In my previous post, I talked about my base repository class that I use, to abstract the NHibernate details away from the actual repository. Now that I have the Do and DoTransaction methods to further the abstraction, I thought it would be good to share my whole abstraction.

using System;
using NHibernate;
using NHibernate.Cfg;
 
namespace RepositoryBase
{
 
    public abstract class Repository : IDisposable
    {
 
        #region Vars
 
        private static readonly Configuration _config;
 
        private static readonly ISessionFactory _factory;
 
        #endregion
 
        #region Constructor / Destructor
 
        static BaseDAL()
        {
            _config = new Configuration();
            _config.Configure(typeof(BaseDAL).Assembly, "RepositoryBase.hibernate.cfg.xml");
            _factory = _config.BuildSessionFactory();
        }
 
        ~BaseDAL()
        {
            Dispose();
        }
 
        #endregion
 
        #region Properties
 
        public ISession Session { get; private set; }
 
        public ITransaction Transaction { get; private set; }
 
        #endregion
 
        #region Methods
 
        protected void Do(Action unitOfWork)
        {
            try
            {
                OpenSession();
                unitOfWork();
            }
            finally
            {
                CloseSession();
            }
        }
 
        protected void DoTransaction(Action unitOfWork)
        {
            try
            {
                OpenSession();
                BeginTransaction();
                
                if (unitOfWork != null)
                    unitOfWork();
                
                CommitTransaction();
            }
            catch
            {
                RollbackTransaction();
                throw;
            }
            finally
            {
                CloseSession();
            }
        }
 
        #endregion
 
        #region Helper Methods
 
        private void OpenSession()
        {
            if (Session != null) return;
 
            Session = _factory.OpenSession();
            Session.FlushMode = FlushMode.Auto;
        }
 
        private void CloseSession()
        {
            if (Session == null) return;
 
            if (Session.IsOpen)
            {
                Session.Close();
            }
            Session.Dispose();
            Session = null;
        }
 
        private void BeginTransaction()
        {
            ValidateSession();
 
            Transaction = Session.BeginTransaction();
        }
 
        private void CommitTransaction()
        {
            ValidateSession();
 
            if (Transaction != null)
                Transaction.Commit();
 
            CloseTransaction();
        }
 
        private void RollbackTransaction()
        {
            if (Transaction != null)
                Transaction.Rollback();
 
            CloseTransaction();
        }
 
        private void ValidateSession()
        {
            if (Session == null)
                throw new ApplicationException("NHibernate Session Not Open.");
        }
 
        private void CloseTransaction()
        {
            if (Transaction == null) return;
 
            Transaction.Dispose();
            Transaction = null;
        }
 
        #endregion
 
        #region IDisposable Members
 
        public void Dispose()
        {
            CloseTransaction();
            CloseSession();
        }
 
        #endregion
 
    }
}

Note that I am using an embedded resource as my "hibernate.cfg.xml" location. Just change this line to use a file system resource, or whatever you want for your hibernate configuration.

I'm still wanting to re-abstract this into a set of objects that let's me be concerned with transactions and queries at a business level. One step at a time, though. The syntax that I would like to see, at the moment, would be something like this:

public void SomeBusinessValue()
{
    Something something = DoSomething.BusinessRelated();
    SomethingElse somethingElse = SomethingElse();
    Repository.DoTransaction(() =>
    {
        SomeRepository.Save(something);
        SomeOtherRepository.Delete(somethingElse);
    });
}

And if I get really ambitious, I may try to incorporate Castle.Windsor's Automatic Transaction Facility, so that I can have syntax like this:

[Transactional(Transaction.Requires)]
public void SomeBusinessValue()
{
    Something something = DoSomething.BusinessRelated();
    SomethingElse somethingElse = SomethingElse();
 
    SomeRepository.Save(something);
    SomeOtherRepository.Delete(somethingElse);
}

Of course, the more I travel down this path, the more obvious it is how Rhino.Commons' Repository came along. Heh - I'm only about 2 steps away from completely re-doing Ayende's UnitOfWork.

In fact, I may try to re-use NHibernateQueryGenerator and go for this syntax:

[Transactional(Transaction.Requires)]
public void SomeBusinessValue()
{
    Something something = Repository<Something>.Find(Where.SomeProperty = someValue);
 
    Something something = DoSomething.BusinessRelated();
    SomethingElse somethingElse = SomethingElse();
 
    Repository<Something>.Save(something);
    Repository<SomethingElse>.Delete(somethingElse);
}

Wouldn't that be fun... I really do like this syntax. In fact, I spent 9+ months working with it on a project because I love the simplicity of the syntax. In the end, though, I did not understand all of the underlying architecture and abstraction and Ayende set up (he includes the ability to plug in any DAL, including Castle.ActiveRecord, NHibernate, etc) and it caused headaches for me.

...

The major problem I have with this syntax, at the moment, is unit testing the Repository object. I either need to hide these details behind an IWhateverRepository interface (complete with Save, Delete and GetByWhatever methods) like I have been doing, or I I'll need to learn how to unit test NHibernate with in-memory database or something... We'll see where this leads.

(side note: how's that for a "ThoughtStream". )

Monday, March 31, 2008 1:22:29 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: .NET | Agile | Data Access | NHibernate | Unit Testing | UnitOfWork

For the last few months, I've had a very small base class that abstracts out the NHibernate configuration, session creation, etc. It works very well, but is very limited in what it can do. basically, every method in my actual repository implementation would have to open a new session, execute a criteria and close the session. A typical implementation would look like this:

public ICollection<Invoice> GetAll()
{
    ICollection<Invoice> invoices = null;
    try
    {
        OpenSession();
 
        invoices = Session.CreateCriteria(typeof(Invoice)).List<Invoice>(); 
    }
    finally
    {
        CloseSession();
    }
 
    return invoices;
}

That certainly is easy and keeps the code fairly clean, removes a lot of duplication, etc.

However, there is a significant limitation - I can't have any code re-use for multi-query scenarios, without duplicating code. In other words, if I want to load that list of invoices and then load some other collection from another repository, I have to use two different Sessions across two different repository implementations. This really becomes an issue when dealing with transactions - I want my entire change set to pass or fail in a single transaction. In my current abstraction, this can't be done.

Fortunately, NHibernate has the solution to my dilemma built right in - all I need to do is create my criteria objects without a session, and then I can execute any / all of them from any session that I want.

public ICollection<Invoice> GetAll()
{
    ICollection<Invoice> invoices = null;
    try
    {
        DetachedCriteria criteria = DetachedCriteria.For<Invoice>();
        
        OpenSession();
        invoices = criteria.GetExecutableCriteria(Session).List<Invoice>(); 
    }
    finally
    {
        CloseSession();
    }
 
    return invoices;
}
 

I don't have a complete abstraction of the separate execution, yet. However, a very basic implementation could look like this (idea stolen from Ray Houston):

protected void Do(Action unitOfWork)
{
    try
    {
        OpenSession();
        unitOfWork();
    }
    finally
    {
        CloseSession();
    }
}
 
public ICollection<Invoice> GetAll()
{
    ICollection<Invoice> invoices = null;
 
    DetachedCriteria criteria = DetachedCriteria.For<Invoice>();
 
    Do(() =>{
        invoices = criteria.GetExecutableCriteria(Session).List<Invoice>();
    });
    
    return invoices;
}

This simple abstraction provides a lot of benefit for us.

  • Eliminates duplicate code (not calling OpenSession / Close Session from all repository methods)
  • Removes ugly Try / Catch blocks from Repository methods
  • Allows multiple Criteria to be executed from a single Session / Transaction

And most importantly - this gives us the ability to create a better abstraction of NHibernate, to support business level transactions, not just repository level transactions. Obviously, this simple example is not going to provide business level transactions. It does get us down the path, though.

Monday, March 31, 2008 10:29:10 AM (Central Standard Time, UTC-06:00)  #    Comments [1]. Trackback 
Tags: .NET | Agile | Data Access | NHibernate | Refactoring | UnitOfWork

 Wednesday, March 26, 2008

In the last few months, I've heard a person say this multiple times

"business runs on de-normalized data"

The context of this statement has always been the discussion of database design and implementation... and I continue to wonder where this person's experience is coming from, to make such a statement. In my world - the business of developing software to run a business or automate a portion of a business - this is very far from the truth. My response to this statement is (and yes, you can quote me on this - please do)

Management reports on de-normalized data, but operations runs entirely on well-normalized data

I'm not going to make any ignorant or naive claims about de-normalized data having no place in business. There certainly is a lot of business value in de-normalized data. That's why we have data warehousing, OLAP cubes, and other reporting database structures (including Views, Stored Procedures, etc. that will de-normalize data for live reporting). When it comes to the day to day business, though - the people on the floor doing the low level business work - well normalized data is an absolute must.

From WikiPedia's entry on Database Normalization:

Database normalization, sometimes referred to as canonical synthesis, is a technique for designing relational database tables to minimize duplication of information and, in so doing, to safeguard the database against certain types of logical or structural problems, namely data anomalies. For example, when multiple instances of a given piece of information occur in a table, the possibility exists that these instances will not be kept consistent when the data within the table is updated, leading to a loss of data integrity. A table that is sufficiently normalized is less vulnerable to problems of this kind, because its structure reflects the basic assumptions for when multiple instances of the same information should be represented by a single instance only.

I'm certainly not a DBA and I'm not a real database guru. I do have more than 10 years experience working with various database systems (SQL Server, Access, Oracle, SQLite, MySQL, DB2/DB400, and XML & flat files , etc.) in various business scenarios (manufacturing, engineering, business process automation, e-commerce/e-business, enterprise integration, etc. etc.) and I have a pretty high opinion of my relational modeling capabilities ... and It evokes a sense of disbelief and shock when I see poorly normalized schema's or hear statements like this being made.

Please, please, PLEASE take the time to understand what well normalized database design is and why it's necessary for flexible, maintainable software.

Wednesday, March 26, 2008 12:38:04 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: Data Access | Database Design | Management

 Monday, March 24, 2008

This might be common knowledge for those that use NHibernate - but it's new to me, since I am not an NHibernate power-user, yet.

Given this object model:

public class Invoice
{
    private int _id = 0;
    private string _invoiceNumber = string.Empty;
    private DateTime _invoiceDueDate = DateTime.MinValue;
 
    private ICollection<InvoiceDetail> _invoiceDetails = new HashedSet<InvoiceDetail>();
 
    public int Id
    {
        get { return _id; }
    }
 
    public string InvoiceNumber
    {
        get { return _invoiceNumber; }
        set { _invoiceNumber = value; }
    }
 
    public DateTime InvoiceDueDate
    {
        get { return _invoiceDueDate; }
        set { _invoiceDueDate = value; }
    }
 
    public ICollection<InvoiceDetail> InvoiceDetails
    {
        get { return _invoiceDetails; }
    }
 
    public InvoiceDetail CreateDetail()
    {
        InvoiceDetail detail = new InvoiceDetail();
        InvoiceDetails.Add(detail);
        return detail;
    }
 
}
 
 
public class InvoiceDetail
{
 
    internal InvoiceDetail() { }
 
    private int _id = 0;
    private string _productName = string.Empty;
    private decimal _productCost = 0;
    private int _productQuantity = 0;
 
    public int Id
    {
        get { return _id; }
    }
 
    public string ProductName
    {
        get { return _productName; }
        set { _productName = value; }
    }
 
    public decimal ProductCost
    {
        get { return _productCost; }
        set { _productCost = value; }
    }
 
    public int ProductQuantity
    {
        get { return _productQuantity; }
        set { _productQuantity = value; }
    }
 
}

We can load any invoice that has an Invoice Detail with a specific Product Name, using this NHibernate code:

invoices = Session.CreateCriteria(typeof(Invoice))
    .CreateCriteria("InvoiceDetails")
    .Add(Expression.Eq("ProductName", productName))
    .List<Invoice>();

Additionally, we can add paging to the result set, by including the SetFirstResult and SetMaxResult calls in the Criteria.

invoices = Session.CreateCriteria(typeof(Invoice))
    .CreateCriteria("InvoiceDetails")
    .Add(Expression.Eq("ProductName", productName))
 
    .SetFirstResult(pageSize * pageNumber)
    .SetMaxResults(pageSize - 1)
 
    .List<Invoice>();

Notice the use of pageSize and PageNumber to set the First Result value - this creates an index-by-zero page number that we want to view. For example, if our page size is 10 and we are on page zero, then the FirstResult is going to be 0 and the last result will be 9. 0 through 9 = 10 results. If we are on page 3, then the FirstResult is 30 and the MaxResult is 39...

Works pretty well... now, let's just hope that the actual underlying database implementation of this can take advantage of each DBMS' optimizations for doing this.

Monday, March 24, 2008 4:39:52 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: .NET | Data Access | NHibernate

If you are considering the use of NHibernate, or are already using NHibernate, be sure that you always override the .Equals and .GetHashCode methods of your entities. NHibernate makes use of these methods extensively and you are likely to have strange issues if you don't override both of these correctly. Unfortunately, GetHashCode is one of those areas that is difficult to get right; but it needs to be done anyways.

For more information, see:

And one specific note on .Equals: be sure to check ReferencEquals as the last resort, if no other comparison is possible.

I've learned these lessons the hard way; hopefully you won't have to.

Monday, March 24, 2008 2:29:02 PM (Central Standard Time, UTC-06:00)  #    Comments [2]. Trackback 
Tags: .NET | NHibernate

In WCF, when using NetDataContractSerializer to enable .NET Remoting, DataContract objects are still serialized. Only ServiceContract objects are marshaled ByRef. The same setup is possible in native .NET Remoting, as well. It seems more likely to happen in WCF, though. Based on my experience, Remoting is usually all MarshalByRef or all Serialized - but that's just my experience. Either way, if you are serializing your object model, you need to be careful.

Parent <-> Child Bidirection Relationships

If you have a Parent<->Child bidirectional relationship in your DataContract objects, when you send the DataContract objects across WCF, they will be serialized/deserialized and the resulting hierarchy of objects will be: Parent->Child->CopyOfParent. This causes problems when using NHibernate to auto save/load your object tree.

For example, this object hierarchy:

Parent
  |--Child
  |     |--Parent (reference to actual Parent)

Will end up looking like this, after being serialized / deserialized:

Parent
  |--Child
  |     |--CopyOfParent (new object, independent of actual Parent)

To fix this, you'll have to manually re-build your references, after the objects are deserialized:

foreach(Child child in parent.Children)
{
    child.Parent = parent;
}

If you don't rebuild the hierarchy references like this, NHibernate will not save or update your child objects correctly. You will either get "Transient Instance" exceptions or you will end up with orphaned children records because they will not have their parent id set correctly.

Here's an example of what one of my data access methods looks like, in an app that remotes the data access layers via WCF (and hides most of the NHibernate code in a base class):

public void Save(FooBar fooBar)
{
    try
    {
 
        foreach(Widget widget in fooBar.Widgets)
        {
            widget.FooBar = fooBar;
        }
 
        OpenSession();
        BeginTransaction();
        Session.SaveOrUpdate(fooBar);
        CommitTransaction();
    }
    catch
    {
        RollbackTransaction();
        throw;
    }
    finally
    {
        CloseSession();
    }
}

Extended Problem: Multi-Parented Children

Although I haven't run into this situation yet, I am assuming that the same problem will occur if you have multiple parents pointing to the same child. For example, if you have:

Parent
  |--Child1
  |     |--GrandChild1 (same reference as Child2's GrandChild)
  |--Child2
  |     |--GrandChild1 (same reference as Child1's GrandChild)

When you send this structure across WCF as a set of DataContract objects, I imagine that you will end up with this:

Parent
  |--Child1
  |     |--GrandChild1 (duplicate of Child2's GrandChild)
  |--Child2
  |     |--CopyOfGrandChild1 (duplicate of Child1's GrandChild)

If your intention is to have Child1 and Child2 reference the same record in the database, you will need to reconstruct the Child1 and Child2 references to GrandChild1, ensuring that both point to the same object. I can see the basic code as traversing the children and comparing each of the gradchildren's values, then picking one winner between the same values and resetting the references on the rest of them. Unfortunately, I think the solution for this scenario would likely be unique to each situation, due to the complexity of picking the correct reference.

Has anyone run into this situation? If so, how did you solve it?

Monday, March 24, 2008 2:01:16 PM (Central Standard Time, UTC-06:00)  #    Comments [2]. Trackback 
Tags: .NET | NHibernate | WCF

 Monday, March 17, 2008

A few people have asked what my blog setup is, in respect to screen shots and code samples, so here's the answer for all to see.

Blog system: dasBlog

I love the simplicity of dasBlog. No extra fluff, no massive database or configuration system. It's just a blog with the features that I want, and it stores all of it's settings and content in XML files.

Be sure to change the web config so that the trust level is "full" and not "medium". Otherwise the blog posting API won't allow you to upload images and other attachments.

Web Host: WebHost4Life

A good .NET hosting company. I have the $10/month account type. There are plenty of other (and probably better) .NET hosting companies out there.

Screen Shots: a custom app I wrote 4 years ago, called ScRap.NET

It's easy to use. just hit the "PrtScn" (print screen) button on your keyboard, then click-n-drag with your mouse. The gray box that draws on your screen is where the screen shot will come from.  There are professional options for this, though - such as SnagIt. The reality of it is that it doesn't matter what you use to create the screen shots. The reason mine look like they do is because of my post authoring tool.

Post Authoring Tool: Windows Live Writer

An off-line blog editing tool (which I am using to write this post). It supports most of the blogs out there - the major ones anyways; and has lots of nice little features to make post writing simple. It has it's limitations (not all HTML is supported or easy to do) but if you can live with the formatting limitations, it produces some high quality work in an offline, "draft"-able manner. It supports screen shots, file attachments and some other cool stuff.

Check out the plugins for it, to add a lot more fun stuff.

Code Format Tool: Code Snippet Plugin

All the beauty of my code examples come from this plugin. Just paste some text into the plugin and set your formatting options.

And that's it... a pretty simple setup that does exactly what I want in my blog.

Monday, March 17, 2008 3:33:44 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: General

Navigation
About Me
View Derick Bailey's profile on LinkedIn

Send mail to the author(s) Contact Me
Archive
<April 2008>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Derick Bailey
Sign In
Statistics
Total Posts: 115
This Year: 0
This Month: 0
This Week: 0
Comments: 44
Themes
Pick a theme:
All Content © 2012, Derick Bailey
DasBlog theme 'Business' created by Christoph De Baene (delarou)