Signature Comparison

Following the signature capture projects, I decided to make a program that analyzes the signature to see how close two different signatures match.

My first step was create an account on bitbucket.  The reason I chose bitbucket over github was because bitbucket allows for private repros.  Also, I wanted to try out Mercurial.  I installed the most recent version on my machine and then added the VisualHG extension to Visual Studio 2010.  Note that you have to change your source control plug in Visual Studio (Tools –> Source Control)

image

I then was pushing/pulling my repro to bitbucket.  There are still some small things I don’t like (for example, having to enter your credentials each push/pull), but on the whole Mercurial is easier to use in Visual Studio than git and bitbucket is much better than github.

In any event, once I set up my source control, I consolidated all of my projects having to do with this signature series and renamed from things.

image

The once thing I don’t like is that the word ’signature’ is in both the namespace and is a class name – so I get conflicts.  I am not sure what I want to rename the class so I left it ‘as is’ right now.  But as Bob Martin says “with these modern/fancy IDEs, renaming a class should take no time at all.”

I then refactored my object graph:

image

Note that I discarded using System.Drawing.Line and System.Drawing.Point from earlier projects so my analysis program would not have a dependency on System.Drawing.  I spun up the unit tests for line and class in a separate project:

image

and got pretty good code coverage:

image

Note that I didn’t write unit tests for things that the API handles – like the constructor.

With the support classes ready to go, I then tackled the signature analysis.  I first started doing a line analysis – basically starting with a base signature and then layering over a second signature to see how close the lines match.  Because the signatures might be different sizes and on different slopes, I thought I would have to adjust the second signature to match the base’s dimensions.  I would do this by matching the left and top point of both signatures and moving the second one’s lines over to that adjustment factor.

I then realized that I don’t know enough about signature analysis to have “THE” solution to code.  I then adjusted my project to reflect that there might be many different ways to analyze a signature and I should use the power of polymorphism to help me.  I then threw away all of the analysis code that I was working on to get to “THE” solution.

I created an interface called IComparisonFactory like so (note the fully qualified name – I really need to change that)

public interface IComparisonFactory
{
    double CompareSignatures(Tff.Signature.Support.Signature signature1, Tff.Signature.Support.Signature signature2, int levelOfFreedom);
}

I then created an abstract base class that has some functions that do the low level plumbing:

public abstract class BaseComparisonFactory
{

    protected List<Line> GetAllLinesInASignature(Tff.Signature.Support.Signature signature)
    {
        List<Line> lines = new List<Line>();
        List<Glyph> glyphs = signature.Glyphs;
        foreach (Glyph glyph in signature.Glyphs)
        {
            foreach (Line line in glyph.Lines)
            {
                lines.Add(line);
            }
        }

        return lines;
    }
    

    protected List<Point> GetAllPointsInASignature(Tff.Signature.Support.Signature signature)
    {
        List<Point> points = new List<Point>();
        List<Line> lines = GetAllLinesInASignature(signature);
        foreach (Line line in lines)
        {
            points.Add(line.StartPoint);
            points.Add(line.EndPoint);
        }
        return points;
    }
}

I then created my 1st attempt an analyzing two signatures by looking at the dispersal of their points – a scatterplot analysis.

public class ScatterplotComparisonFactory: BaseComparisonFactory, IComparisonFactory
{
    public Double CompareSignatures(Tff.Signature.Support.Signature signature1, Tff.Signature.Support.Signature signature2, Int32 levelOfFreedom)
    {

        Int32 pointTrys = 0;
        Int32 pointHits = 0;

        List<Point> basePoints = GetAllPointsInASignature(signature1);
        List<Point> comparePoints = GetAllPointsInASignature(signature2);

        List<Point> normalizedBasePoints = basePoints.Distinct().ToList<Point>();
        normalizedBasePoints.Sort(ComparePoints); 

        List<Point> normalizedComparePoints = NormalizePoints(comparePoints, levelOfFreedom);
        normalizedComparePoints.Sort(ComparePoints);

        foreach (Point basePoint in normalizedBasePoints)
        {
            foreach (Point comparePoint in normalizedComparePoints)
            {
                if (basePoint == comparePoint)
                {
                    pointHits++;
                    break;
                }
            }
            pointTrys++;
        }

        Double returnValue = ((Double)pointHits / (Double)pointTrys);
        return returnValue;
    }

    private List<Point> NormalizePoints(List<Point> comparePoints, int levelOfFreedom)
    {
        List<Point> normalizedPoints = CreateNormalizedPoints(comparePoints, levelOfFreedom);
        normalizedPoints = RemoveNegativePoints(normalizedPoints);
        return normalizedPoints;
    }

    private List<Point> CreateNormalizedPoints(List<Point> comparePoints, int levelOfFreedom)
    {
        List<Point> normalizedPoints = new List<Point>();
        foreach (Point basePoint in comparePoints)
        {
            normalizedPoints.Add(basePoint);
            for (int i = 1; i <= levelOfFreedom; i++)
            {
                normalizedPoints.Add(new Point(basePoint.X - i, basePoint.Y - i));
                normalizedPoints.Add(new Point(basePoint.X - i, basePoint.Y));
                normalizedPoints.Add(new Point(basePoint.X - i, basePoint.Y + i));
                normalizedPoints.Add(new Point(basePoint.X, basePoint.Y + i));
                normalizedPoints.Add(new Point(basePoint.X + i, basePoint.Y + i));
                normalizedPoints.Add(new Point(basePoint.X + i, basePoint.Y));
                normalizedPoints.Add(new Point(basePoint.X + i, basePoint.Y - 1));
                normalizedPoints.Add(new Point(basePoint.X, basePoint.Y - 1));
            }
        }

        return normalizedPoints;
    }

    private List<Point> RemoveNegativePoints(List<Point> normalizedPoints)
    {
        List<Point> points = new List<Point>();

        foreach (Point point in normalizedPoints)
        {
            if (point.X >= 0 && point.Y >= 0)
            {
                points.Add(point);
            }
        }

        return points;
    }

    private static int ComparePoints(Point point01, Point point02)
    {
        if (point01 == point02)
        {
            return 0;
        }
        if(point01.X == point02.X)
        {
            if (point01.Y < point02.Y)
            {
                return -1;
            }
            else
            {
                return 1;
            }
        }

        if (point01.X < point02.X)
        {
            return -1;
        }
        else
        {
            return 1;
        }

    }

}

Note that the ComparePoints function allows me to call .Sort().

The levelOfFreedom argument takes the second signature’s points and increases them to all adjacent points.  I also remove duplicate points, sort them (first X than Y), and remove any point that is less than zero.

Instead of creating tests for each of the ComparisonFactories that I will create, I created a test class for ICompairisonFactory and injected in the implementation in the constructor.  I then ran the scatterplot though the tests to see if they pass basic analysis (same signature returns 1, etc…).

public class ScatterplotComparisonFactory: BaseComparisonFactory, IComparisonFactory
{
    public Double CompareSignatures(Tff.Signature.Support.Signature signature1, Tff.Signature.Support.Signature signature2, Int32 levelOfFreedom)
    {

        Int32 pointTrys = 0;
        Int32 pointHits = 0;

        List<Point> basePoints = GetAllPointsInASignature(signature1);
        List<Point> comparePoints = GetAllPointsInASignature(signature2);

        List<Point> normalizedBasePoints = basePoints.Distinct().ToList<Point>();
        normalizedBasePoints.Sort(ComparePoints); 

        List<Point> normalizedComparePoints = NormalizePoints(comparePoints, levelOfFreedom);
        normalizedComparePoints.Sort(ComparePoints);

        foreach (Point basePoint in normalizedBasePoints)
        {
            foreach (Point comparePoint in normalizedComparePoints)
            {
                if (basePoint == comparePoint)
                {
                    pointHits++;
                    break;
                }
            }
            pointTrys++;
        }

        Double returnValue = ((Double)pointHits / (Double)pointTrys);
        return returnValue;
    }

    private List<Point> NormalizePoints(List<Point> comparePoints, int levelOfFreedom)
    {
        List<Point> normalizedPoints = CreateNormalizedPoints(comparePoints, levelOfFreedom);
        normalizedPoints = RemoveNegativePoints(normalizedPoints);
        return normalizedPoints;
    }

    private List<Point> CreateNormalizedPoints(List<Point> comparePoints, int levelOfFreedom)
    {
        List<Point> normalizedPoints = new List<Point>();
        foreach (Point basePoint in comparePoints)
        {
            normalizedPoints.Add(basePoint);
            for (int i = 1; i <= levelOfFreedom; i++)
            {
                normalizedPoints.Add(new Point(basePoint.X - i, basePoint.Y - i));
                normalizedPoints.Add(new Point(basePoint.X - i, basePoint.Y));
                normalizedPoints.Add(new Point(basePoint.X - i, basePoint.Y + i));
                normalizedPoints.Add(new Point(basePoint.X, basePoint.Y + i));
                normalizedPoints.Add(new Point(basePoint.X + i, basePoint.Y + i));
                normalizedPoints.Add(new Point(basePoint.X + i, basePoint.Y));
                normalizedPoints.Add(new Point(basePoint.X + i, basePoint.Y - 1));
                normalizedPoints.Add(new Point(basePoint.X, basePoint.Y - 1));
            }
        }

        return normalizedPoints;
    }

    private List<Point> RemoveNegativePoints(List<Point> normalizedPoints)
    {
        List<Point> points = new List<Point>();

        foreach (Point point in normalizedPoints)
        {
            if (point.X >= 0 && point.Y >= 0)
            {
                points.Add(point);
            }
        }

        return points;
    }

    private static int ComparePoints(Point point01, Point point02)
    {
        if (point01 == point02)
        {
            return 0;
        }
        if(point01.X == point02.X)
        {
            if (point01.Y < point02.Y)
            {
                return -1;
            }
            else
            {
                return 1;
            }
        }

        if (point01.X < point02.X)
        {
            return -1;
        }
        else
        {
            return 1;
        }

    }

}

Here is the full suite:

image

Finally, I had everything in place to do an integration test.  I loaded some signatures into XML files and ran them through the integration tests.  Interestingly, the scatterplot seemed to work.

Case #1: Same Signature Compared To Itself (Same Person, Same Signature)

imageimage

returns

0 = 1.0

1= 1.0

2= 1.0

3= 1.0

4= 1.0

5= 1.0

6= 1.0

7= 1.0

8= 1.0

9= 1.0

 

Case #2: Two Signatures That Are Close To Each Other (Same Person, Different Times)

imageimage

0 = 0.0131578947368421

1 = 0.177631578947368

2 = 0.266447368421053

3 = 0.391447368421053

4 = 0.463815789473684

5 = 0.542763157894737

6 = 0.628289473684211

7 = 0.661184210526316

8 = 0.700657894736842

9 = 0.753289473684211

 

Case #3: Two Signatures That Are Not Close (Different People)

clip_image001clip_image001[4]

 

0 = 0

1 = 0.111842105263158

2 = 0.154605263157895

3 = 0.180921052631579

4 = 0.213815789473684

5 = 0.220394736842105

6 = 0.299342105263158

7 = 0.305921052631579

8 = 0.319078947368421

9 = 0.345394736842105

Note that using a 9 point degree of freedom, the same person signature is at 75% matching, but the different person ones are at 35%….

Pretty cool.  Next steps will be the adjust for slope and location on the canvas.  Also, Rob Seder suggested that the Line should include a DateTime stamp, because the meter/pace of someone’s signature is also important.  I’ll add that analysis in too – as well as a line comparison….

Signature Capture–WPF

I kept working on the SignaturePanel this last week.  I decided to make the same thing in WPF.   I fired up a new WPF Application and hooked up a reference to my existing SignatureCapture.Support class.  I then added a Rectangle (changed it later to a Canvas) to the MainWindow and three buttons.

image

 

  I then started copy/pasting the code from my WinForms application to the window’s code-behind.

image

 

Yikes!  It looks like the Point and Pen class has been changed from the Windows Implementation to the WPF implementation.  My first thought was to throw away the WPF version by using the trusty Remove and Sort Using context menu:

image

I then fully-qualified my classes:

image

And then I realized that I was completely wrong and if I spent more than 1 second thinking about the problem, the whole remove usings/fully qualifying the classes won’t work.

image

BECAUSE THEY ARE TWO DIFFERENT TYPES.

Given the choice of adding new functions to my Support class or converted, I chose the later.  I added a Convert Line method like so:

private System.Drawing.Point ConvertPoint(System.Windows.Point point)
{
    System.Drawing.Point returnPoint = new System.Drawing.Point();
    returnPoint.X = (Int32)point.X;
    returnPoint.Y = (Int32)point.Y;
    return returnPoint;
}

and then I wired up the UI:

private void signatureCanvas_MouseMove(object sender, MouseEventArgs e)
{
    if (IsCapturing)
    {
        if (startPoint.IsEmpty && endPoint.IsEmpty)
        {
            endPoint = ConvertPoint(e.GetPosition(this.signatureCanvas));
        }
        else
        {
            startPoint = endPoint;
            endPoint = ConvertPoint(e.GetPosition(this.signatureCanvas));
            Line line = new Line(startPoint, endPoint);
            glyph.Lines.Add(line);
            DrawLine(line);
        }
    }
}

Note that the e.GetPosition() method replaces the WinForm’s e.Location property.  The key thing is that the second argument (location) needs to be your capturing panel to make it equivalent to the WinForm implementation…

private void DrawLine(Line line)
{
    System.Windows.Shapes.Line currentLine = new System.Windows.Shapes.Line();
    currentLine.Stroke = System.Windows.Media.Brushes.Black;
    currentLine.X1 = line.StartPoint.X;
    currentLine.Y1 = line.StartPoint.Y;
    currentLine.X2 = line.EndPoint.X;
    currentLine.Y2 = line.EndPoint.Y;
    currentLine.StrokeThickness = 1;
    signatureCanvas.Children.Add(currentLine);

}
private void signatureCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
    IsCapturing = false;
    signature.Glyphs.Add(glyph);
    startPoint = new System.Drawing.Point();
    endPoint = new System.Drawing.Point();

}
private void signatureCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
    IsCapturing = true;
    glyph = new Glyph();
}

All that was left was to handle the button clicks:

private void clearButton_Click(object sender, RoutedEventArgs e)
{
    signatureCanvas.Children.Clear();
}
private void exportButton_Click(object sender, RoutedEventArgs e)
{
    signatureFactory.SerializeSignatureToDisk(signature, fileName);
}
private void DrawSignature()
{
    foreach (Glyph glyph in signature.Glyphs)
    {
        foreach (Line line in glyph.Lines)
        {
            DrawLine(line);
        }
    }
}
private void importButton_Click(object sender, RoutedEventArgs e)
{
    signature = signatureFactory.DeserializeSignatureFromDisk(fileName);
    signatureCanvas.Children.Clear();
    DrawSignature();
}

And now I have a WPF implementation of the signature capture panel:

image

With the cool thing is that I can import and signature from a WinForm into my WPF app, or export my WPF generated signature into the WinForm app or that Web Form app,

Primary Keys and Entity Framework

 

If you are creating EF classes from an existing database schema and one of your classes has a composite key of all of the fields like this:

image

You didn’t define a primary key in the database.  Once you go back and do that, your EF class will reflect it:

image

A curious property about dependency properties

For my Panzer General game, I have a collection of hexes that make up the main board. Before every turn, I update the main canvas with this collection. I wrote some code like this:

this.MainCanvas.Children.Clear(); foreach (Hex hex in Game.CurrentBoard) { hex.HexInfoTextBlock.Text = string.Empty; this.MainCanvas.Children.Add(hex); }

However, when I run it, I get the following exception:

 

System.InvalidOperationException was unhandled

Message=Element is already the child of another element.

 

My first thought was to create a Clone for each Hex – remove the old Hex and then add the new one to the collection. However, I still got the same error.

After mucking around Bing for a bit (and not getting any closer to the answer), I decided to back into the parent and try and clear the collection that way:

Canvas currentCanvas = Game.CurrentBoard[0].Parent as Canvas; if (currentCanvas != null) { currentCanvas.Children.Clear(); }

Surprise! Surprise! It worked. I am guessing that the dependency property of the children still hold onto their parent even after the clear until the page goes out of scope.

Using LINQ to replace foreach

I sometimes forget how powerful LINQ can be – especially when dealing with older constructs that work fine.  I recently wrote this nugget of crappy code:

Unit unit = null; foreach (Hex hex in Game.CurrentBoard) { if (hex.CurrentUnit != null) { if (hex.CurrentUnit.Country.AlliesIndicator == true) { unit = hex.CurrentUnit; } } }

I looked at it for a second and then re-wrote it using LINQ:

var q = (from h in Game.CurrentBoard where h.CurrentUnit.Country.AlliesIndicator == true select h);

Take about an improvement in readability!

Removing something from a collection

Consider the following snippet:

//Remove all hexes that have a unit in them for (int i = 0; i < returnValue.Count; i++ ) { if (returnValue[i].CurrentUnit != null) { returnValue.RemoveAt(i); } } return returnValue;

This does not work.  For example, here is a screen shot:

image

The reason why is that the indexer is evaluated once,, but the list changes after each iteration.  In graphical terms:

0

1

2

3

4

5

X

X

0

1

2

3

4

X

 

 

Index 1 becomes Index 0, and since the loop is already on 1, it gets skipped for evaluation. What we need it to remove a hex and then keep looping – so the while construct needs to be used:

bool keepLooping = true; while (keepLooping == true) { int totalHexes = returnValue.Count; for (int i = 0; i < returnValue.Count; i++) { if (returnValue[i].CurrentUnit != null) returnValue.RemoveAt(i); if (totalHexes == returnValue.Count) keepLooping = false; } }

 

And the results work as expected:

image

Entity Framework: Identity Errors

If you create a table with a PK that is not identity and then create a EF class from that and then try and load in an empty value, you get a System.Data.Update exception:

image

 

If you go ahead and change the server to identity without updating EF on your client, you still get a System.Data.Update exception, but with a different inner exception:

image

What you need to do is to fix it by changing to identity and then updating the EF on the client:

image

Volia: the change will now persist

However, if you create a PK on an existing table where there used to be a Implied key, you get an error. You need to drop the table and re-create.

Select MIN In LINQ

I recently had to take a set of swim time data for an entire season and determine the lowest time for each swimmer.  The TSQL in me thought “this is a snap – just do this:”

Select SwimmerID, MIN(Time) from dbo.tblTimesIndividual where MeetID > 74 and RaceStrokeID = 4 Group By SwimmerID

However, when I tried to implement in LINQ, things went bad pretty quickly.  I stumbled around with the language extensions for a bit before raising the white flag with MSDN.  The easiest implementation that I found was this:

var minQuery = from times in entities.tblTimesIndividuals where times.MeetID > 74 && times.RaceStrokeID == 4 group times by times.SwimmerID into grouping select new { grouping.Key, LowestFreeStyleTime = from times2 in grouping where times2.Time == grouping.Min(times3 => times3.Time) select times2 };

Yikes!  Since it worked, I kept it – however, I do wonder if there is an easier implementation.   I wonder if there is a tool to reverse engineer TSQL to LINQ – like you can look at the TSQL generated from EF when you send using SQL Profiler (which I can’t use on my remote server) or the ToTraceString() method like so:

string sql = ((ObjectQuery)freeMinQuery).ToTraceString();

The key thing is to remember that ObjectQuery is in System.Data.Objects so I had to add a using statement for that.  In any event, check out the TSQL:

SELECT [Project1].[SwimmerID] AS [SwimmerID], [Project1].[C1] AS [C1], [Project1].[TimesIndividualID] AS [TimesIndividualID], [Project1].[SwimmerID1] AS [SwimmerID1], [Project1].[MeetID] AS [MeetID], [Project1].[AgeGroupID] AS [AgeGroupID], [Project1].[RaceStrokeID] AS [RaceStrokeID], [Project1].[RaceTypeID] AS [RaceTypeID], [Project1].[RaceLengthID] AS [RaceLengthID], [Project1].[Lane] AS [Lane], [Project1].[Place] AS [Place], [Project1].[Time] AS [Time], [Project1].[TimerID] AS [TimerID] FROM ( SELECT [GroupBy1].[K1] AS [SwimmerID], [Extent2].[TimesIndividualID] AS [TimesIndividualID], [Extent2].[SwimmerID] AS [SwimmerID1], [Extent2].[MeetID] AS [MeetID], [Extent2].[AgeGroupID] AS [AgeGroupID], [Extent2].[RaceStrokeID] AS [RaceStrokeID], [Extent2].[RaceTypeID] AS [RaceTypeID], [Extent2].[RaceLengthID] AS [RaceLengthID], [Extent2].[Lane] AS [Lane], [Extent2].[Place] AS [Place], [Extent2].[Time] AS [Time], [Extent2].[TimerID] AS [TimerID], CASE WHEN ([Extent2].[TimesIndividualID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]FROM (SELECT [Extent1].[SwimmerID] AS [K1], MIN([Extent1].[Time]) AS [A1]FROM [dbo].[tblTimesIndividual] AS [Extent1]WHERE ([Extent1].[MeetID] > 74) AND (4 = [Extent1].[RaceStrokeID])GROUP BY [Extent1].[SwimmerID] ) AS [GroupBy1]LEFT OUTER JOIN [dbo].[tblTimesIndividual] AS [Extent2] ON ([Extent2].[MeetID] > 74) AND (4 = [Extent2].[RaceStrokeID]) AND ([GroupBy1].[K1] = [Extent2].[SwimmerID]) AND ([Extent2].[Time] = [GroupBy1].[A1])) AS [Project1]ORDER BY [Project1].[SwimmerID] ASC, [Project1].[C1] ASC

 

Yikes!  I’ll continue to search for a tool that can give hints to make my LINQ more clear and have it generate better TSQL.

Export .sdif file to the client’s desktop

One of the user requirements of the swim team website is to send the .sdif file that I blogged about here from the webpage to the user’s browser – or download it to the users file system. Since the file is dynamic and I can’t write to the file system of the web server, I need a way to generate the file and then have a dialog box “Save To” open.

The first step was to add a UI project to the solution that matches the MVC project that the swim team currently users. I love the organization of the initial project – it was very easy and logical to drop in a new Web project:

image

I then opened up the home controller and added a base method and ported the code from the Console UI:

[HttpPost] public ActionResult GetSdifFile() { string fileName = @"C:\Users\Public\PracticeMeetSetup.SD3"; MeetSetupFactory factory = new MeetSetupFactory(); Collection<string> collection = factory.CreateMeetSetUp(72); File.WriteAllLines(fileName, collection); return View(); }

Basically, I need to rip out the disk write code and replace it with something that can go to the browser. Being a traditional ASP.Net guy, I immediately thought of something like this:

public void Guess(string filePath) { try { using (StreamReader sr = new StreamReader(filePath)) { String line; while ((line = sr.ReadLine()) != null) { Response.Write(line + "<br />"); } } } catch (Exception ex) { Response.Write("<p>The file could not be read:"); Response.Write(ex.Message + "</p>"); } }

I then thought “Wait, this is MVC…” so I thought of something like this:

[HttpPost] public ActionResult GetSdifFile() { MeetSetupFactory factory = new MeetSetupFactory(); Collection<string> collection = factory.CreateMeetSetUp(72); return View(collection); }

And the view displaying the contents of the collection:

<h2>GetSdifFile</h2> <% foreach (string _currentString in Model){ %> <%= Html.Encode(_currentString) %> <br /> <% } %>

And here are the results:

image

So it is a start – I guess they could copy/paste the contents of the page. However, the User Case is for them to click a button and get a .sdif file that saves to their file system (via, I assume, a Save Dialog box).

I wrote a new function that returns JSON:

public JsonResult CurrentMeetSdifFile() { MeetSetupFactory factory = new MeetSetupFactory(); Collection<string> collection = factory.CreateMeetSetUp(72); JsonResult result = new JsonResult(); result.Data = collection; result.JsonRequestBehavior = JsonRequestBehavior.AllowGet; return result; }

Since the browser doesn’t know what to do with JSON (I only tested in IE), then you get a save dialog box:

image

Saving that to the desktop, I open in note pad and get the following results:

image

The results are losing their line formatting.

This is the right track – but I have bit more work to do.

My 1st step was to add a parameter of the actual meet that the user wants:

public JsonResult CurrentMeetSdifFile(int meetId) { MeetSetupFactory factory = new MeetSetupFactory(); Collection<string> collection = factory.CreateMeetSetUp(meetId); Etc… }

I then tried it from the browser:

image

Ugh, it looks like with the default routing engine, I need to make the parameter have the name id. I have a choice. I can either add a new route with an explicit meetId or I can use the id variable name. I chose option B as a path of least resistance:

public JsonResult CurrentMeetSdifFile(int id) { MeetSetupFactory factory = new MeetSetupFactory(); Collection<string> collection = factory.CreateMeetSetUp(id); JsonResult result = new JsonResult(); result.Data = collection; result.JsonRequestBehavior = JsonRequestBehavior.AllowGet; return result; }

With the routing set up, I now need to fix the JSON output to stick a new line after each string in the collection.

My 1st attempt was just to throw an Environment.NewLine into the Json result:

image

Yikes! It looks like I am mixing formatting. “\r\n” is coming down as a literal value. I need a way to tell notepad that they are line breaks.

I binged around a bit and looked at attack overflow. This post seems to have the answer. I tried to add

result.ContentType = "text/html";

But then the browser displayed the data:

image

Note the “\r\n” is still there.

I then tried some other ways of breaking (using the \\n for example), nothing.

I then stepped back and thought that I was approaching the problem incorrectly. Instead of sending back JSON from the function, what if I sent something else? A quick tour through MSDN showed me a file result class. That is what I need and here is an example of my question.

A quick run through the overloads of the method, I realized that all I have to do is to convert the string collection into a FileStream and then send it out via the File class. I used the FileStreamResult class and the rest was pretty easy to wire up:

public FileStreamResult CurrentMeetSdifFile(int id) { MeetSetupFactory factory = new MeetSetupFactory(); Collection<string> collection = factory.CreateMeetSetUp(id); StringBuilder stringBuilder = new StringBuilder(); foreach(string _currentString in collection) { stringBuilder.Append(_currentString); stringBuilder.Append(Environment.NewLine); } byte[] byteArray = Encoding.ASCII.GetBytes(stringBuilder.ToString()); MemoryStream stream = new MemoryStream( byteArray ); FileStreamResult fileStreamResult = new FileStreamResult(stream,"text/plain"); return fileStreamResult; }

And if I want to get the popup, I change the output format to “.sdif” and I get the dialog box with the data formatted correctly.

image

And boom goes the dynamite…

MVC Route Constraints

I started to dig into MVC3 routing a bit more over the weekend.  I came across some routing constraints and realized that there is a clear progression.  I created an out of the box MVC3 web application (I am using Razor) and then added a Product Controller with the default methods and 1 View for the Details method.  I made 1 change to the out of the box convention – I changed the name of the int parameter to the Details method to productId.

 

image

The Details controller method pushes the parameter back out to the View:

public ActionResult Details(int productId) { return View(productId); }

and the view parrots the productId back to the user:

@{ ViewBag.Title = "Details"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Details</h2> <br /> You Entered = @Model

 

I then dropped into Global.asax to work with the constraints.  The first thing I did was to add a route to the routing table to account for the productid parameter:

routes.MapRoute( "Product", "Product/{productId}", new { controller = "Product", action = "Details" } );

This worked fine to a point.  Product/1 resolved:

image

 

but Product/foo was allowed by the routing engine and the method threw an error:

 

image

 

To handle this malformed parameter, I added a regular expression to the constraint definition (using Stephen Walter’s blog post as an example:

 

routes.MapRoute( "ProductWithConstraint", "Product/{productId}", new { controller = "Product", action = "Details" }, new { productId = @"\d+" } );

Now, when Product/foo comes in, I got a 404.

image

The next scenario I wanted to handle was that only some integers are allowed as a parameter.  For example, perhaps only products that are in stock are allowed – which means that you need to do a database call before defining the route constraint definition.  To that end, I created a new class that inherited from IRouteConstraint (based on Yuri Nayyeri blog post):

public class ProductRouteConstraint: IRouteConstraint { public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { if ((routeDirection == RouteDirection.IncomingRequest) && (parameterName.ToLower(CultureInfo.InvariantCulture) == "productid")) { int productId = 0; try { productId = Convert.ToInt32(values["productId"]); if (productId > 0 && productId < 10) { return true; } } catch (FormatException formatException) { return false; } } return false; } }

I then added this constraint to the routing table:

 

routes.MapRoute( "ProductWithConstraintClass", "Product/{productId}", new { controller = "Product", action = "Details" }, new { productId = new ProductRouteConstraint() } );

And I got the desired result – products less than 0 or greater than 10 returned a 404.

So the progression in my mind is:

  1. No constraints
  2. Reg Ex constraints in the Global.asax
  3. Create a class that implements IRouteConstraint

I my mind, I would rather skip #2 and go to #3 altogether.  Having all of the logic encapsulated in 1 class seems cleaner and not having the Global.asax mucked up with custom defs makes for a more maintainable solution.