Thursday, November 8, 2012

ISD Code Customization Tips, Part II: Reuse Code Snippet with Page Extension Method

Introduction

Have you ever found yourself writing the same piece of code again and again on a highly costomized page? For example, on a page with a master record control and many detail table controls, you need to access a field in the master record from the detail tables. This is what the code snippet may look like,


Even though the repeated code is just two lines, it could be a maintenance headache. If the field is renamed, or its control type is switched from TextBox to DropDownList, there will be multiple places to be modified.

Here is the tip: Put the code snippet in a page extension method, so that it can be reused.

Solution

You may define the extension method in the control cs or vb file, below the section 1 region, and above all ISD-generated control classes. Below is an example of C# version,

Using the extension method could not be simpler.

Conclusion

Reusing code snippet with page extension methods makes your code easier to read and maintain. Because now you only need to modify one place for renaming or switching control type.

Sunday, June 24, 2012

ISD Code Customization Tips, Part I: Post-Transaction Customization

Introduction

There are two ways to extend ISD-generated base methods:
  1. Call the base method, and insert custom code before or after the call.
  2. Cut and paste base method's code, and modify the pasted code.
ISD makes it easier to use the second approach, because the cut-and-paste is done automatically. From code maintenance point of view, however, the first approach is preferred. This is because, 
  1. Custom code is not mixed with ISD-generated code, making it easier to understand and maintain.
  2. Any changes in the base method are automatically propagated to the override method in the first approach. While in the second approach, you have to redo the customization. It is VERY easy to forget to redo the customization, and result in bugs.
However, it is not always possible to go with the first approach. Sometimes you have to insert your custom code into the middle of ISD code. For some of those cases related to transactions, I have a few tips to offer.

Tip #1: Post-Transaction Customization

It is quite common to add post-transaction customization. For example, you want to send out email notifications only if data is saved into database successfully. You cannot insert your code after calling SaveButton_Click_Base like this, You may think that you have to cut-and-paste all code from SaveButton_Click_Base, and then insert your code right after this.CommitTransaction(sender). Here is the tip. You can avoid cut-and-paste by overriding CommitTransaction method.

Conclusion

Compare and see how the 2nd example greatly simplify the customization, making it much easier to maintain.

Thursday, May 24, 2012

jQuery Deferred Objects Promise Callbacks

jQuery Deferred/Promise objects untangle spaghetti-like asynchronous callbacks.

Introduction

Introduced in jQuery 1.5, Deferred/Promise type is an implementation of CommonJS Promise/A interface. The new types change the way asynchronous methods are called. For example, $.ajax() now returns a Promise object, to which you can attach callbacks. Prior to version 1.5, you make ajax calls like this,
Since version 1.5, you can rewrite the above code like this,
So what's the big deal? Isn't it just another syntax sugar for consistency with jQuery's fluent style? No, it is not a simple syntax change. If it were just for the fluent syntax, it should have been introduced since version 1.0. It is a paradigm shift in asynchronous programming.

I am not going to repeat or rephrase the definition of Deferred/Promise, nor give syntactic usage examples out of context. You can get them from jQuery website, Deferred/Promise. If you are still in the dark after reading the official documentation, then try the following explanation within the context of asynchronous code examples. More specifically, I will show you how asynchronous callbacks make a simple for loop and an if…else branch like spaghetti, and how Deferred/Promise objects untangle the spaghetti code.

Loop example

The example repeats 5 tasks sequentially in a loop, and reports final result after the loop. Let's first look at its synchronous implementation. Although dead simple, the example shows a clear-cut separation between the work logic and the control logic. The worker function, doTask(), does not know or care anything about how it is called; while the main function focuses on execution flow.

Now let's make the worker function asynchronous.
The asynchronous version of doTask() initiates a task, and returns immediately. The task will finish after a random delay. The asynchronicity distorts execution flow. The execution flow does not follow code flow any more. The final report is always executed first; and then the 5 steps are finished in random order.

In order to restore the original execution flow using old-fashioned callbacks, the code flow has to be re-structured. There are two options to re-structure the code. The first option is to use nested callbacks: Two observations for nested callbacks:
  1. The worker function is no longer independent of control logic. It needs to know what to do next. 
  2. The main function is not scalable any more. It is trivial to scale the synchronous version to 100 steps or even 1 million steps. Try that on the nested callbacks!
The other option is to use recursive callbacks: Now, the work logic and the control logic are completely mixed together. Although this version is shorter than the nested callback version, it is actually harder to understand what it does.

One way or the other, callbacks break the separation between the work and the control logic, mix them together, and make spaghetti-like code. If we add task dependency into the control logic, callbacks will make the code even more tangled, as shown in the next code example.

Branch example

In this example, the first task (doTask1) returns a random integer between 0 and 10. If the return value is greater than or equal to 5, then task 2 (addTask) is executed; otherwise, task 3 (multiplyTask) is executed. First, let's have a look at the synchronous version. Please note again the separation between the work and the control logic.
Next, let's look at the asynchronous version.
Once again, the work and the control logic are tangled all over the place. Just reading the code, are you able to figure out what it does without the hint from the synchronous version?

Asynchronous functions are so detrimental to code flow due to their inability to return execution results to their callers. When you call a synchronous function, you can say, "hey, worker, this is what you need to do. Just finish your task. Give me the result. I will take care of the rest." The ability to get hold onto return values enables you to control the execution flow, as well as the code flow. In synchronous programming, execution flow is equivalent to code flow. When you call an asynchronous function, you would have to say this. "Hey, worker, this is what you need to do. Since you don't know when the result will be back, I will have to leave some code here for you to decide what to do next. If you get this result, run this piece of code. If you get that result, run that piece of code. If you don't get any result, then run the fail code." Because you can't get hold of the return value at the end of an asynchronous call, you lose control of the execution flow. You also have to break up the code flow to match the tangled execution flow.

Now it's time to see how Deferred/Promise objects can untangle the spaghetti code.

Deferred/Promise untangle spaghetti code

First, the loop example.
Then, the branch example.
Please appreciate how Deferred/Promise objects restore the separation between the work logic and the control logic, making the code very much similar to their synchronous versions.

You can think of a Deferred/Promise object as an on-going process, which you can wrap around an asynchronous function. Now this is what you can say to the worker. "Hey, worker, this is what you need to do. I have created a process for you. Just finish your task in the process. Don't worry about anything else. I will monitor the process, and take care of the rest." The ability to wrap asynchronous code within and then manipulate a Deferred/Promise object gives you back the control of code flow.

Paradigm shift

In old-fashioned callback paradigm, asynchronous methods are void black holes. The first time you call an asynchronous method, you pass the point of no return. You lose the control of code flow, giving it up to the asynchronous method.

In the new paradigm, asynchronous methods return Deferred/Promise objects. A Deferred/Promise object is an abstraction of an ongoing process. You can monitor the status of the process, attach callbacks to be executed when the process is done or failed, or chain another process to the current process. Most importantly, an asynchronous method returns code flow to its calling method, so you are still in control.

Saturday, February 11, 2012

Intercept Postbacks

Introduction

Why intercept postbacks? Here is a use case. Your users ask for an Excel-like page to edit multiple records at the same time. No big deal! ISD's EditTable page looks just like an Excel sheet out of the box. You can implement the page in less than 5 minutes with minimal customization. However, there is a tricky issue. If a user pages/sorts/filters the table without saving existing changes, any unsaved changes will lost. It is not an acceptable solution to educate your users to always click the Save button before paging/sorting/filtering. There is no question that you have to save changes automatically. The question is how to implement it.

Brute-Force Implementation

A brute-force implementation is to add code to all event handlers of paging/sorting/filtering. For example,



Repeat this 4 times for PreviousPage, FirstPage, LastPage, and PageSizeButton. There are 10 sortable columns. Repeat 10 more times on the column headers' click event handlers. There are 5 filters above the table control. Repeat another 5 times. Even though you can refactor the try block into a method, so that it is reusable in the event handlers, there are still a lot of boilerplate code.

Better Implementation

A better implementation is to intercept a postback earlier in page load event, and check which control initiates the postback. If it is one of the above mentioned controls, execute SaveData(). Otherwise, return execution to normal life cycle.


Conclusion

If you find yourself doing the same thing again and again in many postback events, try intercepting the postbacks.

Sunday, January 29, 2012

ASP.NET MVC Dynamic Model Binding

Published at: http://dotnetslackers.com/articles/aspnet/ASP-NET-MVC-Dynamic-Model-Binding.aspx

Correction

CustomModelBinder definition:
public class PetBinderAttribute : CustomModelBinderAttribute {

  public override IModelBinder GetBinder() {
    return new PetModelBinder();
  }

  public class PetModelBinder : DefaultModelBinder {
    public override object BindModel(ControllerContext controllerContext, 
      ModelBindingContext bindingContext) {
      var type = controllerContext.HttpContext.Request.Form["PetType"];
      bindingContext.ModelName = type;
      bindingContext.ModelMetadata = ModelMetadataProviders
        .Current.GetMetadataForType(null, petMap[type]);
      return base.BindModel(controllerContext, bindingContext);
    }
    
    static Dictionary<string, Type> petMap = new Dictionary<string, Type>{
      {"Dog", typeof(Dog)},
      {"Cat", typeof(Cat)},
      {"Fish", typeof(Fish)}
    };
  }
}


Use the CustomModelBinder:
[HttpPost]
public ActionResult Create([PetBinder]IPet pet) {
  // BusinessLogic.Save(pet)
  return View();
}