Category Archives: Other

Code Kata One as Code – Supermarket Pricing

The code kata on supermarket pricing is one we wanted to do because we have some interest in pricing rules. However, it’s written as a design exercise (which is a good thing), whereas we still wanted to do some coding.
So :

The Checkout Pricing Kata

Some things in supermarkets have simple prices: this can of beans costs £0.20. Other things have more complex prices. For example:
• three for a £ (so what’s the price if I buy 4, or 5?)
• £1.99/pound (so what does 4 ounces cost?)
• buy two, get one free (so does the third item have a price?)
Here’s an example stock list with pricing and rules

Stock Pricing
Baked Beans: 20p per can, with a three-for-two offer.
Bananas: £1 per Kg
Bagged Bananas: £1.20 per bag
Beer: £1.50 per bottle, with a three-for-£4 offer
Bagels: £3 per dozen or £2.00 per half-dozen or 50p each
Kitchen Roll: 50p each
Beer ‘n’ Beans Cleanup Offer: Buy 3 cans of beans, 3 bottles of beer and get one free kitchen roll (not combinable with any other offer).
Beans ‘n’ Bagel Breakfast Offer: Get 6 cans of beans and a dozen bagels for £3.50 (not combinable with any other offer).

Checkout Pricing

Write something which, given a list of items purchased, will print out a priced and itemised receipt, with weights shown where relevant and all discount rules correctly applied. The customer should not be able to get a better price by re-organising or splitting up the shopping basket. The receipt should help the uncertain customer to see this.
In TDD style, do it by writing code to pass tests for a list of increasingly complex requirements.

Example shopping baskets

• 1 can of beans, and 1 bottle of beer
• 6 can of beans, and 3 bottles of beer
• 5 cans of beans and 1.4 kg of loose bananas
• 3kg of bananas and 7 bagels
• 4 cans of beans, 3 bottles of beer and a kitchen roll
• 10 cans of beans, 15 bagels, 4 bottles of beer, 2 kitchen rolls, 3.5 kg loose plus 1 bag of bananas.

I leave to the retail stategists amongst you the question, “Should it always be impossible for the customer to get a cheaper price by adding something to their basket?”

iPad printing problems

iPad printing is unreasonably difficult. It all works fine no doubt if you go out and buy a shiny new AirPrint enabled printer. Otherwise the simplest/cheapest thing I’ve found is:

I’ve had network problems too, similar to the problems with iTunes. Replacing my router was the best solution I had for that. If your router is old, or a very cheap broadband freebie, you may need to replace it.

Sending large byte[] of binary data through WCF – a simple approach

You can send large streams of data through WCF in two ways: by streaming or by buffer. How to enable streaming is discussed here but has the significant restriction that if you stream, you can only have one (yes, 1) parameter into and out of the service. If your goal is just to pass largish objects such as a spreadsheet or a PDF as part of a service operation this could be a significant hurdle to code around.

“By buffer” means that your large data can be handled with no special treatment at all: it is sent, read into memory (the buffer), and processed just like small messages are. You must understand that buffering means the whole message is buffered; As of early 21st century, that fine on most servers for muliti-megabyte data, but not fine for gigabytes of data.

The simple way to handles megabyte messages is to create a binding Configuration with 2 or 3 parameters increased:

  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="basicHttpFor2MBMessage" maxBufferSize="2097152" maxReceivedMessageSize="2097152">
          <readerQuotas maxArrayLength="2097000" />
        </binding>
      </basicHttpBinding>
    </bindings>

and have your service endpoint use it:

<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttpFor2MBMessage" contract="yourContractName>

Voila. You can now send a 2MB large array through SOAP to WCF.

If your large array is binary data, you declare it in your service classes as byte[], which is why you need the maxArrayLength setting. If you are creating test messages by hand or in a testing tool, you also need to know how to represent binary data in a SOAP message.
The usual answer is, use base64 encoding and it should just work, thus:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dat="http://MyServiceName">
   <soapenv:Header/>
   <soapenv:Body>
      <dat:Save>
         <dat:Id>111</dat:applicantId>
         <dat:Pdf>JVBE ... <i>base64 encoded string</i> .... JUVPRgo=</dat:contractPdf>
      </dat:Save>
   </soapenv:Body>
</soapenv:Envelope>

Caveats

Security

The WCF default small buffer and message sizes are a guard against DoS attacks so if your service is publicly visible this would leave it less well guarded.

Performance

How fast does your service process large requests?

If (Expected Peak Requests Per Minute) * (Seconds to Process a Request) is bigger than 60 (Seconds in a Minute) then you need more horsepower.

How much memory do you have?

If (Size of Message * Requests per Hour) – (Speed at which WCF frees up an hour’s worth of used buffers) is bigger than your available memory then you’ll fall over. Alas there is no simple setting for ‘Speed at which WCF frees up an hour’s worth of used buffers’ so that calculation is a problem for another blog post. If you might have a problem, you can use PerfMon to watch memory behaviour during a stress test.

Moq Mocking Indexer Properties

Mocking an indexer property with Moq is nearly but not quite straightforward. SetUpProperty() doesn’t work but if you only need to moq write/read to one or two keys or you can use this code from a stackoverflow post by seanlinmt for each key:

httpContext.SetupSet(x => x.Session["key"] = It.IsAny<object>())
        .Callback((string name, object value) =>
        {
           httpContext.SetupGet(x => x.Session[name]).Returns(value);
        });

The missing functionality is that you have to setup for each specific key you are going to access. You can’t do this to setup all in one:

httpContext.SetupSet(x => x.Session[ /**/ It.IsAny<string>() /**/ ] 
    = It.IsAny<object>()). ....

If you’re mocking out SessionState or ApplicationState you may want to mock multiple keys even for a single test fixture, but don’t want dozens of lines of setup code for each key you use.

You can at least abbreviate by shipping out the setup to an extension method so you can do this:

mockSessionStateBase.SetUpIndexerForKeys("a", "b", "c");

Like this:

public static class MoqStateDictionaryExtension
{
    private static Dictionary<Mock, Dictionary<string, object>> FakeStateDictionaries= new Dictionary<Mock, Dictionary<string, object>>();

    public static void SetUpIndexerForKeys(this Mock<HttpApplicationStateBase> @this, params string[] keys)
    {
        EnsureDictionary(@this);
        foreach (var key in keys)
        {
            string key1 = key;
            @this.SetupSet(
                    x => x[key1] = It.IsAny<object>()
                )
                .Callback<string, object>(
                    (name, val) =>
                    {
                        FakeStateDictionaries[@this][name] = val;
                        @this.SetupGet(x => x[name]).Returns(FakeStateDictionaries[@this][name]);
                    }
                );
        }
    }

    public static void SetUpIndexerForKeys(this Mock<HttpSessionStateBase> @this, params string[] keys)
    {
        EnsureDictionary(@this);
        foreach (var key in keys)
        {
            string key1 = key;
            @this.SetupSet(
                    x => x[key1] = It.IsAny<object>()
                )
                .Callback<string, object>(
                    (name, val) =>
                    {
                        FakeStateDictionaries[@this][name] = val;
                        @this.SetupGet(x => x[name]).Returns(FakeStateDictionaries[@this][name]);
                    }
                );
        }
    }

    private static void EnsureDictionary(Mock mock)
    {
        if (!FakeStateDictionaries.ContainsKey(mock)) { FakeStateDictionaries[mock] = new Dictionary<string, object>(); }
    }
    public static void RemoveDictionary(Mock mock)
    {
        if (FakeStateDictionaries.ContainsKey(mock)) { FakeStateDictionaries.Remove(mock); }
    }
    public static void RemoveDictionaries()
    {
        FakeStateDictionaries = new Dictionary<Mock, Dictionary<string, object>>();
    }
}

Used like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Moq;
using NUnit.Framework;

namespace MockingIndexersWithMoq
{
    [TestFixture]
    public class ApplicationWrapperTests
    {
        private Mock<HttpApplicationStateBase> mockApplication;
        private HttpApplicationStateBase exampleUsage;

        [SetUp]
        public void SetUp()
        {
            mockApplication = new Mock<HttpApplicationStateBase>();
            exampleUsage = mockApplication.Object;
        }

        [TearDown]
        public void ClearFakeStateDictionaries()
        {
            MoqStateDictionaryExtension.RemoveDictionary(mockApplication);
        }

        [Test]
        public void TestWhichReliesOnMockedIndexer()
        {
            mockApplication.SetUpIndexerForKeys("a", "b", "c");

            exampleUsage["a"] = 1;
            exampleUsage["a"].ShouldEqual(1);
            exampleUsage["a"] = 2;
            exampleUsage["a"].ShouldEqual(2);
            exampleUsage["b"] = 1;
            exampleUsage["b"].ShouldEqual(1);
            exampleUsage["c"].ShouldBeNull();
            exampleUsage["d"].ShouldBeNull();
            exampleUsage["e"].ShouldBeNull();
        }
    }
}

Use SqlExpress or Sql Server for Asp.Net Session

Problems using Sql Server or Sql Express for Asp.Net Session State

So you’ve run aspnet_regsql.exe -S .\SqlExpress -E -ssadd -sstype p or something like that, after following instructions about Asp.Net Session State Modes (in particular noting the instructions about the Agent XP setting for SqlExpress if you’re using SqlExpress) and you’ve set your web.config sessionState section; and you hoped that aspnet_regsql might do something useful like give permissions to your machine’s AspNet account to use the session state and it would all Just Work. Aaah. You can hope.

Error messages when setting up Asp.Net to use SqlServer Session State

Cannot open database requested in login ‘ASPState’. Login failed for user {MachineName}\ASPNET

Unable to use SQL Server because ASP.NET version 2.0 Session State is not installed on the SQL server. Please install ASP.NET Session State SQL Server version 2.0 or above

Failed to login to session state SQL server for user ‘AspNetSqlLogin’

Example Web.Config section

<sessionState
    cookieless="false"
    regenerateExpiredSessionId="true"
    mode="SQLServer"
    timeout="30"
    sqlConnectionString="Data Source=.\SqlExpress;Integrated Security=SSPI;"
    stateNetworkTimeout="30">

Solution: Give permissions on Sql Server to your AspNet account

Note the username from your error page telling you what account Asp.Net is using to login to your server. Then, in Sql Management Studio or somesuch:

  1. Create a login for that Windows user
  2. Add the login as a user to your AspState database
  3. Give permissions to that user in that database by making it a member of the owner role

I first tried making the AspNet account a member of the db_reader & db_writer roles, but that didn’t work; making it a database owner did work.

But I put SessionState on a Sql Server instance on a different machine

Then you’re hopefully running your servers within a domain and you can set Asp.Net to run under a domain account, and you can give permissions on Sql server to that domain account.
Or, you create a new Sql Server login. Similar to the above process, but instead of finding an existing Windows login in Sql Management Studio, you create a new Sql Server login). The you specify a connection string with a Sql Security instead of Integrated Security: sqlConnectionString="Data Source=.\SqlExpress;User Id=SqlLoginForAspNet;Password=XXXXXX;

But I get a

Failed to login to session state SQL server for user

and

Login failed for user ‘AspNetSqlLogin’. The user is not associated with a trusted SQL Server connection

That’s because your Sql Server has been configured for Windows Authentication Mode only. Configure it for both Windows and Sql Server Authentication Mode via the Database Properties – Security tab.
When the Gui tells you to restart Sql Server you can do so like this:
[dos]net stop “Sql Server (SQLExpress)”
net start “Sql Server (SQLExpress)”[/dos]
Or do it from Control Panel — Services.