thoughts on programming and computer related stuff RSS 2.0
# Friday, June 12, 2009
This blog post is about changing the DefaultControllerFactory to a custom one to allow constructor dependency injection. In this post I use Structuremap, CommonServiceLocator and I use a slightly changed version of the CommonServiceLocator.StructureMapAdatpter.

The DefaultControllerFactory needs a constructor without any input parameters. I want to get rid of dependencies in my code, so I put all my interface dependencies in the constructor and want to use structuremap to handle this for me. I have used this in a semi-large project, but in this blog post I will use an example with only one dependency and only one page.

Lets get started:

I start by creating a new ASP.NET MVC project. I then delete all controllers and views except for my home controller and my index view.

Then I add a Tools folder and a PersonRepository class in my new folder. This only has a default constructor and a get method that returns a Person, a simple object with only a name Property. I also extract an interface from PersonRepository called IPersonRepository.

In my homecontroller I create a constructor that takes a Ipersonrepository as a parameter. This is my HomeController now:
using System.Web.Mvc;
using DIControllerFactoryExample.Tools;

namespace DIControllerFactoryExample.Controllers
{

    public class HomeController : Controller
    {
        private readonly IPersonRepository personRepository;

        public HomeController(IPersonRepository personRepository)
        {
            this.personRepository = personRepository;
        }

        public ActionResult Index()
        {
            var personid = 1;
            ViewData.Model = personRepository.Get(personid);

            return View();
        }
    }
}
I compile, run and get a: "No parameterless constructor defined for this object." exception. Just as planned.
Now, the fun begins. Getting this to work.

I start by adding a solution folder called Lib, and adding StructureMap.dll and Microsoft.Practices.ServiceLocation.dll and reference them in the solution. These can be found at the locations in the top of this blog post.

Then I add 3 new files to my Tools folder.
StructureMapServiceLocator.cs, DefaultStructureMapRegistry.cs, CommonServiceLocatorControllerFactory.cs

These files look like this:
using StructureMap.Configuration.DSL;

namespace DIControllerFactoryExample.Tools
{
    public class DefaultStructureMapRegistry : Registry
    {
        public DefaultStructureMapRegistry()
        {
            ForRequestedType<IPersonRepository>().TheDefaultIsConcreteType
                <PersonRepository>();
        }
    }
}
using System;
using System.Collections.Generic;
using Microsoft.Practices.ServiceLocation;
using StructureMap;

namespace DIControllerFactoryExample.Tools
{
    public class StructureMapServiceLocator : ServiceLocatorImplBase
    {
        private IContainer container;

        public StructureMapServiceLocator(IContainer container)
        {
            this.container = container;
        }

        protected override object DoGetInstance(Type serviceType, string key)
        {
            return string.IsNullOrEmpty(key) ? container.GetInstance(serviceType) : container.GetInstance(serviceType, key);
        }

        protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
        {
            foreach (object obj in container.GetAllInstances(serviceType))
            {
                yield return obj;
            }
        }
    }
}
using System;
using System.Web.Mvc;
using Microsoft.Practices.ServiceLocation;

namespace DIControllerFactoryExample.Tools
{
    public class StructureMapServiceLocatorControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(Type controllerType)
        {
            var controller = ServiceLocator.Current.GetInstance(controllerType) as Controller;
            return controller;
        }
    }
}

I also add the following to the Application_Start method in my Global.asax.cs:
var registry = new DefaultStructureMapRegistry();
var container = new Container(registry);
ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator(container));
var locator = new StructureMapServiceLocatorControllerFactory();
ControllerBuilder.Current.SetControllerFactory(locator);

I compile, reload and everything works. That's is. Good luck;)

EDIT: Did a small fix in the code i global.asax, check out this post

The full Visual Studio solution can be downloaded here:

DIControllerFactoryExample.zip (463.5 KB)
Friday, June 12, 2009 11:18:17 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | ASP.NET | ASP.NET MVC | Patterns
# Friday, June 05, 2009
I have recently been working on a project where we reimplement an old web application with a new one. Since there has been an page on the same address before, there are also some old applications in other places linking to pages on the old site. For instance, the login.aspx file is linked to in a lot of places.

I will in my example use a link to the page login.aspx in my solution.

To get these links to work, I had (as far as I'm aware of) two options. To make a login.aspx and redirect in this file and add login.aspx to global.asax IgnoreRoute, or to map a new route in global.asax and redirect to my new login. The latter is the solution i went for.

To accomplish this all I had to do was to add the following to RegisterRoutes method in global asax:

routes.MapRoute("Handle login", "login.aspx", new { controller = "Login", action = "Index", id = "" });


Very easy, and now, every request for http://myapp/login.aspx is handled by http://myapp/login/index/

Friday, June 05, 2009 4:43:20 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | ASP.NET | ASP.NET MVC
# Tuesday, October 28, 2008
Today I got an error on a web application after upgrading it from .Net 2.0 and Visual Studio 2005 to .Net 3.5. The project uses Telerik radcontrol, a few releases old version. After upgrading, every page that uses the radcontrol DatePicker failed to load, and the dreaded "Yellow screen of death" was shown instead.

It wasen't my code that failed, it was the radcontrols own prerender-event that threw the exception. Since I don't have the source code for this control and due to the fact that the fault only appaired on one test server without Visual Studio installed, it was hard to debug. Luckily a colleague had experienced a similair problem some weeks ago, and I remembered that changing the locale settings helped him (He is swedish, and used a swedish locale in an otherwise homogenic norwegian development team).

I logged in to the server and tried to change the system locale of the admin user. No help.
I tried to change the user running the application pool from NETWORK SERVICE to the admin I logged in as. Suddenly everything worked.

I did however want to run the process as NETWORK SERVICE and not administrator or have to make another account just for this reason. Therefore I wanted to change the network services locale settings, but since this is a user you can't log in as, this seemed hard.

Luckily I found this blog post: IIS6: Changing the Locale ID when Regional Settings Won't Work
I followed the description to change the settings. For some reason this didn't quite work. Probably due to a typo. I tried to export the regedit settings for HKEY_USERS\S-1-5-20 and just manually change the sShortDate, from the English setting and to norwegian settings. Now everything is working again!

Tuesday, October 28, 2008 8:20:13 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | ASP.NET
# Thursday, October 23, 2008

Checking if a email addresses is valid in SQL can be very convinient from time to time. Here are two ways to do it in SQL, and a bit about them.

 

Here is a full SQL way to do this:

 

Create function VALIDEMAIL(@s varchar(255)) 
--Returns true if the string is a valid email address.
returns bit
as
BEGIN
DECLARE @u VARCHAR(60), @v VARCHAR(60), @x VARCHAR(60), @i int, @j int, @result bit
SET @result=1
set @s = rtrim(ltrim(@s))
SET @i=len(@s)-CHARINDEX('@',reverse(@s))+1
if @i <= 1 or len(@s) < 5
begin
  set @result = 0
  goto done
end
SET @u=LEFT(@s,@i-1)
SET @j=len(@s)-CHARINDEX('.',reverse(@s))+1
SET @v=RIGHT(@s,LEN(@s)-@j)
if @j < 3 or @j < @i
begin
  set @result = 0
  goto done
end
SET @x=substring(@s,@i+1,@j-@i-1)
IF LEN(@x)<2
 BEGIN
 SET @result=0
 GOTo done
 END
IF (LEN(@x)=3) AND (@x NOT LIKE '[a-zA-Z][a-zA-Z][a-zA-Z]'Collate  Latin1_General_BIN)
 BEGIN
 SET @result=0
 GOTo done
 END
IF (LEN(@x)=2) AND (@x NOT LIKE '[a-zA-Z][a-zA-Z]' Collate  Latin1_General_BIN)
 BEGIN
 SET @result=0
 GOTo done
 END
SET @i=1
WHILE (@i<LEN(@u))
 BEGIN
 IF SUBSTRING(@u,@i,1) NOT LIKE '[a-zA-Z0-9_\-.+=/!#$&''*%?^`{|}"@\\ ]' escape '\' Collate  Latin1_General_BIN
  BEGIN
  SET @result=0
  GOTo done
  END
 SET @i=@i+1
 END
SET @i=1
WHILE (@i<LEN(@v))
 BEGIN
 IF SUBSTRING(@v,@i,1) NOT LIKE '[a-zA-Z]' Collate Latin1_General_BIN
  BEGIN
  SET @result=0
  GOTo done
  END
 SET @i=@i+1
 END
SET @i=1
WHILE (@i<LEN(@x))
 BEGIN
 IF SUBSTRING(@x,@i,1) NOT LIKE N'[a-zA-Z\-.0-9]' escape '\' Collate  Latin1_General_BIN
  BEGIN
  SET @result=0
     goto done
  END
 SET @i=@i+1
 END
done:
return @result
END
GO

Here is another way, where you insert .NET CLR code in the database. Here I use the .NET RegEx.Ismatch, so I can use it for tons of other stuff as well with the right input.


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Text.RegularExpressions;

namespace SQLSignature
{
    public class SqlFunctions
    {

        [SqlFunction(IsDeterministic = true, DataAccess = DataAccessKind.None)]
        public static SqlBoolean fn_RegexIsMatch(SqlString input, SqlString pattern)
        {
            return (SqlBoolean)Regex.IsMatch(input.Value, pattern.Value);
        }

    }
}
In SQL:
CREATE ASSEMBLY SQLSignature
FROM 'C:\CLR Assemblies\Performance\SQLFunctions\bin\SQLFunctions.dll';
GO
-- Create fn_SQLSigCLR and fn_RegexReplace functions
CREATE FUNCTION dbo.fn_RegexIsMatch(@input AS NVARCHAR(MAX),@pattern AS NVARCHAR(MAX))RETURNS BIT 
WITH RETURNS NULL ON NULL INPUT 
EXTERNAL NAME SQLSignature.SQLFunctions.fn_RegexIsMatch;
GO
This can be run with a command like:

    select *
    from [AW].[person].emailaddress as p
    where dbo.fn_RegexIsMatch(RTRIM(LTRIM(lower(P.emailaddress))),
'^[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$')=1

I did some timings on the two functions and as you can see below there are big differences. The .NET CLR one is almost 8 times faster(cpu time) than the T-SQL one. It's also a whole lot easier to read, and can be used for all other RegEx's needed. You do however need to get out of management studio to make it, and 1.5 seconds is for processing almost 20000 email addresses still should be fast enough for most uses.

SET STATISTICS TIME ON

    select *
    from [AW].[person].emailaddress as p
    where dbo.VALIDEMAIL(P.emailaddress)=1

    select *
    from [AW].[person].emailaddress as p
    where dbo.fn_RegexIsMatch(RTRIM(LTRIM(lower(P.emailaddress))),
'^[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$')=1 (19929 row(s) affected) SQL Server Execution Times: CPU time = 1466 ms, elapsed time = 1911 ms. (19972 row(s) affected) SQL Server Execution Times: CPU time = 203 ms, elapsed time = 380 ms.
It's pretty cool to see that .NET performs good within SQL as well. I am primarily a .NET developer but do have to use SQL quite a bit as well, but the fact that .NET outperforms SQL both in readability and speed inside SQL on some actions make my job a lot easier.


Thursday, October 23, 2008 8:48:20 AM (GMT Standard Time, UTC+00:00)  #    Comments [1] - Trackback
.NET | T-SQL
# Wednesday, September 03, 2008

All most every ASP.NET website I have seen, uses Databinder.Eval(Container.DataItem, "colum1") in repeaters to get the data passed in and show it on screen. The problem with Databinder.Eval is that it uses reflection to evaluate the item. This is time consuming and unnecessary, so instead, one should use casting to obtain the same effect. Try not to do it this way:

<asp:repeater id="rptAccounts" runat="server">
    <itemtemplate>
        <%# Databinder.Eval(Container.DataItem, "colum1") %> <br />
    </itemtemplate>
</asp:repeater>

But use this one instead:

<asp:repeater id="rptAccounts" runat="server">
    <itemtemplate>
        <%# ((BusinessObjects.Object)Container.DataItem).Column1 %> <br />
    </itemtemplate>
</asp:repeater>

This will also give you a compiler warning if the members of a class should change, and
not just runtime fail such as the first example. If you are not using business
objects, but a DataTable, you can  cast
to datarow and get the column right out of there like this:


<asp:repeater id="rptAccounts" runat="server">     <itemtemplate>         <%# ((DataRow)Container.DataItem)["Column1"] %> <br />     </itemtemplate> </asp:repeater>

This both increases speed and makes the application more robust to runtime errors. A win-win situation:)



Wednesday, September 03, 2008 6:42:36 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | ASP.NET
# Monday, September 01, 2008

Third party applications are great, and its very often smart to use open source or buy modules you need before making your own. However a lot of third party applications do create some problems. One is that prices and functionality can change and new, better and cheaper products come to surface.

 

Therefore, you do not want to be to dependent on these applications, so if you find another tool that does the same thing even better, you should be able to switch it out easy. A good way to do this is to create a interface and a factory pattern, and refer to the interface in your code and let the factory class decide what tool to use.  You can also use this pattern to ease mocking of objects. And let the factory return a mock object if you are unit testing.


Example in C#:

using System;

namespace Logger
{
    // An interface is created
    interface iLog
    {
        bool Log(string s);
    }
    //An implementation using enterprise libraries logging
    class EntLog : iLog 
    {
        public bool Log(string s)
        {
            //Implement logic to save to log
            return false;
        }
    }

    //An implementation using log4net to save logs.
    class log4net : iLog
    {
        public bool Log(string s)
        {
            //Implement logic to save to log
            return false;
        }
    }

    // A factory class
    class LogFactory
    {
        // Creates an object, of the right type.
        public static iLog CreateLog(){
            return new log4net();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            // Get the log object to use.
            iLog logger = LogFactory.CreateLog();
            //Print the type of logger used
            Console.Out.WriteLine(logger.GetType());
            // Log a message and write the result
            Console.Out.WriteLine(logger.Log("Something is happening"));
            Console.ReadKey();
        }
    }
}

To change this to use entLog instead of log4net, all you have to do is change the object created in the logfactory.  To add a new logger, all you have to do is create a new class to implement Ilog and change the creator to return the new logger. The creator could also have a constructor that takes an input, so that you can decide in the class using the log what kind of log you want to use.

 

Monday, September 01, 2008 11:33:16 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | Patterns
# Saturday, August 23, 2008

In .NET there is a String.IsNullOrEmpty, but there is no similar thing on an Array. There should be, and there is a solution is in the new extension methods. This one works for all collections.

using System.Collections;

namespace MyExtensions
{
  public static class CollectionExtensions
  {
    public static bool IsNullOrEmpty(this ICollection col)
    {
      return (col == null || col.Count == 0);
    }
  }
}

Saturday, August 23, 2008 8:58:13 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET
# Tuesday, August 12, 2008
The project I'm currently working on has a large codebase and it takes a while with precompilation and everything if I press F5 to start the project every time. I therefore started to attach and detach the debugger manually every time I needed to debug. This saves me tons of time since I don't have to log in and out of the application and find the place I need to debug every time something has to be debugged. A former coworker(http://www.labraaten.com/) also made this nice little macro to attach to w3wp.exe(aspnet_wp.exe in XP) automatically. Saves me even more time.

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics
Public Module Module1
    Public Sub Attach_ASPNET_WP()
        Attach("w3wp.exe")
        Attach("iexplore.exe")
    End Sub

    Public Sub Attach(ByVal processName As String)
        For Each process As EnvDTE.Process In DTE.Debugger.LocalProcesses
            If process.Name.IndexOf(processName) <> -1 Then
                process.Attach()
            End If
        Next
    End Sub
End Module

I have added this macro as a shortcut that triggers every time I hit ALT-I. This can be set by pressing tools, Options, keyboard, locating your macro and add the shortcut.

Tuesday, August 12, 2008 1:18:07 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | Visual Studio
# Sunday, August 10, 2008
I am one of those who still from time to time get to use MS SourceSafe. This week I had a problem with some production code. To make a long story short, I had to rollback the code to see if our code was the source of the problem. However no labels or anything was made.

That's when I found out that using the sourcesafe command line tool, one could get the source as it was on a specific date. Here's how it's done:

First you need to set the an environmental variable named SSDIR to the location of the sourcesafe database. In Windows Vista, this is done by selecting properties on my computer, selecting Advanced System Settings, and the selecting Environmental Variables on the Advanced tab.

Second you have to open a command prompt and locate the SS.exe file which should be located in the sourcesafe-folder. I then created a new folder for where I wanted my new files and then I wrote something like: "c:\program files\source safe\win32\ss get $/Project/location to . -vd30/04/2007 -R"

I believe the date format changes depending on the country selected in your system settings.

This trick did magic and I was able to find out that our code was not the source of the problems experienced in the production environment.

Sunday, August 10, 2008 4:22:26 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | Source Safe
# Friday, July 25, 2008
I have been struggling a couple of hours to get the calendar control in .Net to meet my requirements. What I needed was a control to select a date, so a DateTimePicker would be great. This does not exist in the ASP framework, so I needed to use the calendar control or buy some third party tool.

My requirements was that only dates within a given time frame should be clickable. The next and previous month button should also only be clickable if there were clickable dates in these months. The solution came to me in a dream, no actually a colleague got me on the idea. The answer was in the events.

Heres the code:

 
private void calDueDate_DayRender(object sender, System.Web.UI.WebControls.DayRenderEventArgs e) {
   DateTime dt = Convert.ToDateTime( dueDate );
   if( dt <= DateTime.Today.AddDays(30) ){
      e.Day.IsSelectable = false;
   }
}

private void calDueDate_VisibleMonthChanged(object sender, System.Web.UI.WebControls.MonthChangedEventArgs e) {
   DateTime dt = Convert.ToDateTime( pageTask.DueDate);
   if( dt <= DateTime.Today){
      calDueDate.NextMonthText= "" 
      calDueDate.NextMonthText= ">";
}
else{
      calDueDate.NextMonthText = "";
      calDueDate.PrevMonthText = "<"; 
   } 
} 
private void  calDueDate_Load(object sender, System.EventArgs e) { 
   calDueDate.PrevMonthText = ""; 
}
I must say, the AJAX Calendar looks pretty sweet as well: http://ajax.asp.net/ajaxtoolkit/Calendar/Calendar.aspx

Friday, July 25, 2008 4:20:21 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | ASP.NET
Navigation
Archive
<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910
About the author/Disclaimer

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

© Copyright 2010
Gaute Magnussen
Sign In
Statistics
Total Posts: 17
This Year: 0
This Month: 0
This Week: 0
Comments: 1
Themes
Pick a theme:
All Content © 2010, Gaute Magnussen
DasBlog theme 'Business' created by Christoph De Baene (delarou)