Joe

Mister Doctor Professor
posts - 23, comments - 17, trackbacks - 101

Monday, June 02, 2008

ActiveRecord: could not resolve property

 

Because this has bitten me in the ass more than once.

When using ActiveRecord to query the database the following error occurs:

Failed: Castle.ActiveRecord.Framework.ActiveRecordException : Could not perform SlicedFindAll for ShoppingCart
  ----> NHibernate.QueryException : could not resolve property: UserId of: MyDomain.Core.ShoppingCart

ProblemThe Property values in the criteria are case sensitive.

Where the query looks like this:

        public ShoppingCart LoadCurrentBasket(string userId, Guid customerId)
        {
            DetachedCriteria criteria = DetachedCriteria.For<ShoppingCart>();
            criteria.Add(Expression.Eq("UserId", userId));
            criteria.Add(Expression.Eq("CustomerId", customerId));

            return ActiveRecordMediator<ShoppingCart>.FindFirst(criteria);
        }

 

And the class looks like this: 

    [ActiveRecord(Table = "Basket")]
    public class ShoppingCart
    {
        private IList<ShoppingCartItem> _LineItems = new List<ShoppingCartItem>();

        [PrimaryKey(PrimaryKeyType.Guid)]
        public Guid BasketID { get; set; }

        [Property("CustomerId")]
        public Guid CustomerID { get; set;}

        [Property("UserID")]
        public string UserID { get; set;}

///
///
///

    }

posted @ Monday, June 02, 2008 1:24 PM | Feedback (7) |

Thursday, January 17, 2008

ExecuteList<T> and ExecuteItem<T>

In a hand coded data-access layer you often end up with some variant of this pattern when translating a datareader to an entity.

public List<ICarrier> LoadList()
{    
    List<ICarrier> items = new List<ICarrier>();
    using (SafeDataReader reader = new AdoHelper().ExecuteDataReader(CommandType.Text, "Select * from _lu_Carrier"))
    {
        while (reader.Read())
        {
            items.Add(MapCarrier(reader));
        }
    }
    return items;
}


where your ADOHelper does whatever it needs to do to connect to the database and run your query.  This is generally coupled with "mapping" method (MapCarrier in this instance)

public static ICarrier MapCarrier(SafeDataReader dr)
{
    ICarrier carrier = new Carrier();
    carrier.Name = dr.GetString("Name");
    carrier.Phone = dr.GetString("Phone");
    carrier.SCAC = dr.GetString("SCAC");
    carrier.URL = dr.GetString("URL");

    return carrier;
}

I think this is a fairly standard approach, and it works well.  However, it's tedious and can lead to lots of repetition as your DAL grows.  Enter the magic of generics and delegates which allow the code in the first block to become something like this:

public List<ICarrier> GetList()
{
    return new AdoHelper().ExecuteList<ICarrier>(CommandType.Text, 
                                                 "Select * from _lu_Carrier", 
                                                 MapCarrier);
}

 

ExecuteList<T> is overloaded to allow for a few variations which all route to this:

public List<T> ExecuteList<T>(CommandType commandType, string commandText, Func<T, SafeDataReader> mappingCallback, params SqlParameter[] parameters)
{
    List<T> collection = new List<T>();
    using (SafeDataReader reader = ExecuteDataReader(commandType, commandText, parameters))
    {
        while (reader.Read())
        {
            collection.Add(mappingCallback(reader));
        }
    }
    return collection;
}

The "magic" here is the Func<T SafeDataReader> delegate being passed in.  This is (lifted directly from Rhino.Commons) and is declared as

public delegate TResult Func<TResult, S>(S s1);

ExecuteItem<T> is similar, but returns a single entity instead of a collection

public T ExecuteItem<T>(CommandType commandType, string commandText, Func<T, SafeDataReader> mappingCallback, params SqlParameter[] parameters)
{
    using (SafeDataReader reader = ExecuteDataReader(commandType, commandText, parameters))
    {
        while (reader.Read())
        {
            return mappingCallback(reader);
        }
    }
    return default(T);
}

 

Over even a small project this can save a lot of typing and a substantial amount of time.

posted @ Thursday, January 17, 2008 3:08 PM | Feedback (4) |

Thursday, January 10, 2008

Extension Methods and Fluent Interfaces

File under stupid semantic tricks.

Extension methods (which seem ripe for abuse and all kinds of bad code) provide some great flexibility when working with fluent interfaces.  For example, take the following extensions to the date class

 

    public static class ExtensionMethods
    {
        public static bool IsBefore(this DateTime dt, DateTime other)
        {
            return other > dt;
        }
    }

 

This allows us to better express our intent

 

    public class WorkingWith
    {
        public void Test1()
        {
            DateTime myBirthday = new DateTime(2008, 3, 1);
            DateTime christmans = new DateTime(2008, 12,25);
            
            if (myBirthday.IsBefore(christmans))
            {
                Console.WriteLine("yes");
            }
 
        }
    }

Which is nice, but can get better by wiring in a call to a utility class

 

    public static class ExtensionMethods
    {
        ...
 
        public static IsBetweenDateRange IsBetween(this DateTime dt, DateTime lowDate)
        {
            return new IsBetweenDateRange(dt, lowDate);
        }
    }

which allows a readable (as opposed to parse-then-translate-able) statement such as

        public voidTest2()
        {
            DateTime myBirthday = new DateTime(2008, 3, 1);
            DateTime halloween = new DateTime(2008, 10, 31);
            DateTime christmans = new DateTime(2008, 12,25);
            
            if (halloween.IsBetween(myBirthday).And(christmans))
            {
                
            }
        }

 

The utility class in this case being

    public class IsBetweenDateRange
    {
        private DateTime _baseDate;
        private DateTime _lowDate;
 
        public IsBetweenDateRange(DateTime baseDate, DateTime lowDate)
        {
            _baseDate = baseDate;
            _lowDate = lowDate;
        }
 
        public bool And(DateTime highDate)
        {
            return _baseDate > _lowDate && _baseDate < highDate;
        }
 
    }

posted @ Thursday, January 10, 2008 8:52 AM | Feedback (4) |

Tuesday, January 08, 2008

Using Binsor in an Asp.Net Application

This tutorial covers using Binsor2 in an asp.net application. 

Because of different versions and the dearth of information available about getting started with Binsor, I would recommend getting the files listed later in this article directly from the trunk as the syntax between versions 1 and 2 has changed.  There is a readme file covering how to build the trunk, and I would add the following points to that:

1.  Run your "Visual Studio Command Line" so that MSBuild is in your path (Start|Programs|Microsoft Visual Studio 200x|Visual Studio Tools)
2.  I had to add a folder called "lib" to "C:\Program Files\Microsoft SDKs\Windows\v6.0A" to get the build to run.

Getting Started

Create a new library called BinsorDemo.Domain, and add the following domain entities (fields made public for brevity).

    public class Account
    {
        public string FirstName;
        public string LastName;
        public string State;
        public decimal Balance;
        public List<Purchase> PurchaseHistory;
    }

    public class Purchase
    {
        public DateTime Date;
        public decimal Amount;
        public decimal AmountDue;

        public Purchase(DateTime date, decimal amount, decimal amountDue)
        {
            Date = date;
            Amount = amount;
            AmountDue = amountDue;
        }
    }

 

Next, add the following interfaces.  These are the services that we will "wire-up" using Binsor.

    public interface ICreditService
    {
        decimal GetCreditRemaining(Account c);
    }

    public interface IInterestCalculator
    {
        decimal CalculateInterestOn(Account account);
    }

Finally, add the following concrete implementations of the interfaces.

    public class CreditService : ICreditService
    {
        private decimal _CreditLimit;

        public CreditService(decimal creditLimit)
        {
            _CreditLimit = creditLimit;
        }

        public decimal GetCreditRemaining(Account account)
        {
            decimal totalOutstanding = 0M;
            account.PurchaseHistory.ForEach(delegate (Purchase p){totalOutstanding += p.Amount - p.AmountDue;});
            return _CreditLimit = totalOutstanding;
        }
    }

    public class InterestCalculator : IInterestCalculator
    {
        private readonly decimal _InStateAmount;
        private readonly decimal _OutOfStateAmount;
        private readonly string _ThisState;

        public InterestCalculator(decimal inStateAmount, decimal outOfStateAmount, string thisState)
        {
            _InStateAmount = inStateAmount;
            _OutOfStateAmount = outOfStateAmount;
            _ThisState = thisState;
        }

        private decimal DetermineInterestRateFor(Account account)
        {
            if (account.State.Equals(_ThisState, StringComparison.InvariantCultureIgnoreCase))
                return _InStateAmount;
            else
                return _OutOfStateAmount;
        }

        public decimal CalculateInterestOn(Account account)
        {
            decimal interestRate = DetermineInterestRateFor(account);
            decimal interest = 0M;

            account.PurchaseHistory.ForEach(delegate (Purchase purchase){interest += purchase.AmountDue*interestRate;});
            return interest;
        }
    }

Create the Web Application

Next create a web application called BinsorDemo.Web.  Add the following references (as mentioned above, I would pull these directly from the trunk - these will be located in [x:\trunk]\rhino-commons\Rhino.Commons\bin\Debug).

Boo.Lang
Boo.Lang.Compiler
Boo.Lang.Parser
Castle.Core
Castle.Windsor
log4net
Rhino.Commons
BnisorDemo.Domain

 

Add a new file to the root of the application called "windsor.boo", and add the following to it.

import BinsorDemo.Domain

creditLimitAmount as decimal = 500.0

component 'credit_service', ICreditService, CreditService:
                        creditLimit=creditLimitAmount


inState as decimal = .05
outOfState as decimal = .075
myState = 'MD'

component 'interest_service', IInterestCalculator, InterestCalculator: 
                            inStateAmount = inState
                            outOfStateAmount = outOfState
                            thisState = "MD"

 

Next add a global code file (global.asax), if it is not already there.  Add the following to this file.  The Application_Start event is where the magic takes place.

 

 public class Global : System.Web.HttpApplication
    {
        private static IWindsorContainer container;

        public static IWindsorContainer Container
        {
            get { return container; }
        }

        protected void Application_Start(object sender, EventArgs e)
        {
            XmlConfigurator.Configure();
            string binsorConfigFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "windsor.boo");
            container = new WindsorContainer();
            BooReader.Read(container, binsorConfigFilePath);
        }
    }

Finally

Let's run a quick test to see if everything is working OK.  Add the following to a WebForm's Page_Load event, and navigate to that page.

    protected void Page_Load(object sender, EventArgs e)
        {
            Account demoAccount = new Account();
            demoAccount.FirstName = "Foo";
            demoAccount.LastName = "Bar";
            demoAccount.Balance = 250;
            demoAccount.State = "MD";

            demoAccount.PurchaseHistory = new List<Purchase>();
            demoAccount.PurchaseHistory.Add(new Purchase(new DateTime(2007, 10, 16), 100M, 15M));
            demoAccount.PurchaseHistory.Add(new Purchase(new DateTime(2007, 11, 16), 10M, 0));
            demoAccount.PurchaseHistory.Add(new Purchase(new DateTime(2007, 12, 16), 250M, 35M));

            Response.Write(Global.Container.Resolve<ICreditService>().GetCreditRemaining(demoAccount));
            Response.Write("<hr/>");
            Response.Write(Global.Container.Resolve<IInterestCalculator>().CalculateInterestOn(demoAccount));
        }
 

posted @ Tuesday, January 08, 2008 12:40 PM | Feedback (7) |

Thursday, December 13, 2007

Rhino Mocks Introductory Tutorials

Another Introduction to Rhino Mocks

Another Introduction to Rhino Mocks - Part II


posted @ Thursday, December 13, 2007 12:17 PM | Feedback (2) |

Thursday, November 15, 2007

lsass.exe Crashes During Login

On HP laptops with the built-in credential manager (fingerprint scanner) running with VMWare (server) running, lsass.exe will crash while logging in telling you that your computer will reboot in 60 seconds.  As documented here, there are a couple of workarounds for this

  1. Disable the fingerprint scanner
  2. Set the VMWare services to Manual instead of Automatic (start | run | services.msc)

Because it's almost 2010 and I want Minority Report style eyeball scanning technology, I settled for number 2 and set following services to Manual.  (they sounded important, and had a circular dependency on each other)

  • VMWare Authorization Service [VMAuthdService]
  • VMWare Registration Service [VMServerdWin32, RPC Service]

I left the other 3 services alone as they seemed innocent enough, and had no dependencies.  They were

  • VMWare DHCP Service [VMNetDHCP]
  • VMWare NAT Service [VMWare NAT Service]
  • VMWare Virtual Mount Manager Extended [vmount2]

 

Obviously, the services I stopped need to be running for the VMWare application to run, and they do not start without being told to (I was hoping that launching the application would trigger the loading of these).  After verifying that I could start and stop these services from the command line, I added a batch file named VMWareServices to my "Startup" folder with the following commands:

net start vmauthdservice
net start vmserverdwin32

AND IT STILL FAILED

Next I tried reversing the order these were called in.  This stopped the crash, but for whatever reason vmserverdwin32 did not start.

I then added a ping delay delay of 60 seconds.  Still no good.  I could get to the command prompt and type the net start command, and I received the message that the service didn't start in a timely fashion.  Immediately try again and it fires up.  Same results as 120 and 180 seconds.

@#$*&($*!#^)&%^*^#$^@

Out of frustration more than anything else, I set the services back to the original order with a 120 second delay and it worked.  Knocked it down to 60 seconds, and it's still good.

SOLUTION

It's irritating, and it's far from optimal, and I'm not sure why I need the delay, but it does the job.

ping 127.0.0.1 -n 60
net start vmauthdservice
net start vmserverdwin32

posted @ Thursday, November 15, 2007 4:14 PM | Feedback (2) |

Wednesday, October 31, 2007

Refactoring: From Disaster to Decorator

 

Messy In Working Effectively with Legacy Code Michael Feathers describes legacy code as "code without tests".  I describe it as the crap I wrote 4 years ago. 

The code block that I was working with looked like the image to the left.  It was a single method that was 278 lines long, had a cyclomatic complexity of 80 and (according to DevExpress' DXCore) a horrific maintenance complexity of 1766.   The method was used to validate warranty claims and had conditional branching for "Save", "Get Price Details", and "Place".  In addition, the "Get Price Details" and "Place" blocks had conditional statements to handle cases for specific countries.

The first step in refactoring this to something resembling a maintainable state was to extract the method in to it's own WarrantyValidator class.  This meant creating a constructor that accepted all of the variables and fields from the original object that would be required to do the validation.  In this case I needed the Warranty Claim being validated, the user's country, a list to store the failure messages in (a discussion for another day) and the number of items on the claim. 

 

 

 

 

 

 

 

 

 

The end result is a class that looked like this (this is "blog" code)

    public class ValidateClaim
    {
        private readonly WarrantyClaim ClaimToValidate;
        private int NumberOfItemsOnClaim;
        private readonly string CountryCode;
        private readonly List<string> ErrorCodes;


        public ValidateClaim(WarrantyClaim claimToValidate, int numberOfItemsOnClaim, string countryCode, List<string> errorCodes)
        {
            ClaimToValidate = claimToValidate;
            NumberOfItemsOnClaim = numberOfItemsOnClaim;
            CountryCode = countryCode;
            ErrorCodes = errorCodes;
        }


        public bool Validate(string ActionName)
        {
            if (ActionName == "SAVE")
            {
                //save-specific validation for example
                if (string.IsNullOrEmpty(ClaimToValidate.AuthorizationNumber))
                {
                    ErrorCodes.Add("12345678-1234-1234-123456789012");
                }
            }
            else if (ActionName == "PRICE")
            {
                //pricing-specifi validation
                
                if (CountryCode == "US")
                {
                    //us specific validation
                }
                else if (CountryCode == "GB")
                {
                    //gb specific valudation
                }
            }
            else if (ActionName == "PLACE")
            {
                if (CountryCode == "US")
                {
                    //us specific validation
                }
                else if (CountryCode == "GB")
                {
                    //gb specific valudation
                }
            }

            /*
             * lots of validation no matter what the action is
             * ...
             * ...
             * ...
             */

            return ErrorCodes.Count == 0;
        }
    }

 

The next step (which I guess probably should have been the first) was generate tests to cover as much code as possible.  In doing this I found that I needed a couple of additional parameters in my constructor as data that was exposed through the properties of the WarrantyClaim wasn't available without banging against the database.  At the end of this phase, I had managed to get about 95% test coverage, and some analysis of the WarrantyClaim suggested that a couple of cases for which validation code existed were no longer possible.  With 76 "green" tests I began breaking up the object and re-running the tests for verification after every change.

The first change was to create the WarrantyValidatorBase.  This abstract class contained a constructor identical to my WarrantyValidator class and an abstract method called Validate. 

public abstract class WarrantyValidatorBase
{
    protected readonly WarrantyClaim ClaimToValidate;
    protected int NumberOfItemsOnClaim;
    protected readonly string CountryCode;
    protected readonly List<string> ErrorCodes;

    public WarrantyValidatorBase(WarrantyClaim claimToValidate, int numberOfItemsOnClaim, string countryCode, List<string> errorCodes)
    {
        ClaimToValidate = claimToValidate;
        NumberOfItemsOnClaim = numberOfItemsOnClaim;
        CountryCode = countryCode;
        ErrorCodes = errorCodes;
    }

    public abstract void Validate();
}

From here I moved all of the Save validation to it's own class, and repeated this with the Price Details and Place validation.  Next, I extracted the country specific validation into separate classes - WarrantyPlaceValidationUS, WarrantyPlaceValidationGB, etc.    

    public class WarrantyPlaceValidator : WarrantyValidatorBase
    {
        public WarrantyPlaceValidator(WarrantyClaimWarrantyClaim claimToValidate, int numberOfItemsOnClaim, 
                                        string countryCode, List<string> errorCodes) 
            : base(claimToValidate, numberOfItemsOnClaim, countryCode, errorCodes)
        {
        }


        public override void Validate()
        {
            //place specific validation

            if (CountryCode == "US")
            {
                new WarrantyPlaceValidatorUS(ClaimToValidate, NumberOfItemsOnClaim, CountryCode, ErrorCodes).Validate();
            }
            else if (CountryCode == "GB")
            {
                //gb specific valudation
                new WarrantyPlaceValidatorGB(ClaimToValidate, NumberOfItemsOnClaim, CountryCode, ErrorCodes).Validate();
            }
        }
    }


    public class WarrantyPlaceValidatorUS : WarrantyValidatorBase
    {
        public WarrantyPlaceValidatorUS(WarrantyClaim claimToValidate, int numberOfItemsOnClaim,
                                        string countryCode, List<string> errorCodes)
            : base(claimToValidate, numberOfItemsOnClaim, countryCode, errorCodes)
        {
        }


        public override void Validate()
        {
            //us specific validation
        }
    }


    public class WarrantyPlaceValidatorGB : WarrantyValidatorBase
    {
        public WarrantyPlaceValidatorGB(WarrantyClaimclaimToValidate, int numberOfItemsOnClaim,
                                        string countryCode, List<string> errorCodes)
            : base(claimToValidate, numberOfItemsOnClaim, countryCode, errorCodes)
        {
        }


        public override void Validate()
        {
            //GB specific validation
        }
    }

 

After this refactoring, my original diagram looked a little like this: 

BrokenUp2

Again, after each change, I re-ran my tests and made sure that everything was still ok. 

So far, so good.  The new code was a far easier to maintain than the previous approach.  I now had small testable classes, which were easy to read and instantly understandable.  They all had very specific purposes and were very loosely coupled.  However, there was still an awful lot of duplication in the constructors, and I was not happy with the country specific cases being handled this way. 

I knew this needed to be resolved, but I was a little stuck.  I am pretty clueless when it comes to design patterns and to be perfectly honest, the GoF book makes me want to gouge my eyes out.  Fortunately, the Head First book and Alex Henderson's Windsor tutorial gave me a little insight into the Decorator pattern.  Time for some more refactoring...

The WarrantyValidatorBase ended up looking like this.

public abstract class WarrantyValidatorBase
    {

        private List<string> _ErrorPhrases;
        private WarrantyClaim _WarrantyClaim;
        private WarrantyValidatorBase _ChildValidator;
        private int _NumberOfPartsInBasket;

        public WarrantyValidatorBase()
        {
        }

        public WarrantyValidatorBase(WarrantyValidatorBase decorateWith)
        {
            _ChildValidator = decorateWith;
        }

        protected void AddErrorId(string errorGuid)
        {
            _ErrorPhrases.Add(errorGuid);
        }

        internal ToolCommerce.Warranty WarrantyClaim
        {
            get { return _WarrantyClaim; }
            set { _WarrantyClaim = value; }
        }

        internal List<string> Phrases
        {
            get { return _ErrorPhrases; }
            set { _ErrorPhrases = value; }
        }

        internal int NumberOfPartsInBasket
        {
            get { return _NumberOfPartsInBasket; }
            set { _NumberOfPartsInBasket = value; }
        }

        internal WarrantyValidatorBase ChildValidator
        {
            get { return _ChildValidator; }
            set { _ChildValidator = value; }
        }


        public abstract void Validate();

        protected virtual void ValidateChild()
        {

            if (_ChildValidator!=null)
            {
                _ChildValidator.Phrases = _ErrorPhrases;
                _ChildValidator.WarrantyClaim = _WarrantyClaim;
                _ChildValidator.NumberOfPartsInBasket = _NumberOfPartsInBasket;
                _ChildValidator.Validate();
            }
        }

    }

 

Note the addition of the _ChildValidator field and the constructor changes to accept another object deriving from WarrantyValidatorBase.  I also moved the arguments from the constructor to properties - this just seemed a little cleaner to me.  The ValidateChild method is key here.  It passes the validation request down the "decorator chain" to its ChildValidator, and its ChildValidator to its ChildValidator and so on.  This approach allowed me to change my WarrantyPlaceValidator to this:

public class WarrantyPlaceValidator : WarrantyValidatorBase
{
    public WarrantyPlaceValidator(WarrantyValidatorBase childValidation)
    {
        ChildValidator = childValidation;
    }

    public override void Validate()
    {
        //place specific validation
        //notice that there is nothing specific to countries in here any more
        ValidateChild();
    }
}

 

End Result

The awesomeness of the decorator is that I can chain these together and add functionality without having to clutter up my classes with conditionals and branching logic.

public void ValidateForPlace(Warranty claimToValidate, int numberOfItemsOnClaim, string countryCode, List<string> errorCodes)
{
    WarrantyValidatorBase validator = null;

    if (countryCode == "US")
    {
        validator = new WarrantyValidator(new WarrantyPlaceValidator(new WarrantyPlaceValidatorUS()));
    }
    else if (countryCode == "GB")
    {
        validator = new WarrantyValidator(new WarrantyPlaceValidator(new WarrantyPlaceValidatorGB()));
    }
    else
    {
        //no country specific validation
        validator = new WarrantyValidator(new WarrantyPlaceValidator());
    }

    validator.NumberOfPartsInBasket = numberOfItemsOnClaim;
    validator.Phrases = errorCodes;
    validator.WarrantyClaim = claimToValidate;
    validator.Validate();
}

 

If new functionality is required, it can be encapsulated and then "chained" in.

posted @ Wednesday, October 31, 2007 4:01 PM | Feedback (3) |

Monday, October 15, 2007

Unable to create a manifest resource name for ...

In case I run into this again...

During a build for a .net solution using Team Foundation Server, we got an error that said.

error MSB3041: Unable to create a manifest resource name for "NewService\WarrantyUpload.aspx.resx". Could not find a part of the path 'E:\Builds\My App\Source\Code\MyApp.WebApp\Folder\File.aspx.vb'.

What had happened was that \Folder had been accidentally dragged and dropped into another folder.

posted @ Monday, October 15, 2007 4:20 PM | Feedback (9) |

Thursday, September 20, 2007

Fluent Interface Take 2

 

Where this is code that is duplicated all over the place with only minor alterations for the data source and the default value:

DropDownList cbo = new DropDownList();
cbo.DataSource = PersonMotherObject.GetPersons();
cbo.DataTextField = "FirstName";
cbo.DataValueField = "LastCommaFirst";
cbo.DataBind();

cbo.Items.Insert(0, new ListItem("", "----- SELECT -----"));

foreach (ListItem listItem in cbo.Items)
{
    listItem.Selected = listItem.Value == "Flanders, Ned";
}

 

This reads a lot easier, and gets us a little more DRY:

new ComboBoxSetter<Person>()
                .WithListControl(ddl)
                .WithDataSourceOf(PersonMotherObject.GetPersons())
                .SetSelectedWhereTextIs("Ned")
                .AddLeadingItemOf("", "-----Select------")
                .TheDataTextFieldIs("FirstName")
                .TheDataValueFieldIs("LastCommaFirst")
                .Bind();

 

The class that does the grunt work (reformatted for brevity)

/// <summary>
/// Provides a fluent interface for setting a combo box
/// </summary>
public class ComboBoxSetter<T>
{
    private ListControl Control;
    private IList<T> DataSource;
    private string DefaultValue;
    private ListItem ListItemToAdd;
    private string DataValueField;
    private string DataTextField;

    public void Bind()
    {
        Control.DataSource = DataSource;
        Control.DataTextField = DataTextField;
        Control.DataValueField = DataValueField;
        Control.DataBind();

        if (ListItemToAdd != null)
            Control.Items.Insert(0, ListItemToAdd);

        if (!string.IsNullOrEmpty(DefaultValue))
            foreach (ListItem listItem in Control.Items)
                listItem.Selected = listItem.Text == DefaultValue;
    }


    public ComboBoxSetter<T> WithListControl(ListControl ddl)
    {
        Control = ddl;
        return this;
    }

    public ComboBoxSetter<T> WithDataSourceOf(IList<T> people)
    {
        DataSource = people;
        return this;
    }

    public ComboBoxSetter<T> SetSelectedWhereTextIs(string defaultSelection)
    {
        DefaultValue = defaultSelection;
        return this;
    }

    public ComboBoxSetter<T> AddLeadingItemOf(string value, string display)
    {
        ListItemToAdd = new ListItem(display, value);
        return this;
    }

    public ComboBoxSetter<T> AddLeadingItemOf(ListItem leadingItem)
    {
        ListItemToAdd = leadingItem;
        return this;
    }

    public ComboBoxSetter<T> TheDataValueFieldIs(string dataValueField)
    {
        DataValueField = dataValueField;
        return this;
        
    }

    public ComboBoxSetter<T> TheDataTextFieldIs(string dataTextField)
    {
        DataTextField = dataTextField;
        return this;
    }

posted @ Thursday, September 20, 2007 9:42 PM | Feedback (4) |

Thursday, September 13, 2007

HTTP Cache Helper With a Fluent Interface

After writing the same 20 lines of boilerplate code for the 1000th time, I decided to encapsulate it in a class and add some syntactic sweetness to it in the form of a fluent interface as recently demonstrated by Joey Beninghove

What I had been doing was something like the following pseudocode (or a slight variation) in every place I wanted to cache something.

declare returnObject as null

if the HTTPCache available
    if the object is available in the cache
        set returnObject
    else
        set returnObject to whatever code is needed to get the object
        add returnObject to the cache
else
  set returnObject to whatever code is needed to get the object

return returnObject  
        
   
    

 

Here is the class I ended up with.

 

public class HTTPCacheHelper<T>
 {
     private DateTime? _AbsoluteExpiration;
     private TimeSpan? _SlidingExpiration;
     private CacheItemPriority _Priority = CacheItemPriority.Default;
     private string _Key;

     /// <summary>
     /// Key of item stored in the cache.
     /// </summary>
     public HTTPCacheHelper<T> WithKeyOf(string key)
     {
         _Key = key;
         return this;
     }

     /// <summary>
     /// Absolute date to expire on
     /// </summary>
     /// <remarks>
     /// Optional, but either this or WithSlidingExpiration must be called
     /// </remarks>
     public HTTPCacheHelper<T> WithExpirationDateOf(DateTime expires)
     {
         _AbsoluteExpiration = expires;
         return this;
     }

     /// <summary>
     /// Sliding timespan to expire on.
     /// </summary>
     /// <remarks>
     /// Optional, but either this or WithExpirationDateOf must be called
     /// </remarks>
     public HTTPCacheHelper<T> WithSlidingExpiration(TimeSpan ts)
     {
         _SlidingExpiration = ts;
         return this;
     }

     /// <summary>
     /// Priority
     /// </summary>
     public HTTPCacheHelper<T> WithPriorityOf(CacheItemPriority priority)
     {
         _Priority = priority;
         return this;
     }

     /// <summary>
     /// Adds the item to the cache based data passed in in previous methods.  
     /// This will the last method call in the chain, and must
     /// be called in order to for the item to be cached.
     /// </summary>
     public void Commit(T item)
     {
         Debug.Assert(!item.Equals(default(T)));
         Debug.Assert(!_AbsoluteExpiration.HasValue ^ _SlidingExpiration.HasValue);
         Debug.Assert(!string.IsNullOrEmpty(_Key));

         if (!_AbsoluteExpiration.HasValue) _AbsoluteExpiration = Cache.NoAbsoluteExpiration;
         if (!_SlidingExpiration.HasValue) _SlidingExpiration = Cache.NoSlidingExpiration;

         lock (HttpRuntime.Cache)
         {
             HttpRuntime.Cache.Add(_Key, item, null, _AbsoluteExpiration.Value, _SlidingExpiration.Value, _Priority, null);
         }
     }


     public  T GetItem(string key)
     {
         if (HttpRuntime.Cache != null)
         {
             object check = HttpRuntime.Cache[key];

             if (check != null)
             {
                 return (T)check;
             }
         }

         return default(T);
     }

     private bool IsNull(T item)
     {
         if (typeof(T).IsValueType)
         {
             return default(T).Equals(item);
         }
         else
         {
             return item == null;
         }

     }


     public  T GetItem(string key, Func<T> itemSourceOnDoesNotExist)
     {
         T check = GetItem(key);

         if (IsNull(check))
         {
             check = itemSourceOnDoesNotExist();
         }

         return check;
     }

     public  T GetItem(string key, Func<T> itemSourceOnDoesNotExist, Proc<T> addToCache)
     {
         T check = GetItem(key);

         if (IsNull(check))
         {
             check = itemSourceOnDoesNotExist();
             addToCache(check);
         }
         return check;
     }
     
 }

 

The methods prior to Commit() make up the fluent interface, and I stole a little code from Ayende's Rhino Commons for the delegates in the GetItem methods.  As a side note, the GetItems methods were originally static, which allowed me to do the following: 

 

private string ItemToCache()
{
    return "expected value";
}


[Test]
public void FluentSyntaxText()
{
    HTTPCacheHelper<string> cache = new HTTPCacheHelper<string>();

    cache
        .WithExpirationDateOf(DateTime.Now.AddDays(1))
        .WithKeyOf("TestKey")
        .WithPriorityOf(CacheItemPriority.Low)
        .Commit(ItemToCache());

    string actualValue = HTTPCacheHelper<string>.GetItem("TestKey", ItemToCache, cache.Commit);

    Assert.AreEqual("expected value", actualValue);

}

 

But it dawned on me a little later that by making them instance methods I could accomplish the same thing in a single statement 

 

[Test] 
public void FluentSyntaxTest2()
{
    HTTPCacheHelper<string> cache = new HTTPCacheHelper<string>();

    string actualValue = cache
        .WithExpirationDateOf(DateTime.Now.AddDays(1))
        .WithKeyOf("TestKey")
        .WithPriorityOf(CacheItemPriority.Low)
        .GetItem("TestKey", ItemToCache, cache.Commit);

    Assert.AreEqual("expected value", actualValue);

}

 

Which is pretty friggin' sweet.

posted @ Thursday, September 13, 2007 11:43 PM | Feedback (5) |

Powered by: