Ivan Porto Carrero

IO(thoughts) flatMap (_.propagandize)

31

Oct
2006

Castle and Workflow Foundation

Yesterday I had to implement workflow in an application.  I had never used it before, and kind of feared that it was going to take me a couple of days to get the hang of it.

Well 2 hours later I had my test workflow going so it isn’t hard at all. If I can do it in that amount of time I’m sure many of you can beat me.

Now I wanted to host the workflow runtime in my container and this is how I went about it:

I made 2 wrapper properties in a settings class :

 

 public static class SeshatAppSettings

    {

        public static WorkflowRuntime WorkflowRuntime

        {

            get

            {

                return HttpContext.Current.Application[“WorkflowRuntime”] as WorkflowRuntime;

            }

            set

            {

                HttpContext.Current.Application[“WorkflowRuntime”] = value;

            }

        }

 

        public static ManualWorkflowSchedulerService WorkflowScheduler

        {

            get

            {

                return WorkflowRuntime.GetService(typeof(ManualWorkflowSchedulerService)) as ManualWorkflowSchedulerService;

            }

        }

 

 

    }

The next thing that comes to mind is, hey I need to start this when my application starts (start, app starts == IStartable). Because the workflow is hosted in asp.net we need a persistence medium. I chose to stay with the standard workflow foundation sql persistence service. And that implementation goes a bit like this :

public class WorkflowHost : IStartable

    {

 

        #region IStartable Members

 

        ///

        /// Starts this instance.

        ///

        public void Start()

        {

            // Create an instance of the workflowRuntime

            WorkflowRuntime workflowRuntime = new WorkflowRuntime();

 

            // Add a manual scheduling service

            ManualWorkflowSchedulerService manualService = new ManualWorkflowSchedulerService();

            workflowRuntime.AddService(manualService);

 

            // Add our persistence

            AddPersistenceService(workflowRuntime);

 

            // Start the workflow runtime

            workflowRuntime.StartRuntime();

 

            // Store it in a place so that the whole application can get to it.

            SeshatAppSettings.WorkflowRuntime = workflowRuntime; 

        }

 

        ///

        /// Stops this instance.

        ///

        public void Stop()

        {

            WorkflowRuntime workflowRuntime = SeshatAppSettings.WorkflowRuntime;

            workflowRuntime.StopRuntime();

 

        }

 

        #endregion

 

 

        private void AddPersistenceService(WorkflowRuntime runtime)

        {

            // Create the SqlWorkflowPersistenceService.

            string connectionString = “Initial Catalog=WorkflowPersistenceStore;Data Source=localhost;Integrated Security=SSPI;”;

            bool unloadOnIdle = true;

            TimeSpan instanceOwnershipDuration = TimeSpan.MaxValue;

            TimeSpan loadingInterval = new TimeSpan(0, 2, 0);

            SqlWorkflowPersistenceService persistService = new SqlWorkflowPersistenceService(connectionString, unloadOnIdle, instanceOwnershipDuration, loadingInterval);

 

            // Add the SqlWorkflowPersistenceService to the runtime engine.

            runtime.AddService( persistService );

 

        }

    }

The last bit we need to take care of is adding our startable component to the container which is pretty easy to do.

 

public class WorkflowFacility : AbstractFacility

    {

        protected override void Init()

        {

            Kernel.AddComponent(“workflow.defaultHost”, typeof(IStartable), typeof(WorkflowHost));

        }

    }

And the final piece is the configuration from the component

 

   

    <facility

          id=”workflow.facility”

          type=”Seshat.Common.WorkflowFacility, Seshat.Common”

    />

That’s it :)

25

Oct
2006

Get Intellisense for Base4 to Edit Schema’s

I don’t know how many of you know about this but I certainly didn’t

When you download the source of base4 which you can find here

And you navigate to the folder C:\Program Files\Base4 Solutions Ltd\Base4 version 2.1\Source**Base4.Storage\Resources**

Or whereever you installed the source and copy the xsd file that is there to the folder C:\Program Files\Microsoft Visual Studio 8**Xml\Schemas** (the folder xml schemas in your visual studio install dir)

When you restart/start visual studio now and you want to edit a schema now the things popup to assist you

Don’t forget to register on the base4 site if you haven’t already.

 

Enjoy

19

Oct
2006

Ubuntu Linux Delivers Great Value

Yesterday evening I installed Ubuntu linux because i want something where i can run mono on that is not running on a windows OS. Lately I’ve got this thing that I want my apps to work cross-platform etc. So I’ve been looking into linux type of OS’es.

The two I like are suse and ubuntu now. Don’t really care much about redhat never have.

In my life as an IT professional I’ve worked with several linux distributions. I don’t know if mandrake is still around but those guys were my first linux installation for firewall implementations, until they got the idea to make their software not free anymore.

Anyway back to ubuntu. It isn’t mac os but it is the easiest linux I’ve used so far. It took me about 30 minutes to install it on my virtual server. And it took anoter 10 to get mono installed on it.

The experience is a whole lot better than having to fiddle all over the place with settings and conf files.

Also I didn’t get the questions about root etc.. Yes this distibution is streamlined and works properly.

And now I finally have a real operating system to put on the iMac that runs on our network. Because MacOS 9 isn’t really networking and Ajax friendly.  The only browser that runs on that iMac is iCab and if you have some experience with iCab then you’ll know that that is not really worth calling a browser.

You can run download an alternate distro of ubuntu that runs on the iMac architecture.

Yes another thing there were only a couple downloads to chose from not the usual list where you almost need to know your motherboard type to get the right version down.

Below is a screenshot. Look at the folder list on the left. It’s not your usual confusing bit but this time it has got real indications of what you want to do. :)

This doesn’t mean I will uninstall Vista and start working on linux from now on but I’m happy about the fact that I don’t have to deal with Mac OS 9 anymore on the intranet for the browser application.

19

Oct
2006

Globat.com Tactics Scandalous

First off I am not going to link to them in this post because that would be like free advertising.

A couple of months ago I took 2 domainname accounts with them and ever since they have been sending me emails every couple of days telling me i need to upgrade my account to some super hosting plan they provide.

I don’t need the friggin hostiing because I have my own server running.
Their constant emailing has made me classify them into a folder that doesn’t show up in my unread mails.

So the latest tactic, which can be called dodgy at the very least, is to send an email about an automatic upgrade and if you fail to opt-out you get a hosting account @ 40USD/month

The letters they sent (I won’t be posting all the other mails of course) :

1st e-mail :

Dear Ivan,

We would like to extend an incredible money-saving offer for medlr.com to thank you for your continued business. In 10 days from this date, we will be providing you with an upgrade to our new TeraByte Pro(tm) Hosting package at the low cost of just $39.95!

This amazing promotion is part of our new special upgrade program of hosting services for our loyal customers. The feedback we received about the upgrade program has been overwhelming. We heard from many of our customers who learned about these upgrade promotions too late or through a friend. Many customers then suggested that we utilize an automatic upgrade process along with some form of an additional incentive to initiate these upgrades. Well, we have listened to your feedback and we are ready to act!

= = = Take Your Domain to the Next Level = = =

As our dedicated domain customer, you will be receiving an automatic upgrade to Globat.com’s award-winning hosting services at a highly-discounted price of only $39.95, instead of $95.40! That breaks down to only $3.33 per month, truly a price that won’t break the bank!

2nd e-mail: (medlr.com has nothing on it, don’t follow)

Dear Ivan,

Congratulation! As per our previous notices, today we have upgraded your hosting account for www.medlr.com to our all new Terabyte Pro™ level and processed your invoice 3629260 in the amount of $39.95 through your credit card account on file.

You are now utilizing Globat.com’s industry-leading TeraByte Plus Hosting(TM) solution! With this upgrade you have also locked in your web hosting pricing for as long as your account remains in this program. You have also qualified to receive one free month of web hosting. To claim your free month please go to:

https://order.globat.com/newOrder/nop_givemonth.pl

and enter the following coupon code

FREEMONTH

into the field provided. Then hit the “Add My Free Month” button. Upon completion of this step your free month will automatically be added to your current account renewal date and you can view your next renewal date at:

https://login.globat.com/cp2/account.php

or view your receipt for this transaction at:

https://login.globat.com/cp2/account.php?page=invoices

 

My response in as much words as i want to give them.

Excuse me I DON’T want anything I DID NOT EXPLICITLY ORDER.
Please cancel your super promotion. I’ll move my domain names because
I think these tactics are scandalous.

I’ll start blogging about it too.. What type of gangsters are you guys.
Where is it legal that I buy stuff because I did not tell you i didn’t  want it.

 

By the time I had finished this email. They already came back to me telling me my money has been refunded.
I’m going to post it anyway because I do think that if that is the direction we are heading then I think nobody will every be safe. Becauwe whenever you buy something somewhere it gives the seller the right to sell you more stuff without even approving the buy.

Well, it feels good to get that of my chest. I’m wondering where these globat people won their award, must be in the gangster syndicate or something, maybe they got it from the Soprano family or something.

18

Oct
2006

Base4 and Castle… The Controller

The next bit for pages in general is that often they represent a list. Often this list is a really long list that you’d like to page through.

I put this functionality into a generic base controller class. The whole idea of my set up is to provide a technique called progressive enhancement of forms.  In short you create an application that works completely without javascript using hyperlinks for gets and submit buttons etc.  Then you attach some css selector classes to the different bits of functionality and you attach these behaviors to the html elements on your page.

I thought that that is a really nice way of enriching the experience of people that allow it to be enriched ;)  And that is the explanation of the ajaxCall bool variable on some of the methods

The first thing is the interface for the class :

using System;

namespace NBlogr.Presenter

{

    public interface INBlogrController

    {

        bool IsCompositeView{get;}

        void Delete(T item, int pageNumber, int pageSize);

        void Delete(T item, int pageNumber, int pageSize, bool ajaxCall);

        void Edit(Guid Id);

        void Edit(Guid Id, bool ajaxCall);

        void JumpToPage(int pageNumber, int pageSize);

        void JumpToPage(int pageNumber, int pageSize, bool ajaxCall);

        void List();

        void List(bool ajaxCall);

        void NavigateBack(int pageNumber, int pageSize);

        void NavigateFirst(int pageSize);

        void NavigateNext(int pageNumber, int pageSize);

        void NavigateBack(int pageNumber, int pageSize, bool ajaxCall);

        void NavigateFirst(int pageSize, bool ajaxCall);

        void NavigateNext(int pageNumber, int pageSize, bool ajaxCall);

        void Show();

        void Show(bool ajaxCall);

        void Save(T item, int pageNumber, int pageSize);

        void Save(T item, int pageNumber, int pageSize, bool ajaxCall);

        void CreateNew();

        void CreateNew(bool ajaxCall);

    }

}

And the next thing is the base class again :

using System;

using System.Collections.Generic;

using System.Text;

using Base4.Storage;

 

namespace NBlogr.Presenter.Abstract

{

    public abstract class AbstractAdminController : AbstractController, INBlogrController where T: class, IItem, new()

    {

        bool compositeView;

        int _pageSize;

 

        public AbstractAdminController()

            : base()

        {

            this.compositeView = false;

            _pageSize = 10;

        }

 

        public AbstractAdminController(bool compositeView) : base()

        {

            this.compositeView = compositeView;

            _pageSize = 10;

        }

 

        internal abstract void getList(int pageNumber, int pageSize, bool skipLayout);

 

 

        internal void getList(int pageNumber, int pageSize)

        {

            getList(pageNumber, pageSize, false);

 

        }

        #region INBlogrController Members

 

        public bool IsCompositeView

        {

            get

            {

                return compositeView;

            }

        }

        public int PageSize

        {

            get

            {

                return _pageSize;

            }

            set

            {

                _pageSize = value;

            }

        }

 

        public void Delete(T item, int pageNumber, int pageSize)

        {

            Delete(item,pageNumber,pageSize,false);

        }

 

        public abstract void Delete(T item, int pageNumber, int pageSize, bool ajaxCall);

 

        public void CreateNew()

        {

            CreateNew(false);

        }

 

        public virtual void CreateNew(bool ajaxCall)

        {

            Edit(Guid.NewGuid(), ajaxCall);

        }

 

        public void Edit(Guid Id)

        {

            Edit(Id, false);

        }

 

        public abstract void Edit(Guid Id, bool ajaxCall);

 

        public void JumpToPage(int pageNumber, int pageSize)

        {

            getList(pageNumber, pageSize);

        }

 

        public virtual void JumpToPage(int pageNumber, int pageSize, bool ajaxCall)

        {

            getList(pageNumber, pageSize, ajaxCall);

        }

 

        public void List()

        {

            getList(1, _pageSize);

        }

 

        public virtual void List(bool ajaxCall)

        {

            getList(1, _pageSize, ajaxCall);

        }

 

        public void NavigateBack(int pageNumber, int pageSize)

        {

            getList(–pageNumber, pageSize);

        }

 

        public void NavigateFirst(int pageSize)

        {

            getList(1, pageSize);

        }

 

        public void NavigateNext(int pageNumber, int pageSize)

        {

            getList(++pageNumber, pageSize);

        }

 

        public virtual void NavigateBack(int pageNumber, int pageSize, bool ajaxCall)

        {

            getList(–pageNumber, pageSize, ajaxCall);

        }

 

        public virtual void NavigateFirst(int pageSize, bool ajaxCall)

        {

            getList(1, pageSize, ajaxCall);

        }

 

        public virtual void NavigateNext(int pageNumber, int pageSize, bool ajaxCall)

        {

            getList(++pageNumber, pageSize, ajaxCall);

        }

 

        public void Show()

        {

            getList(1, _pageSize);

        }

 

        public virtual void Show(bool ajaxCall)

        {

            getList(1, _pageSize, ajaxCall);

        }

 

 

        public void Save(T item, int pageNumber, int pageSize)

        {

            Save(item, pageNumber, pageSize, false);

        }

 

        public abstract void Save(T item, int pageNumber, int pageSize, bool ajaxCall);

 

 

        #endregion

    }

}

Now onto the concrete implementation of my controller class

 

using System;

using System.Collections.Generic;

using System.Text;

using NBlogr.Core;

using NBlogr.Service;

using Base4.Storage;

using Castle.MonoRail.Framework;

using NBlogr.Common.Base4Integration;

using Castle.Services.Transaction;

 

namespace NBlogr.Presenter

{

 

    public class BlogController : Abstract.AbstractAdminController

    {

        BlogService blogService;

        

        public BlogController(IService blogService)

            : base(true)

        {

            this.blogService = blogService as BlogService;

             

             

        }

 

        private void internalGetList(int pageNumber, int pageSize)

        {

            int pageCount;

 

            IItemList blog = blogService.GetAll(“Name”, pageNumber, pageSize, out pageCount);

 

            internalMapProperties(blog , pageNumber,pageSize, pageCount);

        }

 

        private void internalMapProperties(IItemList blogs, int pageNumber, int pageSize, int pageCount)

        {

            int totalCount = blogs.Count;

 

            if (PropertyBag[“blog”] == null)

                PropertyBag.Add(“blog”, new Blog());

            

            PropertyBag.Add(“blogs”, pageNumber);

            PropertyBag.Add(“index”, pageNumber);

            PropertyBag.Add(“count”, totalCount);

            PropertyBag.Add(“pageCount”, pageCount);

            PropertyBag.Add(“pageSize”, pageSize);

        }

 

        internal override void getList(int pageNumber, int pageSize, bool skipLayout)

        {

 

            internalGetList(pageNumber, pageSize);

 

            if (!skipLayout)

                RenderView(“Show”, false);

            else

                RenderView(“List”, true);

        }

 

 

 

 

        public override void Edit(Guid Id, bool ajaxCall)

        {

            Blog blog = blogService.GetById(Id);

 

            PropertyBag.Add(“blog”, blog);

             

            if (!ajaxCall)

            {

                getList(1, 0);

            }

            else

                RenderView(“Save”, ajaxCall);

 

        }

 

        public override void Delete([DataBind(“blog”)] Blog item, int pageNumber, int pageSize, bool ajaxCall)

        {

            blogService.Delete(item);

 

            if (!ajaxCall)

            {

                getList(1, 0);

            }

            else

                RenderText(getJsonResponse(“Contract deleted successfully”));

 

        }

 

        public override void Save([DataBind(“blog”)] Blog item, int pageNumber, int pageSize, bool ajaxCall)

        {

            PropertyBag.Add(“blog”, blogService.Save(item));

 

            if (!ajaxCall)

            {

                getList(1, 0);

            }

            else

                RenderText(getJsonResponse(“Contract saved successfully”));

        }

 

        private string getJsonResponse(string message)

        {

            return “{result: true, content: ‘” + message + “’}”;

        }

 

    }

}

17

Oct
2006

Base4 and Castle … The Service

UPDATE: THE SERVICE CLASS HAS CHANGED AGAIN.
Scratch the update.. it assumes that the object is already in the database which isn’t always the case. For this type of synchronisation to work I need some more time. So I reverted this back to the old version

This post continues my previous posts on using base4 and castle together.

I’m sorry but there is no Active record in the base4 way of doing things.

The previous posts you would be intrested in can be found here :

Hosting base4 inside your web application
Base4 and Castle continued… The facility

Today I want to show you the abstract service class that I use so the controller only talks to the service and never to the dataobject directly. 

It is not a very big class but i found it pretty useful.  As it turns out almost all my service classes in a previous project started to look very much alike so I grouped that functionality in an abstract class. There is one thing you have to look out for and that is if you have something like audit fields (Created, LastModified,…..) you need to first get the object from the datastore and then set just the changed properties on the retrieved object.

The first thing you need is the contract/interface for the service. I expect this interface to grow over time so keep checking NBlogr for updates but this where I am at now :)

using System;

namespace NBlogr.Service

{

    public interface IService

    where T : class, Base4.Storage.IItem, new()

    {

        void Delete(T item);

        Base4.Storage.IItemList GetAll(string sortExpression, int pageNumber, int pageSize, out int pageCount);

        T GetById(Guid Id);

        T Save(T item);

    }

}

And here’s the abstract class :

using Base4.Storage;

using Castle.Core.Logging;

using NBlogr.Common.Base4Integration;

 

using User = NBlogr.Core.User;

using Castle.Services.Transaction;

using Base4.Storage.Utils;

 

namespace NBlogr.Service.Abstract

{

 

    [Transactional]

    public abstract class AbstractService : IService where T : class,IItem,new()

    {

        internal readonly ILogger logger;

        internal IDataObject dao;

 

 

        public AbstractService(ILogger logger, IDataObject dao)

        {

            this.logger = logger;

            this.dao = dao;

        }

 

        [Transaction(TransactionMode.Requires)]

        public virtual T Save(T item)

        {

            return dao.Save(internalPrepareSave(item));

        }

 

        internal abstract T internalPrepareSave(T item);

 

        [Transaction(TransactionMode.Requires)]

        public virtual void Delete(T item)

        {

            dao.Delete(item);

        }

 

        public virtual IItemList GetAll(string sortExpression, int pageNumber, int pageSize, out int pageCount)

        {

            return dao.FindAll(sortExpression, pageNumber, pageSize, out pageCount);

        }

 

        public virtual T GetById(Guid Id)

        {

            return dao.GetById(Id);

        }

 

 

    }

}

And the last bit could be an implementation of this service.  I don’t have companies in NBlogr. I took this class from another project :

using System;

using Castle.Services.Transaction;

using Castle.Core.Logging;

using NBlogr.Common.Base4Integration;

using NBlogr.Core;

 

namespace NBlogr.Service

{

    [Transactional]

    public class BlogService : Abstract.AbstractService

    {

 

        public BlogService(ILogger logger, IDataObject dao)

            : base(logger, dao)

        {

 

        }

 

        internal override Blog internalPrepareSave(Blog item)

        {

            Blog itemToSave = GetById(item.Id);

 

            if (!itemToSave.Internals.InDatabase)

            {

                itemToSave.Created = DateTime.Now;

                itemToSave.Creator = CurrentUser;

            }

 

            itemToSave.AkismetKey = item.AkismetKey;

            //TODO: finish mapping properties

 

            return itemToSave;

        }

    }

}

This concludes the service for use with base4.  I hope you find it as useful as I do.
If somebody knows improvements etc. Please let me know I am always keen to learn.

I also realise that I can just call save on an IItem object but i like every tier to have it’s specific task so the dataobject doesn’t do much else than call IItem.Save() for now

16

Oct
2006

No Post Yesterday I Was Moving From Team Foundation to Subversion

I was using Team foundation workgroup edition  for the past year to manage collaborating with a couple of people. And the other stuff you use team foundation for.

I like Team foundation a lot but unfortunately am not rich enough to buy licenses for everybody that needs to log bugs etc.  To avoid all those licensing issues we have switched over to a combination of open source packets.The total cost of the operation is about 450NZD for unlimited licenses.

So what is the setup :

The whole experience took about 6 hours to configure ant that is because I didn’t know apache at all and getting it to work with SSL did take me some time until I found the install guide that had a link to compiled binaries. I’m not that good in compiling ansi-C on a windows 2k3 64-bit machine without the windows sdk installed. Not to mention all these little tools you have to download all over the place. Most of them i had never heard of before, so yes I was happy to find that guide.

We are now completely switched over, except for the work items that are currently in team foundation server. It was a good moment to do this because the most of the previous projects have come to an end and the new wave is rolling in. I got really tired of retyping the emails etc I got into the issue tracker, simply becasue I couldn’t afford it.

14

Oct
2006

Redirecting Based on a Role in the Loggedin Event of the Login Control

Today I had to fix a website that is built by the guys over in Russia. Completely asp.net no castle involved whatsoever. There is not even an O/R mapper in the application.

Real basic stuff. Sql datasource gridview/formview type of things.

The client asked me to do redirect their site to certain pages depending on which role the user is in.

Cool. Let’s handle the LoggedIn event of the Login control. This is what I started of with. Makes perfect sense to me that that should work.

    protected void Login1_LoggedIn(object sender, EventArgs e)

    {

        if (Roles.IsUserInRole(“School”))

            Response.Redirect(string.Format(“~/Career/School.aspx?ID={0}”, CareerUser.GetUserTypeId(Membership.GetUser().ProviderUserKey)));

        if (Roles.IsUserInRole(“Student”))

            Response.Redirect(string.Format(“~/Career/Student.aspx?ID={0}”, CareerUser.GetUserTypeId(Membership.GetUser().ProviderUserKey)));

        else

            Response.Redirect(“~/Login.aspx”);

    }

Now as expected that didn’t do the trick. But this does

    protected void Login1_LoggedIn(object sender, EventArgs e)

    {

        /*I have to do it this way because there is no cookie set yet so Membership.GetUser() returns an empty object.

        * The same goes for the roles. No cookie means no data for the standard function.

        */

        Login login = sender as Login;

        Guid userObjectKey = (Guid)Membership.GetUser(login.UserName).ProviderUserKey;

        Guid userId = CareerUser.GetUserTypeId(login.UserName, userObjectKey);

        if (Roles.IsUserInRole(login.UserName, “School”))

            Response.Redirect(string.Format(“~/Career/School.aspx?ID={0}”, userId));

        if (Roles.IsUserInRole(login.UserName, “Student”))

            Response.Redirect(string.Format(“~/Career/Student.aspx?ID={0}”, userId));

        else

            Response.Redirect(“~/Login.aspx”);

    }

Just thought I’d share this for other people that have this problem.

Anyway this is wtf numero 10 today. And I haven’t had these moments in the last couple of weeks, using castle and all, so I was feeling a little nostalgic about the whole thing.

To top