The official Fatica Labs Blog! RSS 2.0
# Wednesday, 29 February 2012

In this post I will show how to make testable something that ( at least me ) usually left as untested. I’m talking about the preparing phase of a console app, the checking arguments error reporting and so on. That logic is usually so simple that any good cow boy programmer would probably leave outside any unit testing. Unfortunately we should at least do some manually check that prove that logic working, and doing things manually is always silly. In this post we assume we have a working command line parsing library, and a mocking framework. Let see the cowboy code:

        static void Main(string[] args)
        {
            string optA,optB;
            optA = optB = null;
            bool done = false;
            OptionSet set = new OptionSet();
            set.Add("a=", (k) => optA = k);
            set.Add("b=", (k) => optB = k);
            set.Add("h", (k) => { LongHelp(); done = true; });
            set.Parse(args);
            if (done)
                return;
            if (string.IsNullOrEmpty(optA) || string.IsNullOrEmpty(optB))
            {
                ShortHelp();
                return;
            }
            DoTheJob(optA,optB);
            
        }

        private static void DoTheJob(string optA, string optB)
        {
            //something interesting here
        }

        private static void LongHelp()
        {
            Console.Error.WriteLine("Long help here...");
        }

        private static void ShortHelp()
        {
            Console.Error.WriteLine("Short help here");
        }
    }

So nothing special, the example is actually very simple, we have two mandatory parameters, a command line switch to print a long help. If one argument is missing a short help line must be presented. If all the parameters are provided, the DoTheJob() method should be called with the correct values.

Current code is not testable without hosting the console application as a process, and looking at the stdout to see what happen. Even by this strategy, we can not punctually check what is passed to DoTheJob. So we want to refactor the code, without adding any complexity to the app. So here below the proposed refactoring:

    public class Program
    {
        static void Main(string[] args)
        {
            new Program().Run(args);
        }
        public virtual void Run(string[] args)
        {
            string optA, optB;
            optA = optB = null;
            bool done = false;
            OptionSet set = new OptionSet();
            set.Add("a=", (k) => optA = k);
            set.Add("b=", (k) => optB = k);
            set.Add("h", (k) => { LongHelp(); done = true; });
            set.Parse(args);
            if (done)
                return;
            if (string.IsNullOrEmpty(optA) || string.IsNullOrEmpty(optB))
            {
                ShortHelp();
                return;
            }
            DoTheJob(optA, optB);

        }

        public virtual void DoTheJob(string optA, string optB)
        {
            //something interesting here
        }

        public virtual void LongHelp()
        {
            Console.Error.WriteLine("Long help here...");
        }

        public virtual void ShortHelp()
        {
            Console.Error.WriteLine("Short help here");
        }
    }

 

So pretty easy, we provide a non static method Run(), and all the internal function are declared virtual. This is a five minutes modification we could probably apply to any other code like this we have. The difference is that we can write some unit test, lets see how:

        [TestMethod]
        public void ShouldDisplayShortHelp()
        {
            var moq = new Mock();
            moq.CallBase = true;
            moq.Setup(k=>k.DoTheJob(It.IsAny(),It.IsAny()))
                .Throws(new InvalidProgramException("Should not call"));
            moq.Object.Run(new string[0]);
            moq.Verify(k => k.ShortHelp());
        }
        [TestMethod]
        public void ShouldDisplayLongHelp()
        {
            var moq = new Mock();
            moq.CallBase = true;
            moq.Setup(k => k.DoTheJob(It.IsAny(), It.IsAny()))
                .Throws(new InvalidProgramException("Should not call"));
            moq.Object.Run(new string[]{"-h"});
            moq.Verify(k => k.LongHelp());
        }
        [TestMethod]
        public void ShouldInvokeWithProperParameters()
        {
            var moq = new Mock();
            moq.CallBase = true;
            moq.Setup(k => k.DoTheJob("p1", "p2")).Verifiable();
            moq.Object.Run(new string[] { "-a=p1","-b=p2" });
            moq.Verify();
        }

 

I used the MoQ library, please note the Callbase set to true, because we are using the same object for driving and for expect calls. So in conclusion, we achieve a real unit test of something we sometimes left apart, we did that in memory, and even if the example is really trivial, the concept can be used in complex scenarios too. What about testing the inside part of DoTheJob()? well, if a good testing strategy is used, the internal part should be testable outside somewhere else, here we are  proving we can test the shell. 

Wednesday, 29 February 2012 21:47:36 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
CodeProject | CSharp | Programmin

# Tuesday, 28 February 2012

This is a post for myself, because I’m much more a cow boy programmer, and I know this is bad, even if sometimes results are apparently good. The leaking part in my code are usually unit tests, and there is no excuse about that, since it is necessary to change the approach on its root, for example writing the (failing) test before the real code. This is actually a design strategy, that eventually improve the reliability of the software and the portability of the software against other developers. The hardest part is that sometimes the boundary between unit testing and integration testing is apparently very thin: making that boundary better defined is part of the design process. I found this document, and I think is very useful to understand the common errors producing a non testable code, and the strategy to avoid them.

Tuesday, 28 February 2012 21:13:39 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
CodeProject | Programmin

# Friday, 12 August 2011

Sometimes happens that we need to roll over all the combinations of elements in multiple arrays. This operation is called Cartesian Product and is defined as:

Definition (Cartesian product): Let A1, ..., An be n sets.

Then the set of all ordered n-tuples <x1, ..., xn> , where xi Ai for all i, 1 i n ,

is called the Cartesian product of A1, ..., An, and is denoted by A1 ... An .

Achieving this is possible by some linq tricks, or by an helper class I propose in this post. But just for clarify let’s start from the example:

We have three array as below:

 { "JUICY", "SWEET" }
{ "GREEN", "YELLOW" }
{ "APPLE", "BANANA", "MANGO" }

we want to obtain all the possible tuples, ie:

 

image

This can be achieved by the following code ( helper class ):

public class CartesianProduct<T>
    {
        int[] lengths;
        T[][] arrays;
        public CartesianProduct(params  T[][] arrays)
        {
            lengths = arrays.Select(k => k.Length).ToArray();
            if (lengths.Any(l => l == 0))
                throw new ArgumentException("Zero lenght array unhandled.");
            this.arrays = arrays;
        }
        public IEnumerable<T[]> Get()
        {
            int[] walk = new int[arrays.Length];
            int x = 0;
            yield return walk.Select(k => arrays[x++][k]).ToArray();
            while (Next(walk))
            {
                x = 0;
                yield return walk.Select(k => arrays[x++][k]).ToArray();
            }

        }
        private bool Next(int[] walk)
        {
            int whoIncrement = 0;
            while (whoIncrement < walk.Length)
            {
                if (walk[whoIncrement] < lengths[whoIncrement] - 1)
                {
                    walk[whoIncrement]++;
                    return true;
                }
                else
                {
                    walk[whoIncrement] = 0;
                    whoIncrement++;
                }
            }
            return false;
        }
    }

 

And, just for completeness, the example application:

 

static void Main(string[] args)
        {
            var cross = new CartesianProduct<string>(
               new string[] { "JUICY", "SWEET" }
               , new string[] { "GREEN", "YELLOW" }
               , new string[] { "APPLE", "BANANA", "MANGO" }
               );

            Console.WriteLine("=========================================================");
            foreach (var item in cross.Get())
            {
                Console.WriteLine("{0}\t{1}\t{2}", item[0], item[1], item[2]);
            }
            Console.WriteLine("=========================================================");
        }
Really simple and clear to use in comparison with other linq based solution, even when arrays are unknown at design time.
Friday, 12 August 2011 11:46:46 (GMT Daylight Time, UTC+01:00)  #    Comments [1] - Trackback
Programmin | Recipes

# Sunday, 24 April 2011

If you are used to works with Winform, you probably feel being less productive in WPF, but this can be overtaken by approaching the design the correct way, and with proper tools productivity would be the same as Winform, and probably better. MVVM seems to be the right pattern to cleanup the scaring and messy stuff polluting the XAML and the code behind on the view. There is some framework to help to do this, and I choose Caliburn  for this sample application, but I think the idea is the important part, so let’s try and compare. By the way, it worked for me trying to write my own MVVM framework, this increase the learning curve gradient when we go for another more proven and stable solution. If you want follow this suggestion you probably would look at this very famous video about MVVM, edited by the author of Caliburn, Rob Eisenberg. Rob is a man that look at the mess in the XAML and said: Ok let’s create something to clean up this, instead of start up again with a WinForm app Smile. So let’s talk about the app, should be simple but doing something interesting, and must take 1/2 hour to write. I decided to write a Flickr picture browser so there is as well some async call to web services to show.

Let’s prepare our toolbelt for start we need to:

So start the application and put the required dependencies in the Lib folder of the solution, so we have something like this at the end:

image

All this reference are dependencies of our app, and with this we are ready to start to write the application itself. We start to “bootstrapping” the application. We have a main window for our application, and this will be our application main View, and we need to stick to it a ViewModel. Caliburn can do this automatically in the boostrap phase, lets see.

First create a class deriving from Botstrapper<T> where T is our ViewModel. As below:

class Boot:Bootstrapper<MainWindowModel>
{

This class can be empty to start, we come back later, now we modify the App.Xaml markup, by removing the StartupUri and adding a bootstrapper instance as a resource, so an instance will born:

<Application x:Class="FlickAndFlock.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:FlickAndFlock">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:Boot x:Key="Boot"></local:Boot>
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
            <Style TargetType="Window">
                <Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
            </Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>

If we launch the application at this point, the main window appear anyway, why ?

image

Because Caliburn infer the main window to start from the view model class name. The default convention is that the view has the same type name as the model minus “Model”, so if our model is MainWindowModel, the view to launch is MainWindow. So what happened in the boot was:

ViewModelBinder-INFO:Binding FlickAndFlock.MainWindow and FlickAndFlock.MainWindowModel.
Action-INFO:Setting DC of FlickAndFlock.MainWindow to FlickAndFlock.MainWindowModel.
Action-INFO:Attaching message handler FlickAndFlock.MainWindowModel to FlickAndFlock.MainWindow.

As I can see in my trace output in Visual Studio after having set up the log in the boot class static constructor:

static Boot()
{
     LogManager.GetLog = t => GetLogger(t);
}

We overwritten the function GetLog of LogManager with our one, returning an instance of a class implementing ILog that just dump the messages on the trace. Having a log enabled is a good idea when we have a convention over configuration based framework. Sometimes we don’t understand why convention are not applied as we expect, and usually we can guess where the problem is by looking at the log. Another point here is how we change the log recovering strategy: by setting a function to a property. This way of customization is cross used in the entire Caliburn framework. So as you probably guessed, if we want to change the way we look for the view from the model name, the strategy is exactly the same:

ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>…

 

In the example application we used the default one, but is good to know that if does not feet our convention, we can change.

So now we have the main view,and the main ViewModel, but what exactly we can put inside the ViewModel ? Well we can think the ViewModel as a model oriented to the presentation, focalized on the view, not on the business logic. Is the ViewModel to that is notified about the UI events, ie the “Actions” the user want to do.

We proceed now to add some user control on the XAML to allow the user to search by tag the Flicker photo database, obtaining something like:

image
<StackPanel Grid.Column="0">
            <TextBlock Margin="20">Tag:</TextBlock>
            <TextBox Margin="20" x:Name="Tags"></TextBox>
            <Button Margin="30,20,30,20" IsDefault="True" x:Name="Search">Search Flickr</Button>
            <StackPanel Orientation="Horizontal">
                <Button Margin="10,10,10,10" Width="80"  x:Name="PrevPage">Previous</Button>
                <Button Margin="10,10,10,10" Width="80" x:Name="NextPage">Next</Button>
            </StackPanel>
        </StackPanel>

The XAML is quite simple, there is nothing related to events in it, but, as you can see from the picture, previous and next are disabled, and search is not. This is another Caliburn magic, let see what’s in the log:

ViewModelBinder-INFO:Action Convention Applied: Action Search on element Search.
ViewModelBinder-INFO:Action Convention Applied: Action NextPage on element NextPage.
ViewModelBinder-INFO:Action Convention Applied: Action PrevPage on element PrevPage.

ViewModelBinder-INFO:Binding Convention Applied: Element Tags.

ActionMessage-INFO:Action: Search availability update.
ActionMessage-INFO:Action: Search availability update.
ActionMessage-INFO:Action: PrevPage availability update.
ActionMessage-INFO:Action: PrevPage availability update.
ActionMessage-INFO:Action: NextPage availability update.
ActionMessage-INFO:Action: NextPage availability update.

As we can see inside the log, the framework found some methods in our ViewModel called as the x:Name on the UIelement, so it automatically wired it. Even the command availability is automatically wired to property called Can+action name. Similar sort was reserved to the TextBox, automatically bound to the property Tags.

Let’s have a look now on how the command are actually implemented. Here the search function:

 

       public IEnumerable<IResult> Search()
       {
           currentPage = 1;
           return PageSearch();
       }
       public IEnumerable<IResult> PageSearch()
       {
           Thumbs.Clear();
           yield return new Wait(true);
           yield return new Search(Tags, currentPage);
           yield return new Wait(false);
           NotifyOfPropertyChange(() => this.CanNextPage);
           NotifyOfPropertyChange(() => this.CanPrevPage);
       }

That’s could sound a little strange. The routine is returning an IEnumebrable<IResult>, what is this ? Well this is a strategy to make easy and readable code when we need to spawn asynchronous process, wait for the result, then go on with something else, and so on. This can result in a lot of messy code and complicated lambdas. By returning ( or better, Yielding ) an IResult we return an object that is capable to do something and signal when done. As a consequence, functions cooperate together and all returns in the “master” function, that is called CoRoutine. Not really a new idea since comes from Knuth, but in fact C# has not a native way of express such a context. Result are obtained in C# by an orchestration part ( implemented in Caliburn ) and leveraging the yield statement to leave and re-enter the main routine. I think it is interesting to know that someone creates the same strategy before the yield statement were created ( NET 2.0 ), have a look here if you are courious. It is important to know that implementing the acgtions as CoRoutines is not mandatory, but is strongly encouraged when you have to deal with sync-async calls wired together.

Before ending, let’s have a look on how we wire an IoC to that strategy. This is done by customizing the bootstrapper, here an example:

           protected override object GetInstance(Type service, string key)
           {
               return kernel.Get(service);
           }

 

Caliburn call the function above when he need to instantiate an object, or when we try to instantiate something using the IoC static class. So that function is the extension point to bind the container. In the simple application I bound NInject, but any container can probably fit the purpose. Note that, even if you use the static IoC, without customizing the bootstrapper, natively Caliburn does not perform any IoC, but just plainly creates the requested type.

So the application is finished:

image

And we manage to avoid writing any code inside the view. And it is not all, Caliburn Micro has a lot of feature for us, even remaining a thin framework.

Sunday, 24 April 2011 00:03:42 (GMT Daylight Time, UTC+01:00)  #    Comments [1] - Trackback
Programmin | Recipes | WPF

# Tuesday, 05 April 2011
Cannot start test project “xxxx” because the project does not contains any tests

This error happen when we try to launch a Visual Studio Project defined as test with another testing framework ( ie NUnit ) and we remove all the visual studio test from inside it. To me happened when I try to revert back to NUnit a project with standard Visual Studio tests. Here The message:

clip_image001

 

The solution is to open the project ( csproj ) file with a text editor, and look for the lines below:

image

 

By removing the line in evidence we go back to a standard class library project and the problem is solved.

Tuesday, 05 April 2011 09:58:33 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin | Tips

# Monday, 21 March 2011

This is an example on implementing a behavior for zooming a 2D representations in WPF in a CAD friendly fashion, in the sense we need some facilities to have the same line thickness at different zoom level as well as text size. This because in 2D cad application we usually need to zoom into details but line thickness are actually zero width lines so they must not appear thicker as we look near to them, and even text sometimes need the same strategy. The same as we zoom “far” from the drawing, line thickness should appear the same. In this sample we focalize just a first zoom mode, the one handling the mouse wheel, that is simple because it does not require much user interaction handling ( in comparison to a “zoom redct” function ), but as in a real 2D cad application, we want a real zoom at point.

We implement this function as a behavior, so zooming seems “a thing attached” in XAML. An attached behavior, in WPF term is, as a per John Gossman definition:

The Attached Behavior pattern encapsulates "behavior" (usually user interactivity) into a class outside the visual heirarchy and allows it to be applied to a visual element by setting an attached property and hooking various events on the visual element.

Designing a behavior can start by the XAML, so we start by writing the XAML we expect to use in the way we like, as for example:

<Canvas z:Zoomable.Mode="Wheel" Background="White" SnapsToDevicePixels="True" x:Name="topCanvas">

 

The intent of this XAML is ( hopefully ) clear: we want to handle the zoom functionality on the Canvas in the mode “Wheel”. The underlined portion show where we attach the behavior. I did underline the Background assignment too: this is not exactly related to this behavior, but in general to get the canvas firing the mouse wheel events ( and any other mouse events ) it is necessary to specify a non transparent background, and these events are necessary to our behavior to work.

Implementing a behavior is done by using an attached property, and binding to the UIElement that property is applied to an event handler. In our implementation we use a class called ZoomHandler to handle the events:

public static readonly DependencyProperty ModeProperty =
           DependencyProperty.RegisterAttached("Mode", typeof(ZoomMode)
, typeof(Zoomable)
, new UIPropertyMetadata(ZoomMode.Disabled, new PropertyChangedCallback(OnModeChanged)));

       protected static void OnModeChanged(DependencyObject depo, DependencyPropertyChangedEventArgs depa)
       {
           Panel panel = depo as Panel;
           
           if (null == panel)
               throw new Exception("ZoomBehavior can only be attached to  objects derived from Panel");
           if (!attachedHandlers.ContainsKey(panel))
           {
               attachedHandlers[panel] = new ZoomHandler(panel);
           }
           attachedHandlers[panel].SetMode((ZoomMode)depa.NewValue);
       }

We attach a ZoomHandler class instance for each panel we want to zoom-enable, and we keep collected these object in an internal list. We define another attached property called UnityThickness. This is an important property in implementing CAD like behaviors: it is bound to the value of thickness of a line that appear to be one pixel thick at the selected zoom level. This property value is valuated from the ZoomHandler class, at the same time we apply the scaling factor, here below the function:

void canvas_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
       {
           if (currentMode == ZoomMode.Wheel || currentMode == ZoomMode.WheelAndMouse)
           {
               var reference = panel.Parent as IInputElement;
               if (null == reference)
                   reference = panel;
               Point pt = e.MouseDevice.GetPosition(reference);
               Transform mt = panel.RenderTransform;
               Matrix m = mt.Value;
               double amount = 1.0 + Math.Sign(e.Delta) * .1;
              
               m.ScaleAt(amount, amount, pt.X, pt.Y);
               currentAmount = m.M11;
               panel.RenderTransform= new MatrixTransform(m);
               Zoomable.SetUnityThickness(panel, 1.0 / currentAmount);
           }
       }

This function is the real one doing the zoom work. A scaling matrix is computed, to scale at an origin point defined by the current mouse coordinates. Then the attached UnityThickness property is set on the panel this ZoomHandler is handling. The scale amount is extracted form the first diagonal value of the RenderMatrix, by doing this we suppose we are using uniform scaling in zoom. Since we probably need to draw geometry with different thickness, we need some helper to bound the UnityThickness with different factors. The same can be done for font sizes. In order to achieve this, an helper class called ScaledThicknessSource is created: whith this class we can provide a scaled thickness by the current scale factor, here we can write:

  <Canvas z:Zoomable.Mode="Wheel" Background="White" SnapsToDevicePixels="True" x:Name="topCanvas">
            <z:ScaledThicknessSource  Thickness="1.0"  x:Name="Thickness1" UnityThickness="{Binding Path=(z:Zoomable.UnityThickness), RelativeSource={RelativeSource AncestorType=Canvas}}"  />
            <z:ScaledThicknessSource  Thickness="3.0"  x:Name="Thickness3" UnityThickness="{Binding Path=(z:Zoomable.UnityThickness), RelativeSource={RelativeSource AncestorType=Canvas}}"  />
            <z:ScaledThicknessSource   Thickness="22.0"  x:Name="FontSize12" UnityThickness="{Binding Path=(z:Zoomable.UnityThickness), RelativeSource={RelativeSource AncestorType=Canvas}}"  />
            <TextBlock Canvas.Left="20"  Canvas.Top="40" Foreground="Goldenrod" FontSize="{Binding Path=ActualThickness,ElementName=FontSize12}" >Non scaling text</TextBlock>
            <TextBlock Canvas.Left="240"  Canvas.Top="40" Foreground="Goldenrod" FontSize="22" >Scaling text</TextBlock>
            <Rectangle Fill="RosyBrown" StrokeThickness="2" Canvas.Left="60" Canvas.Top="80" Stroke="DeepSkyBlue" Width="100" Height="100"/>
            <Ellipse Fill="Brown" StrokeThickness="{Binding Path=ActualThickness,ElementName=Thickness1}" Canvas.Left="160" Canvas.Top="80" Stroke="Blue" Width="100" Height="100"/>
            <Path Canvas.Left="20" Canvas.Top="200" Stroke="BurlyWood" Data="M 45,0 L 35,30 L 0,30 L 30,55 L 20,85 L 45,60 L 70,85 L 60,55 L 90,30 L 55,30 L 45,0"/>
            <Path Canvas.Left="120" StrokeThickness="{Binding Path=ActualThickness,ElementName=Thickness3}" Canvas.Top="200" Stroke="DarkCyan" Data="M 45,0 L 35,30 L 0,30 L 30,55 L 20,85 L 45,60 L 70,85 L 60,55 L 90,30 L 55,30 L 45,0"/>
            <Image Canvas.Left="280" Canvas.Top="80" Source="/Assets/leo-mini.png" Width="128" Height="128"></Image>
        </Canvas>

In the example above, we create some instances of ScaledThickessSource, and we bound the property UnityThickess to the UnityThickness of the Zoomable behavior. Please note the syntax used:the () is necessary for an attached property. The thickness value is the desired one, at any zoom level. Then the examples geometry and text are bound tho the ActualThickness property of the helper instance, in order to have the proper scaled value.

Now we can look some examples of the behavior at work:

image The canvas with some example element. A test mantaining the dimension at any zoom level, a text affected by the zoom. The square has a standard thick border, the circle a correted thick border. The left star is with a standard border, and the rightr one has a fix tick border of two pixel. The image is added to show the zoom behavior working on raster images too.
image Here we show how the test is kept at each zoom level.
image Zooming in. The circle border thickness is kept, the square not.
image The same for the star. Notice as the left one appear to be bigger when we zoom in, this is because the left star border is not scaled.
image Zoom in works on Raster images…
image And zoom out too.

Another little thing, even if not necessary, the anti aliasing effect can blur a little the lines drawing, we can disable it in the following way:

 <Canvas z:Zoomable.Mode="Wheel" Background="White" SnapsToDevicePixels="True" x:Name="topCanvas">

Panning is not implemented yet. I would probably enable the ScrollViewer able to handle the pan, at present a standard ScrollViewer does simply not work since canvas does not measures the contained children total occupied size.

Monday, 21 March 2011 21:01:30 (GMT Standard Time, UTC+00:00)  #    Comments [3] - Trackback
Programmin | WPF

# Monday, 14 March 2011

This is the list of the posts related to Workflow4 Re-Hosting

Post 1 First drop, showing we can host the designer view in pure markup.
Post 2 Adding the activities bar into the game.
Post 3 Removing the blue gear, and adding the proper activity image.
Post 4 Showing the property editor grid.
Post 5 Default WorkflowView command routing.

 

Sample Project has a place on Codeplex.

Monday, 14 March 2011 20:16:50 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Recipes | ReHosting | WF4

There is an entire suite of command defined by the designer view:

      • DesignerView.CopyAsImageCommand;
      • DesignerView.CopyCommand;
      • DesignerView.CreateArgumentCommand;
      • DesignerView.CreateVariableCommand;
      • DesignerView.CreateWorkflowElementCommand;
      • DesignerView.CutCommand;
      • DesignerView.CycleThroughDesignerCommand;
      • DesignerView.DeleteBreakpointCommand;
      • DesignerView.DisableBreakpointCommand;
      • DesignerView.EnableBreakpointCommand;
      • DesignerView.ExpandAllCommand;
      • DesignerView.ExpandCommand;
      • DesignerView.ExpandInPlaceCommand;
      • DesignerView.FitToScreenCommand;
      • DesignerView.GoToParentCommand;
      • DesignerView.InsertBreakpointCommand;
      • DesignerView.PasteCommand;
      • DesignerView.RedoCommand;
      • DesignerView.ResetZoomCommand;
      • DesignerView.SaveAsImageCommand;
      • DesignerView.SelectAllCommand;
      • DesignerView.ToggleArgumentDesignerCommand;
      • DesignerView.ToggleImportsDesignerCommand;
      • DesignerView.ToggleMiniMapCommand;
      • DesignerView.ToggleSelectionCommand;
      • DesignerView.ToggleVariableDesignerCommand;
      • DesignerView.UndoCommand;
      • DesignerView.ZoomInCommand;
      • DesignerView.ZoomOutCommand;

But since the view is internal in the designer it is not automatic binding commands form the UI to the underlying target. So our hosting solution propose an attached property, to route the command to the view in a way suitable in pure markup too. The property we create is called DesignerCommandTarget, and is used as below:

   1:  <Button Style="{StaticResource toolButton}" ToolTip="Zoom In"   Command="wfview:DesignerView.ZoomInCommand"
   2:   host:HostingHelper.DesignerCommandTarget="{StaticResource wfHost}" 
   3:  >
   4:                      <Image Source="Assets/zoom_in.png"></Image>
   5:  </Button>
   6:                 

 

So we basically bind the command to the button by using Command, as usual, but we set the DesignerCommandTarget to our hosting instance, that is defined in the resource. In order to have the commands exposed by the designer in XAML, we added the wfview namespace reference as underlined below:

   1:  <Window x:Class="WF4Host.MainWindow"
   2:          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:          xmlns:toolbox="clr-namespace:System.Activities.Presentation.Toolbox;assembly=System.Activities.Presentation"
   5:          xmlns:host="clr-namespace:WF4Host"
   6:          xmlns:custom="clr-namespace:MyActivities;assembly=MyActivities"
   7:          xmlns:wf="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
   8:          xmlns:wfview="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
   9:          xmlns:activities="clr-namespace:System.Activities.Statements;assembly=System.Activities"
  10:          Title="MainWindow" Height="650" Width="825"
  11:          Icon="Assets/App.ico"
  12:          TextOptions.TextFormattingMode="Display"
  13:         
  14:          >

Command routing is very simple, and it is shown below:

 

   1:   protected static void OnCommandTargetChanged(DependencyObject depo, DependencyPropertyChangedEventArgs depa)
   2:          {
   3:              var src = depo as ICommandSource;
   4:              if (null != src && depa.NewValue != null )
   5:              {
   6:                  var ctargetdescr = DependencyPropertyDescriptor.FromName("CommandTarget"
   7:                                                , depo.GetType()
   8:                                                , depo.GetType()
   9:                                                ,true);
  10:                  ctargetdescr.SetValue(depo
  11:  , (depa.NewValue as HostingHelper).Designer.Context.Services.GetService<DesignerView>());
  12:              }
  13:          }

Every time we attach the property we check if the target is a CommandSource, so basically the strategy works for any object implementing this interface. If this pre condition is satisfied, we simply bind the WorkflowDesigner view as a CommandTarget for that source. In this way commands from our UI behave as they are defined inside the workflow, and we don’t need to write any code behind to custom wire them.

Our example application now looks like this:

 

image

Code for this post is hosted here.
Monday, 14 March 2011 11:13:09 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Recipes | ReHosting | WF4

# Monday, 07 March 2011

Showing the property grid is achieved using  the same strategy we used to show the main editor, since it is a View internally exposed by the WorkflowDesigner. So we can decide where to put the property grid in XAML only in order to be blend friendly. Let’s have a look at the XAML:

   1:              <toolbox:ToolboxControl Grid.Column="0" Grid.Row="1" host:ToolboxItemSource.CategorySource="{StaticResource GeneralTools}"/>
   2:              <GridSplitter ResizeDirection="Columns" Grid.RowSpan="3" Grid.Column="1" Grid.Row="1" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
   3:              <host:MainViewPresenter HostingHelper="{StaticResource wfHost}" Grid.RowSpan="3" Grid.Column="2" Grid.Row="1"/>
   4:              <GridSplitter ResizeDirection="Rows" Grid.Column="0" Grid.Row="2" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
   5:              <host:PropertyViewPresenter HostingHelper="{StaticResource wfHost}" Grid.Column="0" Grid.Row="3"/>

Here we have all the graphical widget composing the designer as XAML tags. At line 5 we actually present the property editor. As a noise there is also some splitter we add in order to have a better usable interface. Code for the presenter is quite the same as the one we use to present the main view. So similar that could be refactor in some base class, but it is not done at the moment.

What we have right now is this:

image

A little problem appear to be the cursor over the splitter. For some weird reason the WF controls seems to stole the mouse cursor management and there is no way out to change it.

A poor man workaround is to make the splitter turn to bold when the mouse hover on it:

       <Style TargetType="GridSplitter">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Background" Value="Black"/>
                 </Trigger>
 
            </Style.Triggers>
        </Style>

 

As usual the code so far can be found here.

Monday, 07 March 2011 20:41:30 (GMT Standard Time, UTC+00:00)  #    Comments [2] - Trackback
Programmin | Recipes | ReHosting | WF4

# Sunday, 06 March 2011

When we host the activities palette, we notice a blue gear without meaning on each of our activity. This should be easy to solve, but it is not so easy for the built in activities. So what we have till now is:

image

We want to have something better, even for the standard activities. In WF4 is not said that the ToolboxBitmapAttribute, the one used to bind the image to show in the palette is attached to all activities, and we can say it is not for sure for the built in activities. Fortunately the WF4 architecture provides a way to attach attributes to objects runtime, this is achieved via the AttributeTableBuilder class, that provide a way to register and apply attributes runtime to objects via another helper class: MetadataStore. So if we have somewhere these icons, we can attach the proper attribute runtime, and all is done, but where to find it ?  Do we have proper licensing to distribute it ? A reply to this question could be found here, and in particular in the Brannon King’s comment. Brannon show how to grab the icons from the redistributable System.Activities.Presentation dll, and more, it post the whole code to provide the attributes creation and binding, and graphics retrieval too. So we grab this great work and we add it in our helper class ToolboxItemSource, so we can ensure designer has the proper graphic information for both built in and custom activities we add as a source. Let’s have a look at the code, that is just a little modified:

   1:                  var builder = new AttributeTableBuilder();
   2:                  foreach (var item in query)
   3:                  {
   4:                      AddIconAttributes(item, builder);
   5:                  }
   6:                  MetadataStore.AddAttributeTable(builder.CreateTable());

Well this portion is in the member function AddTools, already present in our helper, we basically creates an AttributeTableBuilder, then we add the resources attributes, and then we realize, at line 6, the association. Let’s have a look at the Brannon’s code, that we embed in the AddIconAttributes:

   1:        protected static  bool AddIconAttributes(Type type, AttributeTableBuilder builder)
   2:          {
   3:              var secondary = false;
   4:              var tbaType = typeof(ToolboxBitmapAttribute);
   5:              var imageType = typeof(System.Drawing.Image);
   6:              var constructor = tbaType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { imageType, imageType }, null);
   7:              string resourceKey = type.IsGenericType ? type.GetGenericTypeDefinition().Name : type.Name;
   8:              int index = resourceKey.IndexOf('`');
   9:              if (index > 0)
  10:              {
  11:                  resourceKey = resourceKey.Remove(index);
  12:              }
  13:              if (resourceKey == "Flowchart")
  14:              {
  15:                  resourceKey = "FlowChart"; // it appears that themes/icons.xaml has a typo here
  16:              }
  17:              resourceKey += "Icon";
  18:              Bitmap small, large;
  19:              object resource = resources[resourceKey];
  20:              if (!(resource is DrawingBrush))
  21:              {
  22:                  resource = resources["GenericLeafActivityIcon"];
  23:                  secondary = true;
  24:              }
  25:              var dv = new DrawingVisual();
  26:              using (var context = dv.RenderOpen())
  27:              {
  28:                  context.DrawRectangle(((DrawingBrush)resource), null, new Rect(0, 0, 32, 32));
  29:                  context.DrawRectangle(((DrawingBrush)resource), null, new Rect(32, 32, 16, 16));
  30:              }
  31:              var rtb = new RenderTargetBitmap(32, 32, 96, 96, PixelFormats.Pbgra32);
  32:              rtb.Render(dv);
  33:              using (var outStream = new MemoryStream())
  34:              {
  35:                  BitmapEncoder enc = new PngBitmapEncoder();
  36:                  enc.Frames.Add(BitmapFrame.Create(rtb));
  37:                  enc.Save(outStream);
  38:                  outStream.Position = 0;
  39:                  large = new Bitmap(outStream);
  40:              }
  41:              rtb = new RenderTargetBitmap(16, 16, 96, 96, PixelFormats.Pbgra32);
  42:              dv.Offset = new Vector(-32, -32);
  43:              rtb.Render(dv);
  44:              using (var outStream = new MemoryStream())
  45:              {
  46:                  BitmapEncoder enc = new PngBitmapEncoder();
  47:                  enc.Frames.Add(BitmapFrame.Create(rtb));
  48:                  enc.Save(outStream);
  49:                  outStream.Position = 0;
  50:                  small = new Bitmap(outStream);
  51:              }
  52:   
  53:              var tba = constructor.Invoke(new object[] { small, large }) as ToolboxBitmapAttribute;
  54:              builder.AddCustomAttributes(type, tba);
  55:              return secondary;
  56:          }

 

I’ve just stored the resources needed in a local static dictionary, by this call:

 

   1:  static ResourceDictionary resources = new ResourceDictionary
   2:   { Source = new Uri("pack://application:,,,/System.Activities.Presentation;component/themes/icons.xaml") };

 

Basically Brannon’s code use the assumption the icon is the name of the activity suffixed by “Icon”, except for a typo on FlowChart solved at lines 13-16. Then he create a bitmap and it save it on a memory stream. That stream is then use as an argument for ToolboxBitmapAttribute. So we put all together and we obtain this new presentation:

image

So we got rid of the blue gear. We will need the property editor next, always a XAML only strategy to present it has to be provided, probably with the same strategy we used for the main editor itself.

Have a look at the code here.

Sunday, 06 March 2011 22:10:10 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Recipes | ReHosting | WF4

# Saturday, 05 March 2011

In the previous post we had a look on how to host WF4 designer in pure markup, we continue now to host, always in markup, the toolbar for dropping activities into the designer surface. The ToolboxControl is fortunately a standard WPF control, so it is not a problem to use it in markup, but it is a little difficult to add a list of categorized activities, because we have to add as a content of the control a list of category, each with a list of category. We want to use some sort of activity list source instead. So let’s have a look at the markup that add a ToolBoxControl to our designer:

   1:   <host:MainViewPresenter HostingHelper="{StaticResource wfHost}"  Grid.Column="1" Grid.Row="1"/>
   2:   <toolbox:ToolboxControl Grid.Column="0" Grid.Row="1" host:ToolboxItemSource.CategorySource="{StaticResource GeneralTools}" >

So the line 2 is the markup tag for adding the ToolboxControl, but the interesting part is the:

host:ToolboxItemSource.CategorySource="{StaticResource GeneralTools}"

CategorySource is an attached property of type ToolboxItemSource, an helper class we use to make easier binding to a list of activities. In this particular case we have defined an instance on the resources, called GeneralTools, as below:

   1:   <host:ToolboxItemSource x:Key="GeneralTools">
   2:              <host:ToolboxSource TargetCategory="General" AllSiblingsOf="{x:Type activities:Delay}"  />
   3:              <host:ToolboxSource TargetCategory="Custom" AllSiblingsOf="{x:Type custom:Activity1}"  />
   4:   </host:ToolboxItemSource>

As we can see, we can add to the ItemSource a list of source of type ToolboxSource, an helper class too. That class has the only pourpose of collecting the target category name, and a type of one activity to pick an assembly and contextually add all activities contained in. Most of the job is done by ToolboxItemSource, when the property is actually attached, so the code below:

   1:          protected static void OnCategorySourceChanged(DependencyObject depobj,DependencyPropertyChangedEventArgs dpcea)
   2:          {
   3:              if (null != dpcea.NewValue && dpcea.NewValue is ToolboxItemSource)
   4:              {
   5:                  var tbsrc = dpcea.NewValue as ToolboxItemSource;
   6:                  foreach (var source in tbsrc.Sources)
   7:                  {
   8:                      AddTools(depobj as ToolboxControl, source);
   9:                  }
  10:              }
  11:          }
  12:   
  13:          private static void AddTools(ToolboxControl toolboxControl, ToolboxSource source)
  14:          {
  15:              if (null != source.AllSiblingsOf)
  16:              {
  17:                  var cat = toolboxControl.Categories.Where(q => q.CategoryName.Equals(source.TargetCategory)).FirstOrDefault();
  18:                  if (null == cat)
  19:                  {
  20:                      cat = new ToolboxCategory(source.TargetCategory);
  21:                      toolboxControl.Categories.Add(cat);
  22:                  }
  23:                  var query = from type in source.AllSiblingsOf.Assembly.GetTypes()
  24:                              where type.IsPublic &&
  25:                              !type.IsNested &&
  26:                              !type.IsAbstract &&
  27:                              !type.ContainsGenericParameters &&
  28:                              (typeof(Activity).IsAssignableFrom(type) ||
  29:                              typeof(IActivityTemplateFactory).IsAssignableFrom(type))
  30:                              orderby type.Name
  31:                              select new ToolboxItemWrapper(type);
  32:                  foreach (var item in query)
  33:                      cat.Add(item);
  34:              }

35: }

 

Very easy, when we attach the item source, we pump on the control all the required activities, and it’s done. So this is another step on a XAML only re-hosting, let’s see the result:

image

Well you will probably note the very fancy gear icon that is the “mean nothing” icon that we usually have when we forgot something, and actually we forgot something, but differently from the old workflow is not so easy: a look here could help, and we probably work on that direction in further steps.

All are welcome to grab or cooperate on the source code for this sample here.

Saturday, 05 March 2011 22:37:05 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Recipes | ReHosting | WF4

This post is based on this great post from The Problem Solver, but here we try to use less code behind and more markup language, to let the “host” decide where to put the designer components in his application. Basically there is three main chunk involved in hosting the WF:

  1. The main designer surface
  2. The property view
  3. The toolbox.

Components 1) and 2) are exposed as property of the WorkflowDesigner class, that itself is not a UIElement, but a simple class derived from object. The two property of interest are View and PropertyInspectorView, and both are UIElements. The problem is: how to let’s the host application writer to decide where to place these in XAML ? Lets start creating a class that act as an helper, and provide inside it some initialization code:

namespace WF4Host
{
    public class HostingHelper:DependencyObject
    {
        public WorkflowDesigner Designer { get { return workflowDesigner;} }
        WorkflowDesigner workflowDesigner;
        public HostingHelper()
        {
            workflowDesigner = new WorkflowDesigner();
            new DesignerMetadata().Register();
            //Creates an empty workflow
            workflowDesigner.Load(new Sequence());
        }
    }
    
}

basically we creates an instance of the WorkflowDesigner itself, load a first Sequence activity, and start the designer runtime. In xaml we can declare such an object as a StaticResource:

   1:  <Window x:Class="WF4Host.MainWindow"
   2:          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:          xmlns:host="clr-namespace:WF4Host"
   5:          xmlns:wf="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
   6:          Title="MainWindow" Height="650" Width="825"
   7:          >
   8:      <Window.Resources>
   9:          <host:HostingHelper x:Key="wfHost"/>
  10:          <LinearGradientBrush x:Key="backBrush" StartPoint="0,0" EndPoint="0,1" >
  11:              <GradientStop Offset="0.1" Color="DarkSlateBlue"></GradientStop>
  12:              <GradientStop Offset="0.2" Color="DarkSlateGray"/>
  13:              <GradientStop Offset="0.9" Color="BlanchedAlmond"/>
  14:          </LinearGradientBrush>
  15:      </Window.Resources>

 

Easy: at line 4 we add the namespace for our project. At line 9 we declare an instance of our HostingHelper and we name it wfHost. Remaining resources are just some beautiful ( ehm ) arts.

Then we need to present the graphical components. Lets declare in our code a control deriving from grid:

   1:  namespace WF4Host
   2:  {
   3:      public class MainViewPresenter : Grid
   4:      {
   5:          public HostingHelper HostingHelper
   6:          {
   7:              get { return (HostingHelper)GetValue(HostingHelperProperty); }
   8:              set { SetValue(HostingHelperProperty, value); }
   9:          }
  10:   
  11:          public static readonly DependencyProperty HostingHelperProperty =
  12:              DependencyProperty.Register("HostingHelper", typeof(HostingHelper)
  13:              , typeof(MainViewPresenter)
  14:              , new UIPropertyMetadata(null, new PropertyChangedCallback(OnHostingHelperChanged)));
  15:   
  16:          protected static void OnHostingHelperChanged(DependencyObject dobj
  17:              , DependencyPropertyChangedEventArgs dpcea)
  18:          {
  19:              if (dobj is MainViewPresenter)
  20:              {
  21:                  var mvp = dobj as MainViewPresenter;
  22:                  mvp.Children.Clear();
  23:                  if (dpcea.NewValue != null)
  24:                  {
  25:                      var host = dpcea.NewValue as HostingHelper;
  26:                      host.Designer.View.SetValue(Panel.HorizontalAlignmentProperty, HorizontalAlignment.Stretch);
  27:                      host.Designer.View.SetValue(Panel.VerticalAlignmentProperty, VerticalAlignment.Stretch);
  28:                      mvp.Children.Add(host.Designer.View);
  29:                  }
  30:              }
  31:          }
  32:      }
  33:  }

 

So this class is the MainView presenter. We call the Main View the one in which the WF is actually drawn. This control has a dependency property called HostingHelper, so we can bind it to the current HostingHelper that orchestrates the Workflow design. Let see how to do it in xaml:

   1:  <Grid Name="grid">
   2:              <Grid.RowDefinitions>
   3:                  <RowDefinition Height="32"/>
   4:                  <RowDefinition Height="*"/>
   5:              </Grid.RowDefinitions>
   6:                  <Grid.ColumnDefinitions>
   7:              <ColumnDefinition Width="100"></ColumnDefinition>
   8:              <ColumnDefinition></ColumnDefinition>
   9:              <ColumnDefinition Width="100"></ColumnDefinition>
  10:          </Grid.ColumnDefinitions>
  11:               
  12:              <host:MainViewPresenter
  13:               HostingHelper="{StaticResource wfHost}"  Grid.Column="1" Grid.Row="1"/>
  14:              
  15:          </Grid>

So at line 12 we declare our presenter, and we bind his hosting helper property to the hosting helper instance wfHost.

This is the current result:

image

So we achieved less code behind and more “blendability” by creating a little tool library. We should improve it with others components to complete the designer environment. I decided to publish this example as a project on codeplex, so you can join to use and improve it.

Saturday, 05 March 2011 10:49:00 (GMT Standard Time, UTC+00:00)  #    Comments [2] - Trackback
Programmin | Recipes | ReHosting | WF4

# Sunday, 13 February 2011

Volevo sperimentare un po’ con silverlight, e la prima cosa che mi è venuta in mente è stata fare un bel blog gatget che mostrasse le ultime attività “meritorie” su StackOverflow, utilizzando le Stack Overflow API.

Con un po’ di lavoro il risultato è stato questo:

image

Ovviamente, anche se il progetto è semplicissimo, non ho saputo rinunciare a Caliburn Micro sia per l’aggancio MVVM che per le action. Ancora un paio di cose, e poi proverò a distribuirlo sul mio blog.  Come visibile nella barra di destra il componentino c’è: Provato sia su firefox che su IE sembra comportarsi bene. Il deploy su Aruba è stato facilissimo, anche perchè non c’è niente da fare a livello permissionWinking smile

Sunday, 13 February 2011 16:40:07 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Silberlight

# Tuesday, 28 September 2010

Per una piccola applicazione che sto scrivendo, un generatore di etichette molto colorate, con grafici e altre informazioni sopra, ho pensato di utilizzare XAML con uno user control WPF, disegnato in una immagine ( JPEG o PNG ). Con la classe XamlReader si legge lo script XAML, con RenderTargetBitmap si ottiene un rendering in memoria, e tramite PngBitmapEncoder si salva l’immagine, per esempio in PNG. Ho quindi costruito un “server di etichette”, basato su WebServer C#, di cui ho parlato qui. Il problema è che, all’interno di un servizio, o comunque in qualsiasi thread non di interfaccia utente, il rendering provoca questo simpatico errore:

“Cannot create instance of 'UserControl' defined in assembly 'PresentationFramework,
Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
The calling thread must be STA, because many UI components require this.  Error at Line 1 Position 2”

XamlRender vuole che il thread usato per il rendering sia un thread STA, cosa abbastanza normale per la UI, che deve gestire le interazioni con l’utente in modo sequenziale ( message based ). Il problema può essere risolto facendo partire un nuovo thread in modalità STA, e aspettandolo in modo sincrono. Il codice è questo:

   1:   EventWaitHandle xamlLoaded = new EventWaitHandle(false,EventResetMode.ManualReset);
   2:         
   3:          private void CreateVisualInStream(IRequest request,MemoryStream ms)
   4:          {
   5:              string fileName = GetFileName(request.Uri);
   6:              Thread t = new Thread(q => StaLoadAndRenderXaml(fileName, ms));
   7:              t.SetApartmentState( ApartmentState.STA);
   8:              xamlLoaded.Reset();
   9:              t.Start();
  10:              xamlLoaded.WaitOne(TimeSpan.FromSeconds(30));
  11:          }
  12:          private void StaLoadAndRenderXaml(string fileName, MemoryStream ms)
  13:          {
  14:              if (File.Exists(fileName))
  15:              {
  16:                  using (var s = new FileStream(fileName, FileMode.Open))
  17:                  {
  18:                      logger.Warn("Loading control:" + fileName);
  19:                      var control = XamlReader.Load(s) as Control;
  20:                      int w = Convert.ToInt32(control.GetValue(Control.WidthProperty));
  21:                      int h = Convert.ToInt32(control.GetValue(Control.HeightProperty));
  22:                      control.DataContext = new DummyDataContext();
  23:                      control.Measure(new System.Windows.Size((int)control.Width, (int)control.Height));
  24:                      control.Arrange(new System.Windows.Rect(new System.Windows.Size((int)control.Width, (int)control.Height)));
  25:                      control.UpdateLayout();
  26:                      logger.Warn("Rendering control:" + fileName);
  27:                      var renderedXaml = RenderLabel(control, w, h);
  28:                      PngBitmapEncoder png = new PngBitmapEncoder();
  29:   
  30:                      png.Frames.Add(BitmapFrame.Create(renderedXaml));
  31:                      png.Save(ms);
  32:                      logger.Info("Saved rendered control. Size=" + ms.Length);
  33:                      xamlLoaded.Set();
  34:                  }
  35:              }
  36:              else
  37:              {
  38:                  xamlLoaded.Set();
  39:                  throw new InternalServerException("Failed to process file '" + fileName + "'.");
  40:              }
  41:   
  42:          }

 

 

Alla linea 1 è creato loggetto di sincronizzazione che ci serve per capire che il thread ha finito. Alla linea 7 specifichiamo che il thread creato deve essere di tipo STA, e dopo averlo lanciato lo aspettiamo alla linea 10. Notare il timeout che ci consente di evitare deadlock in ogni situazione. Alla linea 27 il codice di RenderLabel provvede a fare il rendering vero e proprio:

   1:   private RenderTargetBitmap RenderLabel(Visual v, int width, int height)
   2:   {
   3:              var bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
   4:              bmp.Render(v);
   5:              return bmp;
   6:   }
Tuesday, 28 September 2010 09:59:37 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin | Recipes

# Monday, 27 September 2010

Note: For visitors of your site, this entry is only displayed for users with the preselected language Romanian/română (ro)

Când se creează o aplicaţie unde sunt implicate mai multe procese server side, iar diferiţii agenţi trebuie să comunice între ei, este adesea necesară o strategie care să meargă dincolo de obişnuitele canale WCF/Remoting raw; este nevoie de un mecanism fiabil şi simplu. Mai jos găsiţi un scenariu exemplificativ, pentru a descrie problematica:

 

 

 

 

image

 

În exemplu, un server apropiat producţiei ştie când un produs s-a terminat, şi comunică acest eveniment printr-un bus. Depozitul este desigur interesat de acest eveniment şi „subscrie „ acest lucru. În acelaşi fel, depozitul poate publica că, având în vedere noul produs ce a sosit cu un stoc înainte, acum nu mai este nimic disponibil, mesaj ce cu siguranţă interesează serverul e-commerce, şi aşa mai departe pentru alte evenimente alţi agenţi.

 

Acum întrebarea ar putea fi:De ce nu conectăm diferiţii agenţi cu anumite cereri, de exemplu WCF? Sau cu cereri normale Web Service pe http, sau remoting? Motivele bune sunt:

  • Când agenţii sunt în număr mare, aceştia nu pot fi în contemporan activi. Dacă trebuie să restartez indiferent di ce motiv server-ul depozitului, nu pot să stau să-mi fac griji pentru avizele asupra produselor terminate care sosesc din producţie.

  • Elaborarea pentru un eveniment ar putea cere mult timp, pot să fac să aştepte aplicaţia care la detectat ?

  • Soluţia ar putea creşte, iar alţi agenţi ar putea fi interesaţi de evenimentele pe care le ridică alte aplicative.

In acest moment, un candidat ideal pare să fie MSMQ (sau un alt mecanism de queuing), dar şi în acest caz folosirea crudă a instrumentului poate duce la o situaţie puţin elegantă din punct de vedere al design-ului. Avem deci nevoie de un mecanism care să ne permită să lucrăm cu publicare şi subscriere de mesaje care:

  • Să garanteze livrarea mesajelor către subscriitori: dacă cine publică nu primeşte excepţii, suntem siguri că subscriitorii vor primi(mai devreme sau mai târziu, în mod rezonabil) mesajul de care sunt interesaţi

  • Să garanteze că mesajele s-au primit o singură dată de la subscriitori

  • Să ofere posibilitatea de a trimite mesaje după un anumit interval de timp

  • Să fie simplu de folosit: nu vreau să scriu zeci de pagini de configurare pentru a pune pe picioare şi menţine soluţia.

 

In reţea am găsit aceste două lucruri care fac exact asta:

Din păcate amândouă sunt „puternic tipate” şi nu permit o publicare curată a evenimentelor identificate de un label cu un playload pur textual, şi nu permit nici ridicarea de evenimente prelungite în timp. De aceea am implementat o soluţie făcută în casă, care nu conţine mult dintre cele două soluţii descrise mai înainte - în special nu deţine noţiunea de "Saga", adică un mesaj cu un istoric ce se modifică în faţa altor evenimente din bus - dar ce permite transcrierea şi subscrierea cu uşurinţă a evenimentelor în modalitate plain text. Soluţia utilizează drept canal sigur MSMQ, deci este MS oriented. Ce s-ar întâmpla dacă aş vrea să intru în arhitectura de mai sus a aplicaţiilor scrise în Java ? Cateva raspunsuri le puteti gasi in acest blog excelent, in special acest articol despre Apache ActiveMQ. Pare să fie o soluţie “cutting edge” când trebuie să facem să convieţuiască aplicaţii C#JAVA.

Monday, 27 September 2010 12:56:13 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin

# Saturday, 25 September 2010

Note: For visitors of your site, this entry is only displayed for users with the preselected language Romanian/română (ro)

Ne poate fi de ajutor în multe cazuri să avem un server web *fără* să instalăm IIS şi fără să fie necesar să facem un hosting pentru ASP.NET, dar având un „plain vanilla” server http, care să răspundă direct la cererile socket, care să fie uşor de extins cu module custom, simplu şi de încredere. În codeplex se găseşte C# WebServer , deploy-ul în situaţia minimală constă într-un singur DLL (ce frumos!), iar pentru a-l pune în funcţiune sunt de ajuns câteva rânduri de cod:

 

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         // create a server.
   6:         var server = new Server();
   7:  
   8:         // same as previous example.
   9:         server.Resources.Add(new FileResources("/", Environment.CurrentDirectory + "\\files\\"));
  10:         server.Add(new FileModule(server.Resources, false));
  11:  
  12:         // use one http listener.
  13:         server.Add(HttpListener.Create(IPAddress.Any, 8085));
  14:  
  15:         // add our own module.
  16:         server.Add(new Streamer());
  17:  
  18:  
  19:         // start server, can have max 5 pending accepts.
  20:         server.Start(5);
  21:  
  22:         Console.ReadLine();
  23:     }
  24: }

Faptul că avem exigenţe reduse de dependinţe, face din acesta un instrument ideal pentru îmbogăţirea cu UI a aplicaţiilor „service” pe care le vom scrie.

Saturday, 25 September 2010 14:58:35 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin

# Friday, 24 September 2010

Può servire in molte occasioni  avere un server web *senza* installare IIS e senza dover fare hosting di ASP.NET, ma di avere un “plain vanilla” server Http, che risponda direttamente alle chiamate socket, che sia facilmente estendibile con dei moduli custom, semplice ed affidabile. Su codeplex si trova C# WebServer , il deploy nella situazione minimale consta di una sola dll ( che bellezza ! ), e metterlo in funzione si fa con un paio di righe di codice:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         // create a server.
   6:         var server = new Server();
   7:  
   8:         // same as previous example.
   9:         server.Resources.Add(new FileResources("/", Environment.CurrentDirectory + "\\files\\"));
  10:         server.Add(new FileModule(server.Resources, false));
  11:  
  12:         // use one http listener.
  13:         server.Add(HttpListener.Create(IPAddress.Any, 8085));
  14:  
  15:         // add our own module.
  16:         server.Add(new Streamer());
  17:  
  18:  
  19:         // start server, can have max 5 pending accepts.
  20:         server.Start(5);
  21:  
  22:         Console.ReadLine();
  23:     }
  24: }

La ridotta esigenza di dipendenze ne fa uno strumento ideale per arricchire di UI le applicazioni “service” che andiamo a scrivere.

Friday, 24 September 2010 21:04:32 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin

# Thursday, 23 September 2010

Quando si crea un applicazione in cui sono coinvolti diversi processi server side, e i diversi agenti devono tra loro comunicare, è spesso necessaria una strategia che vada un po’ oltre i soliti canali WCF/Remoting crudi, ma serve qualche meccanismo più affidabile e magari anche più semplice. Qui sotto uno scenario di esempio, per descrivere la problematica:

distr_app

Nell’esempio un server vicino alla produzione conosce quando un prodotto è finito, e comunica questo evento su un bus. Il magazzino è ovviamente interessato a questo evento e lo “sottoscrive”. In modo analogo il magazzino può pubblicare che, a fronte di un nuovo prodotto arrivato una giacenza prima  nulla è ora dinuovo disponibile, messaggio di sicuro interesse per il server dell’ e-commerce, e così via per altri eventi ed altri agenti.

Ora la domanda potrebeb essere: perchè non connettere i vari agenti con delle chiamate, per esempio WCF ? O normali chiamate Web Service su http, o magari remoting ? Buoni motivi sono:

  • Quando gli agenti sono molti, non tutti possono essere “su” contemporaneamente. Se devo riavviare per qualsiasi motivo il server del magazzino, mica posso stare li a preoccuparmi di che fine fanno le notifiche sui prodotti finiti che arrivano dalla produzione.
  • L’elaborazione a fronte di un evento potrebbe richiedere molto tempo, posso fare aspettare l’applicazione che lo ha rilevato ?
  • La soluzione potrebbe cerscere, ed altri agenti potrebbero essere interessati ad eventi che altri applicativi sollevano.

A questo punto un candidato ideale sembra essere MSMQ ( o altro meccanismo di queuing ), tuttavia anche qui un utilizzo crudo dello strumento può portare ad una situazione poco elegante in termini di design. Occorre quindi un meccanismo che ci consenta di lavorare con pubblicazione e sottoscrizione di messaggi che:

  • Garantisca la consegna dei messaggi ai sottoscrittori: se chi pubblica non riceve eccezioni, siamo sicuri che i sottoscrittori riceveranno ( prima o poi, ragionevolmente ) il messaggio a cui sono interessati
  • Garantisca che i messaggi siano ricevuti una ed una sola volta dai sottoscrittori
  • Dia la possibilità di inviare dei messaggi trascorso un determinato lasso di tempo.
  • Sia semplice da usare: non voglio scrivere pagine e pagine di configurazione per mettere in piedi e mantenere la soluzione.

In rete ho trovato questi due pezzi che fanno esattamente questo mestiere:

Sfortunatamente tutti e due sono “fortemente tipati” e non consentono la mera pubblicazione di eventi identificati da una label con un payload puramente testuale, e nemmeno consentono di sollevare degli eventi dilazionati nel tempo. Così ho implementato una soluzione fatta in casa, che non ha molto delle due soluzioni pocanzi descritte - in particolare non ha il concetto di “Saga”, ovvero un messaggio con una storia che si modifica a fronte di altri eventi sul bus –, ma che consente di trasmettere e sottoscrivere con facilità eventi in modalità plain text. La soluzione utilizza come canale sicuro MSMQ, quindi è decisamente MS oriented. Cosa accadrebbe se volessi fare entrare nell’architettura di cui sopra dlle applicazioni scritte in Java ? Un po’ di risposte si possono trovare in questo eccellente blog, in particolare questo articolo su Apache ActiveMQ. Pare essere una soluzione “cutting edge” quando dobbiamo far convivere applicazioni C#-JAVA.

Thursday, 23 September 2010 14:10:59 (GMT Daylight Time, UTC+01:00)  #    Comments [4] - Trackback
Programmin

# Friday, 03 September 2010

Meglio, più in generale, non funziona più l’intellisense per qualsiasi documento XML per cui abbiamo uno ( o più.. ) XSD. Questo accade perchè Visual Studio trova più sorgenti per lo schema dichiarato nell’ XML, nel caso NHibernate abbiamo la seguente dichiarazione in testa:

<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="....." namespace="....." >

Se abbiamo nella solution, o nella directory %Program Files%/Microsoft Visual Studio XXX/Xml/Schemas  uno o più file che definiscono lo stesso namespace, in questo caso

<xs:schema targetNamespace="urn:nhibernate-mapping-2.2" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:nhibernate-mapping-2.2" elementFormDefault="qualified" attributeFormDefault="unqualified">

Visual Studio trova un ambiguità per cui smette di fornire l’intellisense. La situazione si può risolvere facilmente senza disperare:

Basta cliccare sul documento XML da visual studio e dalla finestra proprietà editare la voce Schemas come mostrato qui sotto:

xmlschemas

Così facendo si apre una finestra in cui è possibile abilitare/disabilitare gli schema che visual studio intende usare:

xmlschema2

Deselezionando uno degli schemi ambigui tutto torna a funzionare. Ovviamente questo funziona per qualsiasi ambiguità di schema, non solo per NHibernate.

Friday, 03 September 2010 10:28:31 (GMT Daylight Time, UTC+01:00)  #    Comments [3] - Trackback
NHibernate | Programmin

# Wednesday, 16 June 2010

Probabilmente vi sarà capitato di dovere eseguire qualche azione all’interno del vostro software dipendentemente da trigger innescati da data e/o ora. Per esempio un certo processo non si scatena del week-end, oppure un altro processo deve partire solo a mezzanotte etc etc. Quando queste cose non siano attuabili esternamente con il task scheduler di sistema, sarebbe bello poter avere una libreria pronto uso in .NET. Ebbene questa libreria c’è, ed è il parser di espressioni Cron che trovate qui. Lungi dall’essere un concetto moderno, si tratta di un modo di esprimere range temporali tramite una stringa che arriva da Unix. Oggi chiamiamo una cosa simile “Domain Specific Language”, ma tant’è il concetto è il medesimo. Lungi anche dall’ essere originale il concetto in .NET, la libreria che propongo è una estrapolazione di codice dalla più completa e complessa libreria Quartz.NET, a sua volta un porting della libreria Java Quartz. Se non volete introdurre troppa complessità al vostro deploy aggiungendo una nuova libreria, potete “grabbare” le classi dalla dll di Cron.NET ( sono tre file… ) e compilarli con il vostro progetto, ed avrete a disposizione tutta la potenza delle espressioni Cron ;).

Già, ma come funzionano le espressioni Cron ? Facciamo un paio di esempi:

"* * 10-11 L * ?" = questo trigger è valido l’ultimo del mese dalle 10-11

"* * * ? * MON-FRI" = questo trigger è valido tutti i giorni, a tutte le ore, eccetto il week end.

Ok, questo da l’idea delle potezialità della cosa, ma ovviamente serivirebbe una più estesa…

Documentazione

Una espressione Cron è composta da 6 campi obbligatori, più uno opzionale. Il significato dei campi è schematizzato nella tabella qui sotto:

Campo   Valori permessi   Caratteri speciali ammessi
Secondi   0-59   , - /
Minuti   0-59   , - /
Ore   0-23   , - /
Giorno del mese   1-31   , - ? / L W C
Mese   1-12 or JAN-DEC   , - /
Giorno della settimana   1-7 or SUN-SAT   , - ? / L #
Anno ( Opzionale )   vuoto, 1970-2099   , - /

 

Il carattere jolly ‘*’ può sempre essere usato con il significato di “tutti i valori”, per esempio se metto ‘*’ nel campo minuti significa tutti i minuti.

Il carattere ‘?’ può essere usato solo come Giorno del mese o giorno della settimana. Significa “nessun valore in particolare” e serve quando devo fissare l’altro. Se per esempio fisso il giorno della settimana metterò ‘?’ nel giorno del mese, e viceversa.

Il carattere ‘-‘ lo utilizzo quando voglio specificare un range: per esempio 10-12 nel campo ora significa dalle 10 a tutte le 12.

Uso invece la ‘,’ quando voglio specificare singolarmente più valori possibili: JAN,MAR significa il mese di Gennaio e di Marzo, ma non Febbraio ( e, ovviamente, non tutti gli altri ).

Per specificare dei periodi utilizzo ‘/’: per esempio se metto 0/15 nel campo secondi, significa “ogni 15 secondi”: sarà attivo il trigger quindi ai secondi 15,30,45 etc etc.

Il carattere speciale ‘L’ significa ultimo. Nel campo mese significa semplicemente l’ultimo giorno del mese (30/31/28 o 29 ) in modo concorde con il mese / anno bisestile. Nel campo settimana significa semplicemente 7 ( cioè Sabato ), ma se preceduto da un numero significa “l’ultimo xxxx del mese”. Per esempio se scrivo 6L significa l’ultimo Venerdì del mese.

Il carettere ‘W’ può essere specificato solo nel campo giorno del mese, preceduto da un solo carattere, e significa “il giorno lavorativo più vicino a”. Per esempio se scrivo 1W significa il giorno lavorativo più vicino al primo del mese. Se scrivo LW significa l’ultimo giorno lavorativo del mese.

Per ultimo il carattere ‘#’, utilizzabile solo nel giorno della settimana, indica l’ennesimo xxxx del mese. Per esempio, se mi interessa il terzo venerdì del mese, scriverò 6#3.

Per utilizzare una cron expression basta creare un istanza dell’ogegtto CronExpression, passando la stringa sul costruttore. Ecco un esempio:

   1:          [Test]
   2:          public void LastOfTheMonth()
   3:          {
   4:              var exp = new CronExpression("* * * L * ?");
   5:              Assert.IsTrue(exp.IsSatisfiedBy(new DateTime(2000, 1, 31)));
   6:              Assert.IsTrue(exp.IsSatisfiedBy(new DateTime(2000, 2, 29)));
   7:              Assert.IsFalse(exp.IsSatisfiedBy(new DateTime(2000, 2, 28)));
   8:          }
Wednesday, 16 June 2010 16:52:39 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Cron | Programmin | Recipes

# Thursday, 20 May 2010

Può succedere di dover specificare il nome di un assembly completo, magari in un file di configurazione dell’applicativo. Sfortunatamente non si può tagliare ed incollare la stringa dalla GAC, e bisogna copiare ed incollare i vari pezzi dalla finestra property. Ecco quindi un piccolo tool che mostra tutto il contenuto della GAC e ci permette di copiare l’assembly full name. Eccolo all’opera:

gac

Si accettano complimenti per la meravigliosa interfaccia utente ;).

Nondimeno importante segnalare lo snapshot di codice che consente con facilità di maneggiare la GAC run-time.

Thursday, 20 May 2010 11:48:35 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin | Recipes

# Friday, 14 May 2010

Un problema veramente divertente: collegandomi ad Oracle Express, ma probabilmente lo stesso accade con l’edizione ufficiale, la connessione mi falliva con:


Oracle.DataAccess.Client.OracleException : ORA-12154: TNS: impossibile risolvere l'identificativo di connessione specificato

 

ovvero

 

TNS:could not resolve service name

sulla stessa macchina un’altra applicazione, con la medesima connection string funzionava perfettamente. Ora l’applicazione non funzionante era un test con NUnit, e sulle macchine a 64 bit il setup installa in ProgramFiles( x86 ). Pare che il client Oracle faccia qualche considerazione sulla directory in cui si trova il chiamante, e questo fallisca se ci sono dei caratteri strani ( secondo Oracle ) come le parentesi.Spostando NUnit in un percorso senza parentesi nel nome, tutto funziona. Indagando sul web, ho trovato una conferma al problema ivi descritto. Ho perso la mattinata appresso ad una stupidaggine :-~.

Friday, 14 May 2010 13:18:52 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin

# Tuesday, 27 April 2010

stringtemplate Una libreria che ho utilizzato più volte, ma che è secondo me misconosciuta è ANTLR StringTemplate. E’ parte di un progetto molto più grande ed importante, ovvero il generatore di linguaggi ANTLR, ma qui ci interessa la libreria di templating.

bene: che cosa è ? E’ un template engine, ovvero un componente che, data qualche forma di metadata mi produce un documento di testo il cui contenuto è in qualche modo pilotato dal metadata. Semplice? Forse un esempio è meglio:

Supponiamo di avere un oggetto .NET rappresentante una carrello della spesa di un sito web. Questo rappresenta il nostro metadata. Ora supponiamo che vogliamo scrivere una mail riassuntiva, rappresentante il contenuto del carrello. Ovviamente dovrebbe essere facile modifcare il corpo di questa mail, la sua formattazione, tramite una semplice configurazione. Vediamo come potrebbe essere l’oggetto carrello in questione:

   1: public class Basket
   2: {
   3:     public Basket ()
   4:     {
   5:         BasketItems = new List<BasketItem>();
   6:     }
   7:     public string UserName { get; set; }
   8:     public string UserSurname { get; set; }
   9:     public string UserEmail { get; set; }
  10:     public IList<BasketItem> BasketItems { get; set; }
  11: }
  12:  
  13: public class BasketItem
  14: {
  15:     public string Code { get; set; }
  16:     public string Description { get; set; }
  17:     public int Count { get; set; }
  18:     public decimal UnitPrice { get; set; }
  19:     public decimal Price { get { return UnitPrice * Count; } }
  20: }

Bene, ora scriviamo il template della mail, nel linguaggio di StringTemplate:

   1: Dear Mr $Basket.UserName$,$Basket.UserSurname$
   2:  
   3: Your order contains:
   4:  
   5: Item #    Description        Qty    Unit Cost    Total Cost
   6: $Basket.BasketItems:
   7: {
   8: $it.Code$    $it.Description$        $it.Count$    $it.UnitPrice$        $it.Price$ }
   9: $
  10:  
  11: ______________________________
  12: Thanks for ordering @ ACME.ORG

Da notare, se siete abituati ad altri template engine ( ASP è uno di questi, ma anche T4 ) noterete che questi quando lavorano con collezioni vi fanno scrivere qualcosa come <% for….. %>, facendovi fare il template in modo procedurale. StringTemplate usa invece un approccio dichiarativo $collection: {}$ tra le graffe va il template da ripeter per ogni item della collection. l’elemento corrente è rappresentato da $it$.

Ora bisogna procurere i binari del porting c# di StringTemplate. Mettete come reference nel vostro progetto le dll StringTemplate, ma ricordatevi che per il deploy servono tutte e tre le dll! Fatto questo, per processare il template basta scrivere:

   1: public void TryTemplate()
   2: {
   3:     using( var st =  new StreamReader("mailtemplate.txt") )
   4:     {
   5:         StringTemplate processor = new StringTemplate(st.ReadToEnd());
   6:         //GetBasket ritorna un basket di esempio
   7:         processor.SetAttribute("Basket", GetBasket());
   8:         //ToString processa il template con i dati...
   9:         Console.WriteLine(processor.ToString()); 
  10:     }
  11: }

Per ottenere l’output seguente:

   1: Dear Mr Will,Coyote
   2:  
   3: Your order contains:
   4:  
   5: Item #    Description        Qty    Unit Cost    Total Cost
   6:  
   7: 1    T.N.T Stick        200    0,45        90,00 
   8: 2    Chain Gun        1    1450        1450 
   9: 3    Rockets        60    200        12000 
  10:  
  11: ______________________________
  12: Thanks for ordering @ ACME.ORG

Semplice, e molto efficace. Questo è un esempio base, collection e parametri a me sono sempre bastati, ma la libreria in se è molto più potente: basta leggersi la documentazione. So benissimo che a qualcuno potrebbe venire voglia, di fronte ad una simile problematica, di scriversi una propria versione, ma… okkio, questa è una delle classiche cose semplici che vi portano via, a farla male, una giornata, ma a farla bene… beh, prendete quella già fatta perchè:

  • è collaudata - è usata come back-end di ANTLR, un genaratore di compilatori di successo-
  • è potente – gestisce collection, sostituzioni, inclusioni, join etc etc
  • è open source, ed è in C#.
Tuesday, 27 April 2010 22:31:08 (GMT Daylight Time, UTC+01:00)  #    Comments [3] - Trackback
Programmin

# Wednesday, 21 April 2010

E’ da un po’ che in una certa realtà lo ripeto, ma non avevo mai trovato qualcosa di ufficiale a conforto. Adesso l’ho trovato.

In pratica vediamo perchè, scrivere l’interfaccia ICustomer sull’entità Customer sia sempre un errore. Sempre. Non in qualche caso. Perchè un entità non ha comportamenti. Un entità, per esempio “Customer” ritorna sempre la stessa cosa quando le si chiede, ad esempio, Name. Non c’è un algoritmo od una strategy che possa abbellire o rendere più intelligente il comportamento di Name. E’ poi interessante vedere come larchitettura diventa semplicemente oscena quando ICustomer ha per esempio un IAddress o qualsiasi altra reference…

Poichè è anche considerato un antipattern il Domain Model anemico ( cioè quello composto da sole entità  senza comportamenti) chi si ferma a leggere le prime righe di tutti gli articoli potrebbe pensare che piazzare su una bella interfaccia per ogni entità sia la soluzione. Questo non è però vero: vanno messi “dietro ad un interfaccia” i comportamenti che ha senso pensare che possano variare, ed eventualmente le entità potranno implementare una o più di queste interfacce di behavior. Per esempio Customer potrebbe implementare ICanPlaceHorder, e la logica con cui si piazza l’ordine potrebbe cambiare, ma le entità in gioco sono concrete.

Un interessante find storico sulla situazione è un caso pratico di Prima & Dopo.

Wednesday, 21 April 2010 13:13:42 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin

# Saturday, 17 April 2010

Uno scenario di possibile utilizzo di RemotingAppender è quando si voglia concentrare in un unico punto i log di più app domain hostati nello stesso processo. Infatti un app domain separato vede difatto un’altra configurazione di log4net e, se si volesse per esempio usare un appender su file, ci sarebbe una violazione di condivisione. Per cui RemotingAppender torna utile, consentendo di ribaltare il log di tutti gli app domain sul programma principale. Per prima cosa bisogna mettere il programma principale in grado di ricevere le chiamate di log via remoting. Supponiamo che MyLauncher sia la classe che avvia un processo in un AppDomain separato, potremmo scrivere:

class MyLauncher:MarshalByRefObject,log4net.Appender.RemotingAppender.IRemoteLoggingSink
{

E’ necessario che la classe derivi da MarshalByRef object, in quanto ne esporremo l’istanza via remoting. Deve altresì implementare IRemoteLoggingSink , per fungere da target per i messaggi di log4net.

A questo punto bisogna registrare il canale e l’istanza dell’oggetto atto a fare il sink dei messaggi:

   1:  private void PrepareRemotingLoggerListener()
   2:  {
   3:              var channel = new IpcChannel("log4net"+Guid.NewGuid().ToString());
   4:   
   5:              ChannelServices.RegisterChannel(channel, false);
   6:              var oref = RemotingServices.Marshal(this, null);
   7:              AppenderURI = channel.GetUrlsForUri(oref.URI)[0];
   8:  }

 

Supponiamo di chiamare il codice di sopra per ogni hosting di app domain. Il canale viene battezzato con una componente casuale, in modo da non entrare in conflitto con altre istanze dello stesso programma, o all’interno del medesimo processo se vi fossero più app domain esterni in esecuzione. Per registrare con remoting un istanza già creata si è usato RemotingServices.Marshal(---). Il codice memorizza in una proprietà AppenderURI: questo sarà l’indirizzo da usarsi con remoting appender per definire la proprietà Sink.In pratica è l’indirizzo remoting dell’oggetto “sink” dei messaggi.

Implementare IRemoteLoggingSink è facilissimo:

   1:          #region IRemoteLoggingSink Members
   2:   
   3:          public void LogEvents(log4net.Core.LoggingEvent[] events)
   4:          {
   5:              foreach (var le in events)
   6:                  logger.Logger.Log(le); // logger è un'istanza di 
//ILog nel programma principale, ottenuta con logManager.GetLogger...
   7:          }
   8:   
   9:          #endregion

In questo modo si ottiene un redirect di tutti i messaggi sul logger dell’applicazione principale, con filtri e appender come stabilito dalla configurazione dell’applicativo principale. I messaggi saranno accodati, quindi non ci saranno violazioni di condivisione anche con appender su file fisico.

Nell’app domain basterà configurare log4net in modo da utilizzare RemotingAppender. Il modo più facile è utilizzare BasicConfigurator:

   1:    BasicConfigurator.Configure(CreateAppender());
   2:    logger = LogManager.GetLogger("logger name");
 

CreateAppender si occuperà di creare il RemotingAppender in questo modo:

   1:          private IAppender CreateAppender()
   2:          {
   3:              RemotingAppender ra = new RemotingAppender();
   4:              ra.Layout = new log4net.Layout.SimpleLayout();
   5:              ra.Sink = LoggerURI; // Indirizzo del "sink"
   6:              ra.BufferSize = 512;
   7:              ra.Lossy = false;
   8:              ra.ActivateOptions(); // Ricordarsi di chiamare !!!
   9:              return ra;
  10:          }

Ed il gioco è fatto. LoggerUri è l’indirizzo dell’oggetto di sink ( vedi AppenderURI di prima ). BufferSize è il numero di messaggi da accodare prima di fare effettivamente la chiamata al canale: per ragioni di performance è meglio non abbassare troppo la soglia dei messaggi da raggruppare.

Saturday, 17 April 2010 09:02:23 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
log4net | Programmin

My Stack Overflow
Contacts

Send mail to the author(s) E-mail

Tags
profile for Felice Pollano at Stack Overflow, Q&A for professional and enthusiast programmers
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2017
Felice Pollano
Sign In
Statistics
Total Posts: 157
This Year: 0
This Month: 0
This Week: 0
Comments: 124
This blog visits
All Content © 2017, Felice Pollano
DasBlog theme 'Business' created by Christoph De Baene (delarou) and modified by Felice Pollano