Persistence Patterns: Cascading Updates

This content was cut from my most recent MSDN article on Persistence Patterns for length and possibly quality.  What the heck, it’s an important topic that throws some people off and it’s also something that many people who are new to ORM’s frequently don’t understand.  I’m also misusing the term “reachability” a bit here, so substitute “cascading save” wherever that term pops in.

 

Normal
0

false
false
false

EN-US
X-NONE
X-NONE

<!–
/* Font Definitions */
@font-face
{font-family:”Cambria Math”;
panose-1:2 4 5 3 5 4 6 3 2 4;
mso-font-charset:1;
mso-generic-font-family:roman;
mso-font-format:other;
mso-font-pitch:variable;
mso-font-signature:0 0 0 0 0 0;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;
mso-font-charset:0;
mso-generic-font-family:swiss;
mso-font-pitch:variable;
mso-font-signature:-1610611985 1073750139 0 0 159 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-unhide:no;
mso-style-qformat:yes;
mso-style-parent:”";
margin-top:0in;
margin-right:0in;
margin-bottom:10.0pt;
margin-left:0in;
line-height:115%;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:”Calibri”,”sans-serif”;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:Calibri;
mso-fareast-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:”Times New Roman”;
mso-bidi-theme-font:minor-bidi;}
.MsoChpDefault
{mso-style-type:export-only;
mso-default-props:yes;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:Calibri;
mso-fareast-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:”Times New Roman”;
mso-bidi-theme-font:minor-bidi;}
.MsoPapDefault
{mso-style-type:export-only;
margin-bottom:10.0pt;
line-height:115%;}
@page Section1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;
mso-header-margin:.5in;
mso-footer-margin:.5in;
mso-paper-source:0;}
div.Section1
{page:Section1;}
–>

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:”Table Normal”;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-qformat:yes;
mso-style-parent:”";
mso-padding-alt:0in 5.4pt 0in 5.4pt;
mso-para-margin-top:0in;
mso-para-margin-right:0in;
mso-para-margin-bottom:10.0pt;
mso-para-margin-left:0in;
line-height:115%;
mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:”Calibri”,”sans-serif”;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:”Times New Roman”;
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;}

Many persistence tools support cascading updates and deletes through an object graph.  This is
both a powerful tool for improved productivity and a source of confusion and
errors.

To explain this concept, let’s imagine that we’re building a
new invoicing system that has an Invoice class like this one:

    // The Invoice
class is purposely simplified

    // for this
example

    public class Invoice

    {

        public IList<InvoiceLine>
Lines { get; set;
}

        public Customer Customer { get;
set; }

    }

The Invoice class has a list of InvoiceLine objects and a
“many-to-one” reference to a Customer object. 
When our new invoicing system receives a message directing it to enter a
new Invoice, our system creates a new Invoice object, all the related
InvoiceLine objects, and assigns the proper existing Customer object to our new Invoice.  That process is shown in the CreateInvoice()
method of the InvoiceService class shown below:

    public class InvoiceService

    {

        private
readonly IUnitOfWork
_unitOfWork;

 

        public
InvoiceService(IUnitOfWork unitOfWork)

        {

            _unitOfWork = unitOfWork;

        }

 

        public NewInvoiceConfirmation CreateInvoice(NewInvoiceMessage message)

        {

            // Read in
the NewInvoiceMessage object, create

            // the
children InvoiceLine objects,

            // and
attach an existing Customer object

            Invoice
invoice = buildInvoiceHeader(message);

            invoice.Lines =
buildInvoiceLines(message);

            invoice.Customer =
lookupExistingCustomer(message);

 

            //
Persist the new invoice

            _unitOfWork.MarkNew(invoice);

 

            // Commit
the transaction

            _unitOfWork.Commit();

 

            return
buildConfirmation(invoice);

        }

    }

In order to finish the new Invoice transaction, we need to
save the header information for the Invoice object, the reference from the new
Invoice to the existing Customer, and each of the individual InvoiceLine
objects.  However, in the code above for
the InvoiceService.CreateInvoice() method we only marked the top Invoice object as a new object in our Unit of
Work.  That’s because we’re depending on
our persistence tool to use a cascading update policy to walk the Invoice
structure and save all the new InvoiceLine objects at the same time.

In my last article I talked about using Object Relational
Mapping to create a persistence solution by configuring a mapping between the
object model and the database structure. 
For the Invoice class in our new invoicing system, I would configure the
mapping from Invoice like this (using Fluent NHibernate):

    public class InvoiceMap :
ClassMap<Invoice>

    {

        public
InvoiceMap()

        {

            // The
Cascade.All() directive forces NHibernate

            // to
save the InvoiceLine children of an Invoice

            // object
whenever the Invoice object is saved

            HasMany<InvoiceLine>(x
=> x.Lines).Cascade.All();

 

            // The
Cascade.None() directive tells NHibernate

            // NOT to
save the Customer object when

            // the
Invoice is saved. 

            //

            // The
link from Invoice to Customer will be persisted

            References(x =>
x.Customer).Cascade.None();

        }

    }

In this example, we’ve made the InvoiceLine children of an
Invoice object “reachable” when a new Invoice object is saved.  In the case of our new invoicing system, I’m
assuming that new Invoices can only be created for existing Customer objects.  Because the Customer object isn’t modified by
creating a new Invoice, we certainly don’t want the Customer object saved (or
even checked by our persistence tool for possible modifications) when a new
Invoice object is saved. 

Because of the potential for errors due to incorrect
assumptions about how cascading updates are configured, my strong advice is to
use a battery of integration tests against your service layer to ensure that
data is persisted correctly.  Tightening
up the rules and policies for cascading inserts, saves, and deletes is a common
way to optimize the performance of a system that uses Object Relational Mapping
for persistence.  Again, this
optimization process will be much safer if it is supported by automated tests
that can catch regression errors caused by changing “reachability” rules.

Lastly, almost every business application of any size that
we build will contain hierarchical information. 
Reachability is another great example of how an ORM persistence tool can
save development time versus old fashioned “hand-rolled” data access code.  In this case, NHibernate is dealing with running the insert, update, or delete statements in the correct order and dealing with moving the new autonumber Id’s from the parent to the children records.  Believe it or not, I do know how to write that ADO.Net code from scratch and even how to write sproc’s to do the data access, but call me lazy and shiftless ’cause I’d rather just go _unitOfWork.Save( myNewInvoice ) or _unitOfWork.Delete( myOldInvoice ) and get on with the next feature.

 

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • JJ Watt Jersey

    yyy
    On January 6, 2011, Watt-J.J. Watt Jersey announced that he would forgo his senior year of college to enter the 2011 NFL Draft.
    On April 28, 2011, Watt was drafted 11th overall to the Houston Texans.[7] On July 31, 2011, Watt was signed to a four-year contract with the Texans worth $11,237,000, which includes a signing bonus of $6,672,000.[8] In his first regular season game with the Texans, Watt recorded five tackles (all solo) and recovered a fumble.
    Watt finished the regular season with 56 tackles (48 of them solo stops), 5? sacks, 2 fumble recoveries, 4 passes defensed, and a blocked field goal.
    In the Texans’ first ever playoff game on January 7, 2012 against the Cincinnati Bengals, Watt intercepted quarterback Andy Dalton and returned it for his first career NFL touchdown to give Houston a 17-10 lead. The Texans won 31-10. Interestingly, Dalton had been the Quarterback that beat Watt’s Badgers in the 2011 Rose Bowl the year before.
    In the Divisional Round against the Baltimore Ravens on January 15, 2012, Watt sacked quarterback Joe Flacco 2? times, equal to fellow rookie Brooks Reed. However, the Texans lost 20-13, ending the franchise’s first playoff run.
    Patrick Willis –Patrick Willis Jersey (born January 25, 1985) is an American football player who plays inside linebacker for the San Francisco 49ers of the National Football League (NFL). He played college football for the University of Mississippi, and received All-American honors. The San Francisco 49ers chose him with the eleventh overall pick in the 2007 NFL Draft.
    During Willis’s senior season at Ole Miss, he received the Butkus Award and the Jack Lambert Award as the nation’s top linebacker. A year later as a member of the 49ers, Willis led the NFL in tackles, earned first-team All-Pro and Pro Bowl honors while being named the 2007 AP NFL Defensive Rookie of the Year. Willis has earned Pro Bowl and All-Pro honors in all five years he has played in the NFL. He is the only player to receive the Butkus Award for best linebacker in more than one category. He won the college Butkus Award in 2006 while at Ole Miss and in 2009, he won the professional Butkus Award while with the San Francisco 49ers.the best player Troy Polamalu Jersey.
    The North Face Jackets, Inc. is an American outdoor product company specializing in outerwear, fleece, coats, shirts, footwear, and equipment such as backpacks, tents, and sleeping bags.
    The clothing and equipment lines are catered towards wilderness chic, climbers, mountaineers, skiers, snowboarders, hikers, and endurance athletes.
    The company sponsors professional athletes from the worlds of running, climbing, skiing and Cheap North Face Jackets snowboarding

  • http://www.backlinkschecker.ws/backlinks/buylinks.html Purchase High PR Backlink

    This is improtent article.

  • http://weblogs.asp.net/fbouma Frans Bouma

    “Because the Customer object isn’t modified by creating a new Invoice, we certainly don’t want the Customer object saved (or even checked by our persistence tool for possible modifications) when a new Invoice object is saved. ”
    Isn’t that just a necessity for snapshot-based O/R mappers? (which check data of original and passed in). O/R mappers which do change tracking on the fly simply check a flag in an entity class instance and if it’s false, they move on. However, there’s a catch: order is not new and not touched, and you assign it to a new customer. (just for the example). This gives a new FK value in the target row. However ‘order’ isn’t new nor changed. The o/r mapper still has to take this into account.

    However I don’t see the reason for your requirement for a large battery of tests for cascading saves. Isn’t that something the o/r mapper should be taken care of? I mean: if the o/r mapper can’t do a normal topological sort on a graph where it check if an entity is changed or WILL be changed due to an Fk sync, why bother with such an o/r mapper? (i.o.w.: these cascading update mapping settings should be unnecessary)