Note that there are some explanatory texts on larger screens.

plurals
  1. POEF, POCO, DB First... how to do Business Logic in Property "set"?
    text
    copied!<p>OK, so I've been building my first large(ish) EF 4.1 POCO + MVC application. It's a replacement of a legacy system so I 'm using an existing database. </p> <p>I've generated my POCO classes using DbContext T4 generation. I've got some really nice forms going on and some really nice validation happening with a lot of sexy generics in my MVC classes to cut down on boiler-plate code... All's good. </p> <p>Suddenly I realized that the most sensible thing (to me) would be for some of business logic to be in the "set" of some of the properties of my POCO objects. </p> <p>E.g. Suppose the following class was generated by the T4;</p> <pre><code>public partial class SalesOrderLine { public int ID { get; set; } public int SalesOrderID { get; set; } public int ProductID { get; set; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public decimal ExtendedPrice { get; set; } public virtual Product Product { get; set; } public virtual SalesOrder SalesOrder { get; set; } } </code></pre> <p>Ignore for a moment the obvious argument that the calculated field "ExtendedPrice" shouldn't even be <em>stored</em> in the database, and just come along with me for the ride...</p> <p>...then, it seems to me, logically, if this object is really supposed to represent a Sales Order Line, that I should be able to construct my object such that the following unit test will work:</p> <pre><code>SalesOrderLine sol = new SalesOrderLine(); sol.UnitPrice = 100; sol.Quantity = 5; Assert.IsEqual(sol.ExtendedPrice, 500); </code></pre> <p>...obviously I can't do that as long as I want the base POCO to be generated by the T4. It seems to me I have several options:</p> <ol> <li><p>Set the generated code file's properties "do not compile", copy and paste the generated code into another file and modify the "set" to do the business logic of setting the extended price when the UnitPrice or Quantity is set. The downside here is that the logic will be run whenever an object is loaded from the database (since the EF will set the public properties and not my private fields). Additionally, this object will then need to be maintained manually for the rest of the life of the project when database changes occur.</p></li> <li><p>Create an UpdateTotals function that gets called in the Validate routine that I have for my object, which gets called by the SaveChanges() on the DbContext. Obviously, the above Unit Test above would not work in that case. The system, and my integration tests however would work and would only call the code when a change was done to the object.</p></li> <li><p>Decide that I'm asking the wrong question, and that I should really add methods to the object called "SetPrice" and "SetQuantity", and then qualify the set accessors of the UnitPrice and Quantity to be "internal". The downside here is that MVC will try and update the model from the form and won't be able to set those properties.</p></li> <li><p>Some solution that involves downloading two or three more frameworks that create even more levels of abstraction than I already have... A repository pattern, or "use NHibernate" or something like that... You can suggest this, but I'm growing weary of how much work it is to set things up to do it the "academically correct" way. For this project, I'd rather meet halfway on the long-term-maintainability vs. speed-of-development spectrum and not over-complicate my project with a ton of extra tools and dlls... ...but I'll try an keep an open mind :)</p></li> </ol> <p><strong>--- EDIT: another idea ---</strong></p> <p>[5.] Another thought, since the fields are always simply calculated there should really be no need to ever set them - either from the database or otherwise. Therefore, something like this might work:</p> <pre><code> public decimal ExtendedAmount { get { return UnitPrice * Quantity; } internal set { } } </code></pre> <p>...my thought is that the EF instantiation would attempt to call the "set", but the set would do nothing, then, when the object was saved or checked for changes it would call the 'get' and that would return the calculated value and that value would get stored in the DB. The only downside here is when you were trying to use the object model to validate the database when the database had in incorrect value stored in the ExtendedAmount field. It's a little hokie, I know, but I thought it would be an interesting trick... in fact the "set" could perhaps throw an exception if (value != UnitPrice * Quantity)</p> <p><strong>--- END EDIT ---</strong></p> <p>I'm curious to hear what other have done in these kinds of cases, as I'm sure it's common. Seems like a lot of the tutorials take you as far as "generating POCO classes from the database", and then leave the rest of the project development up to you.</p> <p>Cheers, Chris</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload