- The View
- only post the key fields and changed fields
- The controller action
- transform the post into a DTO and ensure key is set
- validate changed fields
- The domain layer
- accept the DTO and the list of changed properties
- validate all business logic
- The Service layer
- accept the DTO
- update ONLY the changed columns.
- without fetching a new copy of the object
Goal completed.
1. In the View every input has a wrapper div marked with `data-original="@Model.oldValue"`
OnClick for the save button(s): javascript marks all input fields that have changed to `disabled='disabled'`
2. In the controller action the params required are simply the key fields. I new-up a DTO, and TryUpdateModel on it. For some reason I had to manually set the key property.
3. Domain layer is a simple pass through for now, no business logic is present yet.
4. Service Layer takes in the DTO (as an interface), maps it into a concrete type, and iterates the list of changed property names, setting the ObjectStateManager to modified for each property.
1. For Instance:
<div class="editor-label"> @Html.LabelFor(model => model.Threshold3) </div> <div class="editor-field" data-original="@Model.Threshold3"> @Html.EditorFor(model => model.Threshold3) @Html.ValidationMessageFor(model => model.Threshold3) </div> <p> <input type="submit" value="Save" /> </p>
and
<script type="text/javascript"> $(document).ready(function () { $('input[type="submit"]').on('click', function () { $('.editor-field').each(function (index,e) { var input = $('input', e); var old = $(e).attr('data-original'); if (typeof (old) !== 'undefined' && old !== false && old == $(input).val()) { input.attr('disabled', 'disabled'); } }); }); }); </script>
More after the break...
2.public virtual ActionResult DealActivityEdit(long id) { ViewBag.Edit = true; if (Request.HttpMethod == "POST") return DealActivityEditPost(id); var model = dealActivity.Find(f => f.ID == id).FirstOrDefault(); return View(MVC.EF.Views.DealActivityCreate, model); } ActionResult DealActivityEditPost(long id) { var posted=new DealActivityDTO(); TryUpdateModel(posted); posted.ID = id; var keys=Request.Form.Keys.Cast<string>().Distinct(); this.dealActivity.Update(posted, keys); //this.dealActivityUpdates.Update(Tuple.Create(model.ID),model); return Content("Would have edited deal activity " + id.ToString()); }
3. Pass through not yet created, I went straight to the service layer for testing =/
4.
/// <summary> /// Update /// </summary> IDealActivity Core.Behaviors.IRepository<IDealActivity>.Update(IDealActivity item, IEnumerable<string> propertyList) { var concrete=Core.SyntaxSugar.Helpers.MapUsingSerializer<DealActivity>(item); this.DealActivities.Attach(concrete); foreach(var p in propertyList) ObjectStateManager.GetObjectStateEntry(concrete).SetModifiedProperty(p); this.SaveChanges(); return concrete; }
Thoughts:
The post needs to be Ajax so that if the post fails, the user doesn't lose anything, and we don't have to return a new copy of the item from the database for editing. Also, after the post to the client is done, we're re-inflating to a DTO, which is a problem for serialization and transfer to other layers, unless we use JSON for that then all the empties don't have to be included.
Other Helpers:
- Ninject
- T4 all over
- T4Mvc
- Ef's .tt file modifications
- My own T4 DTO generator which copies attributes down to the DTO from the interface
- Static Reflection
- Query Interception
- Rewrites the queries from the domain into EF friendly types
- For Example:
/// <summary> /// Class to enable business logic to query a source without knowing implementation of the source /// </summary> [System.CodeDom.Compiler.GeneratedCode("T4","1.0.0.0")] IQueryable<IDealActivity> Core.Behaviors.IRepository<IDealActivity>.Query() { var generalExpWriter=new ExpressionWriter(new DebugTextWriter()); var provider=InterceptingProvider.Intercept(new ExpressionWriter(new DebugTextWriter("Underlying IDealActivity")), this.DealActivities.Select<DealActivity,IDealActivity>(c=>c), new ExpressionWriter(new DebugTextWriter()),_TypeChangeVisitor ,new ExpressionWriter(new DebugTextWriter())); return provider; }
public interface IRepository<T> where T : class { IQueryable<T> Query(); IEnumerable<T> Find(Expression<Func<T, bool>> where); T Create(T item); void Delete(T item); T Update(T item, IEnumerable<string> modifiedList); }
No comments:
Post a Comment