tag:blogger.com,1999:blog-59275076380552131912024-03-14T08:47:49.566+02:00Thoughts about programming methodologiesIdeas, designs and solutions with explanations and examples.Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.comBlogger9125tag:blogger.com,1999:blog-5927507638055213191.post-87276020019830092442008-12-29T20:18:00.001+02:002008-12-29T20:30:24.153+02:00Thoughts about SOA and beyond<h2><em>Preface</em></h2> <p>Every serious framework has its Collection classes. Why? Because there is no application that does not need List or Hashtable, thus framework designers thought about that and provided a ready implementation, so we can focus on our application needs instead of writing our Lists and Hashtables over and over again. </p> <p>Yep, thanks a lot. Now how many times have you defined your own fresh implementation for Customer or Order? </p> <h2><u>A word about SOA</u></h2> <p>SOA is about contracts. Of course, there is a famous <a href="http://msdn.microsoft.com/en-us/library/aa480190.aspx" target="_blank">ABC</a> - Address-Binding-Contract, but "AB" are mostly "solved" by tools, meaning we don't waste time to provide them. Most of our time is spent on contracts, i.e. creating the required classes, getting the relevant data and filling it in, sometimes with other services and other times with our business logic and finally calling the required operation. Actually calling the operation is the 'finally' thing, the time was spent on <strong>creating the required classes, getting the relevant data and filling it in</strong>. </p> <p>Why does it happen, why the tools do not help us? Because they do not know how! They 'see' those classes as property bags, they know what are the low level basic types of those properties, but they know <strong>nothing</strong> about their <strong>applicative</strong> meaning. So we spend best years of our lives to mapping between different types, yet very common in principle, but invented by us or different vendors.</p> <h2><u>Can you explain more?</u></h2> <p>Yes, of course. Frameworks did a great job in helping us to describe <em>how</em> my object is <em>passed</em> between the services. Well, thanks, but I also need some help to <em>use</em> that object. I mean that if I have a Person object and a Phone service, I want to be able to give this Phone service my Person and the <em>tools </em>will get the phone # from the person and pass it to the service. Or even more, I want to feed my grid with Persons and Phone service. The grid should understand that one of the Person fields is actually her phone #, render it as a link that when clicked calls the Phone service! And if this field is email and the service is Mail service, the click will open a 'new mail' window or start a chat or whatever, depending on the field and service in use. </p> <p>As an application developer I need only to tell where are the 'Persons' and where are the services, <em>all the rest should be done by tools, visual tools</em>.</p> <p> </p> <h2><u>Wow, what do you need to get it live?</u></h2> <p>Actually much, but not too much. I need a common <em>application</em> object schema. This schema should define what is phone #, what is email and what is person that contains email, phone # and other information. This schema should not cover everything, it should provide only the common denominator around which the majority of our lives revolves. Just consider how much services we would be able to build/consume if we just had Person with its contacts (also Persons) with Address, email and may be some ID. Half of Google applications are built just out of this! And of course this schema must be publicly available, reasonable extensible etc, so every vendor will be able to extend it for his own needs; hopefully opening it for public too!</p> <p>Interestingly that an effort to create this kind of schema was started, just did not get enough attention. It was a part of LDAP specification, see <a href="http://tools.ietf.org/html/rfc4519" target="_blank">rfc4519</a> or <a title="X.500 Schema" href="http://tools.ietf.org/html/rfc4524" target="_blank">rfc4524</a>. Most of the examples I talked about can be created with elements defined there! Of course we don't have to evolve LDAP specification, but this can be a good start: standard concept, description language and even good, ready elements and many existing tools.</p> <p> </p> <h2><u>OK, you got your schema. Describe your perfect world!</u></h2> <p>Suppose we have a reasonable big number of common types.</p> <ul> <li>There will be a substantially big number of services 'working' with those types. It will be clear to the vendors what API the world expects. The leading platforms, like .Net, will probably provide some basic services, like persistence, out of the box. </li> <li>It will be much easier to consume those services, since they will fit each other. </li> <li>Workflow engines and their complementary graphical designers will be able to manipulate on a much higher level of abstraction: there can be a concept of Person, Customer, Order etc as basic types with corresponding graphical representation. Probably many services will be also able to identify themselves and get correct glyphs. </li> <li>GUI controls will also upgrade and operate with higher level objects. They will be also able to take service references as parameters to get their work done! </li> <li>And finally every platform will provide a class library with those common types implemented and having many useful methods. Those types will be really first class citizens in the SOA world and application developers will need only extend them, if required. Their learning capabilities and efficiency will also increase since there will be much more common in this world! </li> </ul> <p> </p> <p>This is how I would like it to be, and very interested to hear you opinion.</p> <p>Please leave your comments!</p> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0tag:blogger.com,1999:blog-5927507638055213191.post-6639287526696256502008-07-07T20:59:00.001+03:002008-07-07T20:59:31.792+03:00Method Hiding - Feature or Bug in Compiler?Since its very beginning C# has a 'method hiding' feature, which lets us 'hide' base class non-virtual methods. In other words one can write: <pre class="csharpcode"><span class="kwrd">class</span> Base<br />{<br /> <span class="kwrd">public</span> <span class="kwrd">void</span> Test() {<br /> Console.WriteLine(<span class="str">"Base"</span>);<br /> }<br />}<br /><br /><span class="kwrd">class</span> Derived : Base<br />{<br /> <span class="kwrd">public</span> <span class="kwrd">new</span> <span class="kwrd">void</span> Test() {<br /> Console.WriteLine(<span class="str">"Derived"</span>);<br /> }<br />}<br /><br /><span class="rem">//...</span><br /><br />Derived d = <span class="kwrd">new</span> Derived();<br />d.Test(); <span class="rem">// "Derived" is printed</span></pre><br /><br /><p>So far so good, but what happens if I write:</p><br /><br /><pre class="csharpcode">Base d = <span class="kwrd">new</span> Derived();<br />d.Test(); <span class="rem">// "Base" is printed</span></pre><br /><br /><p>Is this an intended behavior? Well, may be yes, but may be no. In case of 'no', we just called a wrong function, and no warning or error was issued by the compiler... Just consider again what happened, we declared a non-virtual method - that's our contract that should be ensured by the compiler. Instead it provides us with a feature to break it.</p><br /><br /><p>That's not all, consider the situation where you have some 3rd party library and use it in your project. Now the vendor releases a new version where he hides some method and provides an alternative implementation. Your program will still call the old method until you recompile! So in case you have several modules some recompiled and some not, you may have inconsistent behavior...</p><br /><br /><p>Of course you can always find use-cases, where the feature is carefully used and actually is very useful in that context. Yet its disadvantages outweight the advantages, so I think it would be better if we will develop our programs without using it.</p><br /><br /><p>Unfortunately there is an additional very similar "feature" - private interfaces, which can be implemented by a type even if its base class already implemented it and declared it sealed. Again the language helps us to break the contract and if it's not done carefully enough, the bugs won't wait to come...</p> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0tag:blogger.com,1999:blog-5927507638055213191.post-41413203440004972342008-07-04T09:46:00.001+03:002008-07-04T11:24:51.893+03:00C# Co[ntra]variance<p>To get introduced to the topic, please read <a href="http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx">Eric Lippert's variance series</a> first.</p> <p>Here I would like to present my solution to the problem.</p> <p>After all there are only 3 possibilites:</p> <ol> <li>Clear covariance (IEnumerator<T><t>). </li> <li>Clear contravariance (IComparer<T><t>). </li> <li>Undecidable (IList<T><t>). </li> </ol> <p>All the 3 possibility are easily detectable by compiler and I believe for the first two we would like the compiler will decide the variance automatically. We need a solution for the third case only.</p> <p>To see the solution, I would like first analyze how we selected which case the given interface belongs to. We analyzed the method signatures in which T is involved: </p> <ol> <li>If in all of them T is 'out' parameter, the interface is covariant. </li> <li>If in all of them T is 'in' parameter, the interface is contravariant. </li> <li>If there is a mix - undecidable. </li> </ol> <p>My solution is to say the compiler how I'm going to use my variable and enable either covariant or contravariant invocations:</p> <pre class="csharpcode">IList<Animal> aa = whatever;<br />IList<<span class="kwrd">in</span> Giraffe> ag = aa;<br />IList<<span class="kwrd">out</span> Giraffe> agX = aa; <span class="rem">//fails to compile</span></pre><br />ag user sees: 'int ag.IndexOf(Giraffe)' and 'object ag[int]'. <br /><br /><pre class="csharpcode">IList<Giraffe> ag = whatever;<br />IList<<span class="kwrd">out</span> Animal> aa = ag;<br />IList<<span class="kwrd">in</span> Animal> aaX = ag; <span class="rem">//fails to compile</span></pre><br />aa user sees: 'int aa.IndexOf(<only null can be passed><only passed be can null>)' and 'Animal aa[int]'. <br /><br /><p>Now I can say in a type safe manner:</p><br /><br /><pre class="csharpcode"><span class="kwrd">class</span> X<T> {<br /><br /> T _t;<br /><br /> <span class="kwrd">void</span> Test(IList<<span class="kwrd">out</span> T> at) {<br /> T t = at[0]; <span class="rem">//clearly t is 'out' parameter</span><br /> }<br /><br /> <span class="kwrd">void</span> Test(IList<<span class="kwrd">in</span> T> at) { <span class="rem">//overloaded method!</span><br /> <span class="kwrd">int</span> i = at.IndexOf(_t); <span class="rem">//clearly _t is 'in' parameter</span><br /> }<br />}<br /><br /><span class="rem">//...</span><br /><br /><span class="kwrd">new</span> X<Animal>().Test(<span class="kwrd">new</span> List<Giraffe>()); <span class="rem">//first method is called</span><br /><span class="kwrd">new</span> X<Giraffe>().Test(<span class="kwrd">new</span> List<Animal>()); <span class="rem">//second method is called</span></pre> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0tag:blogger.com,1999:blog-5927507638055213191.post-82639887981921274422008-06-16T21:10:00.001+03:002008-06-16T21:24:59.711+03:00Domain Specific Language (DSL) with Workflow, Part 2<p>In the <a href="http://ktriger.blogspot.com/2008/06/domain-specific-language-dsl-with.html">previous article</a> I presented some basic ideas for creating DSL activities in Workflow. In this part I want to deep into the challenges I faced during the <a href="http://kostat.googlepages.com/CS.zip">implementation</a> and share the solutions. As usual, let's start from the requirements:</p> <ol> <li>DSL is a set of services, each one implemented by a separate function. </li> <li>For each such function provide a specialized Activity. </li> <li>All the return/out/ref parameters should work as expected. </li> <li>Developing of those Activities should be simple and provide Activity validation. </li> </ol> <p>The <strong>first</strong> requirement is easy - I can have a separate method for each DSL service in my ExternalDataExchange interface:</p> <pre class="csharpcode"><pre class="alt"><span class="rem">// The ExternalDataExchange attribute is a required attribute </span><br /><span class="rem">// indicating that the local service participates in data exchange with a workflow</span><br />[ExternalDataExchange]<br /><span class="kwrd">public</span> <span class="kwrd">interface</span> ITaskService<br />{<br /> <span class="kwrd">int</span> CreateTask(<span class="kwrd">string</span> taskId, <span class="kwrd">string</span> assignee, <span class="kwrd">ref</span> <span class="kwrd">string</span> text);<br /><br /> <span class="rem">//...</span><br />}</pre></pre><br /><br /><p>To meet the next requirements I wanted to provide a base class, that by simple derivation from it and decorating my properties with some attribute, the desired DSL activity will be created:</p><br /><br /><pre class="csharpcode"><pre class="alt">[ToolboxItemAttribute(<span class="kwrd">typeof</span>(ActivityToolboxItem))]<br /><span class="kwrd">public</span> <span class="kwrd">class</span> CreateTask : DslActivity<CreateTask><br />{<br /><span class="rem">// Properties on the task</span><br /><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> DependencyProperty ReturnValueProperty = DependencyProperty.Register(<span class="str">"ReturnValue"</span>, <span class="kwrd">typeof</span>(System.Int32), <span class="kwrd">typeof</span>(CreateTask));<br /><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> DependencyProperty AssigneeProperty = DependencyProperty.Register(<span class="str">"Assignee"</span>, <span class="kwrd">typeof</span>(System.String), <span class="kwrd">typeof</span>(CreateTask));<br /><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> DependencyProperty TaskIdProperty = DependencyProperty.Register(<span class="str">"TaskId"</span>, <span class="kwrd">typeof</span>(System.String), <span class="kwrd">typeof</span>(CreateTask));<br /><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> DependencyProperty TextProperty = DependencyProperty.Register(<span class="str">"Text"</span>, <span class="kwrd">typeof</span>(System.String), <span class="kwrd">typeof</span>(CreateTask));<br /><br /><span class="kwrd">public</span> CreateTask() <br /><span class="rem">// Map the activity to the service method</span><br />: <span class="kwrd">base</span>(<span class="kwrd">typeof</span>(ITaskService), <span class="str">"CreateTask"</span>)<br />{<br />}<br /><br /><span class="rem">// Property, mapped to the return value of the method</span><br />[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]<br />[BrowsableAttribute(<span class="kwrd">true</span>)]<br /><span class="rem">// "Well Known" name for the return value</span><br />[DslParameter(Name = <span class="str">"(ReturnValue)"</span>)]<br />[Category(<span class="str">"ReturnValue"</span>)]<br /><span class="kwrd">public</span> <span class="kwrd">int</span> ReturnValue<br />{<br /> get<br /> {<br /> <span class="kwrd">return</span> ((<span class="kwrd">int</span>)(<span class="kwrd">base</span>.GetValue(ReturnValueProperty)));<br /> }<br /> set<br /> {<br /> <span class="kwrd">base</span>.SetValue(ReturnValueProperty, <span class="kwrd">value</span>);<br /> }<br />}<br /><br /><span class="rem">// Property, mapped to the 'assignee' parameter of the method</span><br />[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]<br />[BrowsableAttribute(<span class="kwrd">true</span>)]<br />[DslParameter(Name = <span class="str">"assignee"</span>)]<br />[Category(<span class="str">"Parameters"</span>)]<br /><span class="kwrd">public</span> <span class="kwrd">string</span> Assignee<br />{<br /> get<br /> {<br /> <span class="kwrd">return</span> ((<span class="kwrd">string</span>)(<span class="kwrd">base</span>.GetValue(AssigneeProperty)));<br /> }<br /> set<br /> {<br /> <span class="kwrd">base</span>.SetValue(AssigneeProperty, <span class="kwrd">value</span>);<br /> }<br />}<br /><br /><span class="rem">// Other properties - mappings to the rest method parameters</span></pre></pre><br /><br /><p>In this way development of DSL activities is simple and straightforward. Now let's look into the base class to see how it's achieved. All the magic is done inside the constructor:</p><br /><br /><pre class="csharpcode"><pre class="alt"><span class="rem">// Initializes parameters by reflecting the typeof(T) members</span><br /><span class="rem">// and looking for DslParameterAttribute attribute.</span><br /><span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> IList<ParameterInfo> _parameters = GetParameters(<span class="kwrd">typeof</span>(T));<br /><br /><span class="kwrd">public</span> DslActivity(Type interfaceType, <span class="kwrd">string</span> methodName)<br />{<br /> <span class="rem">// Set up the interface and method</span><br /> InterfaceType = interfaceType;<br /> MethodName = methodName;<br /><br /> <span class="kwrd">for</span> (<span class="kwrd">int</span> i = 0; i < _parameters.Count; i++)<br /> {<br /> ParameterInfo pi = _parameters[i];<br /> <span class="rem">// This is the main 'trick' - create a binding to 'this' Activity</span><br /> <span class="rem">// with path set to the relevant property name</span><br /> ActivityBind bind = <span class="kwrd">new</span> ActivityBind(<span class="str">"/Self"</span>, pi.Path);<br /> WorkflowParameterBinding binding = <span class="kwrd">new</span> WorkflowParameterBinding(pi.ParameterName);<br /> binding.SetBinding(WorkflowParameterBinding.ValueProperty, bind);<br /> ParameterBindings.Add(binding);<br /> }<br />}</pre></pre><br /><br /><p>That's all, hope you enjoyed! Download <a href="http://kostat.googlepages.com/CS.zip">here</a> the sample code for this article.</p> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com1tag:blogger.com,1999:blog-5927507638055213191.post-70167492195874710352008-06-14T23:43:00.001+03:002008-06-14T23:46:00.071+03:00Domain Specific Language (DSL) with Workflow<p>Workflow provides two basic activities for interaction with outer world: CallExternalMethodActivity and HandleExternalEventActivity. But sometimes we want to limit our workflow to not use those, since they are too "generic". Instead, we want to provide our set of <em>Domain Specific</em> activities.</p> <p>The simplest way to achieve this goal is by derivation, providing the required specialization.</p> <p><a href="http://kostat.googlepages.com/CS.zip">Here</a> is a link to a simple solution demonstrating the technique. In this example I create 2 application tasks and wait for completion of any of them. All the work is done with custom activities, defining my DSL.</p> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0tag:blogger.com,1999:blog-5927507638055213191.post-44300388309290090992008-06-03T00:34:00.000+03:002008-06-03T00:36:01.779+03:00Workflow (WF) RuleEngine without WorkflowRule engine is a very useful feature and not always we want the entire Workflow to get into play. <br /> <br />Here is a simple code snippet showing how one can use RuleCondition/RuleSet outside the Workflow scope: <br /> <br /> <pre class="csharpcode">WorkflowMarkupSerializer serializer = <span class="kwrd">new</span> WorkflowMarkupSerializer();<br /><span class="kwrd">using</span> (Stream s = <span class="kwrd">typeof</span>(Program).Assembly.GetManifestResourceStream(<span class="str">"SimplePolicyWorkflow.rules"</span>))<br />{<br /> RuleDefinitions rd = (RuleDefinitions)serializer.Deserialize(XmlReader.Create(s));<br /> RuleCondition c = rd.Conditions[0]; <span class="rem">// or RuleSet</span><br /><br /> <span class="rem">//typeof(rule context) should be passed</span><br /> RuleValidation v = <span class="kwrd">new</span> RuleValidation(<span class="kwrd">typeof</span>(Program), <span class="kwrd">null</span>);<br /><br /> <span class="kwrd">if</span> (c.Validate(v))<br /> {<br /> <span class="rem">//rule context instance should be passed</span><br /> RuleExecution exe = <span class="kwrd">new</span> RuleExecution(v, <span class="kwrd">new</span> Program());<br /> c.Evaluate(exe);<br /> }<br />}</pre><br /><style type="text/css"><br />.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }</style> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0tag:blogger.com,1999:blog-5927507638055213191.post-18410390235737893422008-06-01T00:51:00.001+03:002008-06-01T00:53:23.349+03:00Self Code Review Methodology<p>When I worked as a team leader I didn't want any code to get into the product without the code review in addition to the unit tests added. But good code review takes time, so I started thinking how I can optimize the process...</p> <p>First, I came to a conclusion that a review actually consists of two parts:</p> <ul> <li>Code logic. This is best done by challenging. If the developer quickly and correctly answers to questions, he probably thought about that scenario and covered it. If not, we open the relevant code and check its logic <em>in depth</em>. This gives the opportunity to concentrate on less 'polished' code, analyze it deeper and correct more issues. </li> <li>Code sanity, which includes: <ul> <li>Code format - ensured by IDE. </li> <li>Code style - which actually is a routine check list. Bingo! Why not have this check list written and given to the developers to do themselves to save the reviewer's time? </li> </ul> </li> </ul> <p>Below is the check list I created, please leave your comments...</p> <h5>For each added field </h5> <ol> <li>Consider type safety. I.e. when the field is used, no cast is required. </li> <li>Ensure its access modifier is private. </li> <li>Ensure its value cannot be computed by other means (using other fields/methods). If yes, it means this field is for caching purposes or used by some setter. If it's for caching: <ul> <li>Ensure this caching is required and comment this in code. </li> <li>Ensure it is always in sync with computed value and comment how this is achieved in code.</li> </ul> </li> <li>Consider marking this field 'readonly'. If not possible, consider refactoring resulting this field becomes 'readonly'. </li> </ol> <a href="http://www.google.com/notebook/onpage?client=gnotesff&v=1.0.0.19&zx=1212267493828"></a> <h5>For each added property </h5> <ol> <li>Ensure it has a getter. </li> <li>Consider removing a setter. </li> <li>Ensure set/get parity, i.e. if some value is set, the same one is get. </li> <li>Ensure sequential gets return a same logical value. </li> </ol> <a href="http://www.google.com/notebook/onpage?client=gnotesff&v=1.0.0.19&zx=1212267493828"></a> <h5>For each added property/method </h5> <ol> <li>Consider minimizing its access scope (private static <--> public virtual) </li> <li>Ensure all the arguments are checked and relevant exception are thrown. </li> </ol> <a href="http://www.google.com/notebook/onpage?client=gnotesff&v=1.0.0.19&zx=1212267493828"></a> <h5>For each added class </h5> <ol> <li>Consider minimizing its access scope (private sealed inner class <--> public not sealed). </li> <li>Ensure it has a minimal set of constructors. </li> <li>Consider refactoring to have one constructor with initialization logic and other forwarding to it. If needed, add a private constructor. </li> <li>If there is an override for Equals/Hashcode methods, ensure they come in pair and both are computed from the same data. </li> </ol> <a href="http://www.google.com/notebook/onpage?client=gnotesff&v=1.0.0.19&zx=1212267493828"></a> <h5>For each local variable </h5> <ol> <li>Consider type safety. I.e. when the variable is used, no cast is required. </li> <li>Minimize variable scope. </li> <li>Ensure proper clean up in finally blocks; usually initailization should be right before the try block and not inside it. </li> </ol> <a href="http://www.google.com/notebook/onpage?client=gnotesff&v=1.0.0.19&zx=1212267493828"></a> <h5>For each added line of code </h5> <ol> <li>Consider implicit impacts (boxing, objects creation, computation). </li> <li>Ensure that every System.SystemException derived exception can be possibly thrown is by intention. (NullReference, Cast etc). </li> </ol> <h5>For each 'lock' statement</h5> <ol> <li>Consider using a framework class, which has the required synchronization built-in. For example, if you need a synchronized Hashtable, don't do locking yourself, but create it with Hashtable.Synchronized() / SynchronizedKeyedCollection<K, T>.</li> <li>Otherwise ensure the design is discussed with your manager.</li> </ol> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0tag:blogger.com,1999:blog-5927507638055213191.post-88639022997582120322008-05-28T15:48:00.008+03:002008-12-10T08:49:06.569+02:00MVC Using Workflow (WF) as Model/Controller and ASP.NET as View<h5><a href="http://kostat.googlepages.com/ASP-WF.zip">Download source code for this article</a></h5> <h5>Agenda</h5> <p></p> Since workflow aims to provide an application business logic engine, it seems natural that we should be able to plug to it a presentation technology of our choice. <br /><br />As usual, let's start from the requirements to our solution: <br /> <ol> <li>Total separation between View and Workflow (Model/Controller) logic. We assume that Workflow is provided by an external team. </li> <li>Workflow should be developed with no assumptions on the presentation technology used. </li> <li>The components should be plugged by configuration. </li> </ol> <h5>Workflow definition</h5> <p>We assume it is an event driven state machine. This is how it looks for our example: <br /> <br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_DdoiMKuCx7M/SD5v9xvUskI/AAAAAAAABfs/bTSGAl0vNrU/s1600-h/StateMachine.PNG"><img id="BLOGGER_PHOTO_ID_5205721326401401410" style="margin: 0px auto 10px; display: block; cursor: pointer; text-align: center;" alt="" src="http://4.bp.blogspot.com/_DdoiMKuCx7M/SD5v9xvUskI/AAAAAAAABfs/bTSGAl0vNrU/s320/StateMachine.PNG" border="0" /></a></p> <p>For each State we will create an interface with:</p> <ol> <ol></ol> <li> <p>Initialize() method, which is responsible for state initialization (in our case opening a form to collect data from the user).</p> </li> <li> <p>Several events that can happen in this state (user submits data).</p> </li> <li> <p>SubmitXXX methods, each of them raising a corresponding event.</p> </li> </ol> <p>For example, for a State where we need to collect a customer name we will create a following interface: <br /></p> <pre class="csharpcode">[ExternalDataExchange] <span class="rem">//required for Workflow binding </span><br /><span class="kwrd">public</span> <span class="kwrd">interface</span> IGetNameState<br />{<br /> <span class="kwrd">event</span> EventHandler NameReceived;<br /><br /> <span class="kwrd">void</span> Initialize(Guid instanceId);<br /> <span class="kwrd">void</span> SubmitName(Guid instanceId, <span class="kwrd">string</span> first, <span class="kwrd">string</span> last); <span class="rem">//raises NameReceived event. </span><br />} </pre><style type="text/css"><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; </style><br />At the end of the StateInitializationActivity we invoke the corresponding Initialize() method:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_DdoiMKuCx7M/SD1zpxvUsjI/AAAAAAAABfk/SKszMlAXR3s/s1600-h/StateInit.PNG"><img id="BLOGGER_PHOTO_ID_5205443905873818162" style="margin: 0px auto 10px; display: block; cursor: pointer; text-align: center;" alt="" src="http://3.bp.blogspot.com/_DdoiMKuCx7M/SD1zpxvUsjI/AAAAAAAABfk/SKszMlAXR3s/s320/StateInit.PNG" border="0" /></a><br /><p>to show the appropriate view.<br /><br />Once we are done for each State, we can provide our workflow to the Presentation team.<br /><br /></p><h5>Presentation</h5><p>First we must admit that the controller provided by the Workflow does not show any form. Further, it cannot do it by definition, since it does not know how! Instead it calls a relevant Initialize() method to be implemented by the concrete Controller.<br /><br /><br />That means that we must provide a concrete Controller implementation to perform the actual navigation, i.e: </p><pre class="csharpcode"><span class="kwrd">void</span> IGetNameState.Initialize(Guid instanceId)<br />{<br /> HttpContext.Current.Server.Execute<br /> (<span class="str">"~/GetName.aspx?InstanceId="</span> + instanceId, <span class="kwrd">false</span>);<br />}</pre><p>Later on we will see how we plug this Controller in by configuration.</p><p>Now for each state the correct form is shown to the user, but his actions are not handled. Let's do it:<br /></p><pre class="csharpcode"><span class="kwrd">protected</span> <span class="kwrd">void</span> Button1_Click(<span class="kwrd">object</span> sender, EventArgs e)<br />{<br /> WorkflowRuntime workflowRuntime = (WorkflowRuntime)Context.Application[<span class="str">"WorkflowRuntime"</span>];<br /><br /> workflowRuntime.SubmitName(<br /> <span class="kwrd">new</span> Guid(Request.QueryString[<span class="str">"InstanceId"</span>]),<br /> txtFirstName.Text, txtLastName.Text);<br /><br /> <span class="rem">// Once we call Workflow, it's responsible to route the request </span><br /> Response.End();<br />} </pre><h5>Initialization and Configuration</h5> <ul><li>Initialization:<br /> <br />In the Application_Start handler we initialize the WorkflowRuntime:<br /><br /> <pre class="csharpcode">WorkflowRuntime workflowRuntime = <span class="kwrd">new</span> WorkflowRuntime(<span class="str">"WorkflowRuntime"</span>); <span class="rem">//passing the configuration section name </span><br /><br />workflowRuntime.StartRuntime();<br /><br />Application[<span class="str">"WorkflowRuntime"</span>] = workflowRuntime;</pre> <p>and in Application_End we stop it: </p> <pre class="csharpcode">WorkflowRuntime workflowRuntime = (WorkflowRuntime)Application[<span class="str">"WorkflowRuntime"</span>];<br />workflowRuntime.StopRuntime();</pre> </li></ul><ul><li>Configuration:<br /> <br />Let's configure <span class="html">ExternalDataExchangeWorkflowService</span> with our concrete Controller type:<br /> <pre class="csharpcode"><span class="kwrd"><</span><span class="html">ExternalDataExchangeWorkflowServices</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">Services</span><span class="kwrd">></span><br /> <span class="rem"><!-- The concrete Controller implementation --></span><br /> <span class="kwrd"><</span><span class="html">add</span> <span class="attr">type</span><span class="kwrd">="Workflow.RuntimeServices.Controller, Controller, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3a221384c77ffbe5"</span><span class="kwrd">/></span><br /> <span class="kwrd"></</span><span class="html">Services</span><span class="kwrd">></span><br /><span class="kwrd"></</span><span class="html">ExternalDataExchangeWorkflowServices</span><span class="kwrd">></span><br /><span class="kwrd"><</span><span class="html">WorkflowRuntime</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">Services</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">add</span> <span class="attr">type</span><span class="kwrd">="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"</span><span class="kwrd">/></span><br /> <span class="kwrd"><</span><span class="html">add</span> <span class="attr">type</span><span class="kwrd">="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"</span> <span class="attr">ConfigurationSection</span><span class="kwrd">="ExternalDataExchangeWorkflowServices"</span><span class="kwrd">/></span><br /> <span class="kwrd"></</span><span class="html">Services</span><span class="kwrd">></span><br /><span class="kwrd"></</span><span class="html">WorkflowRuntime</span><span class="kwrd">></span></pre> <h5>Summary</h5> <p>This tutorial helps to build a skeleton implementation of ASP.NET front-end for Workflow Controller. In our design we put a special attention on separation and correct interaction between the two. A very important property is that the Workflow does not dictate the implementation to the Presentation layer, its work can be performed in one or several steps. Only when all the information required by the Workflow is collected, it is invoked. For example, to collect the user full name, consisting of first and last names, the presentation may open one or two forms; provide any validation logic it wants and only at the end Submit the information. Such approach separates the Big application flow from the Presentation, opening a possibility to customize/update/change technology/upgrade it in any possible way.</p><br /> </li><br /></ul>Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0tag:blogger.com,1999:blog-5927507638055213191.post-4150732110908674162008-05-24T00:16:00.001+03:002008-05-24T00:41:56.073+03:00ASP.Net Unit Testing<p>Some tell there are many solid solutions by HP (Mercury) or Microsoft's TeamSystem, some claim that all those are not Unit, but Integrative tests. And there are many that mostly perform manual testing... Who is right and what is the best approach? <br /> <br />As usual, let's start from the requirements from the testing framework.</p> <ol> <li> Granularity. We are talking about unit testing, the name suggests that we want to test application units, separately.</li> <li> Ability to automate. Definitely we want an ability to run the tests automatically, for example after a build. This requirement also yields a requirement for results stability through the application life cycle.</li> <li>Coverage. Every part that is not covered by a unit test joins the risk group of deferred bugs. Even if there is integrative testing, it probably does not run frequently and therefore our control over correct system status reduces when the un-unit-tested group grows.</li> </ol> <p>Let's partition our application and see how we can test every part.</p> <ol> <li>Libraries, that do not require any "web context". Here we can simply write plain unit tests. No problems.</li> <li>Custom and User controls. In fact, those are also libraries, providing a well defined API, that can be systematically tested... But how to do it if they require "web context"?</li> <li>Pages, pages, pages, which actually produce our application on the server side and...</li> <li>Client side logic - let's not forget all the Java Script we run on the client browsers.</li> </ol> <p>Unfortunately most of the "well known" solutions, including those mentioned at the beginning address mainly point 3 above. The common pattern is to record the requests/responses sent/returned by the server; later sent those requests back to the servers, receive the response and validate it again previously recorded one. <br /> <br />This introduces several problems:</p> <ol> <li>Granularity (since 'Custom and User controls' part is handled on the 'Page' level). When the test fails, we know that the Page failed, not some specific control and therefore had to invest more time in regression diagnostics.</li> <li>Ability to automate. Since the page evolves over time, our old recording may become invalid, even if everything is correct. This produces many false alarms, preventing automation.</li> <li>Coverage. Client side logic is left untested.</li> </ol> <p>In other words we have a problem in every aspect of our requirements, what can we do?</p> <ol> <li>Re-introducing test-driven development. Since not every code can be easily tested, we must include its testability into the very beginning of its design phase. The rule of dumb says that testability reduces when we move forward over the following path: 'Simple Library' -> 'Custom/User control' -> 'Page' -> 'Client Side logic'. This means that we must strive to move our application code as 'left' as possible.</li> <li>Create unit tests for our Custom/User controls. We use to create a separate test for every class in 'Simple Library', the same we can do with controls. Once we have a dedicated test page per control, it does not change when the application changes, improving our 'Ability to Automate' over time.</li> <li>Client Side logic testing. Since client logic runs in the browser, in order to test it, it must run in the browser, period. Any other option simply does not do the job. One of the best tools I know to run tests on the browser is <a href="http://selenium.openqa.org/">Selenium</a> - it will be covered in the next article.</li> </ol> <h4>Summary</h4> <ol> <li>Partition your application as described above.</li> <li>Try to move as much code as possible 'up' within the partition.</li> <li>For each User/Custom controls write a dedicated test page to test its functionality.</li> <li>Use some tool for testing client side logic, my recommendation is <a href="http://selenium.openqa.org/">Selenium</a>.</li> <li>If you go with <a href="http://selenium.openqa.org/">Selenium</a> for client side logic, for consistency use it also for Pages testing.</li> </ol> Konstantin Trigerhttp://www.blogger.com/profile/02032160863186530414noreply@blogger.com0