Routing revisited

It turned out to be a lot easier to work around not being able to override the route parsing method then I had thought it would be last night. Though when they allow me to override that method I will be changing over.

By setting up the following routes I was able to get almost the effect that I was looking for:

RouteTable.Routes.Add(new Route
{
    Url = "Example/[id]/[action]",
    Defaults = new { Controller = "Example" },
    RouteHandler = typeof(MvcRouteHandler)
});

RouteTable.Routes.Add(new Route
{
    Url = "Example/[Category1]/[id]/[action]",
    Defaults = new { Controller = "Example" },
    RouteHandler = typeof(MvcRouteHandler)
});

RouteTable.Routes.Add(new Route
{
    Url = "Example/[Category1]/[Category2]/[id]/[action]",
    Defaults = new { Controller = "Example" },
    RouteHandler = typeof(MvcRouteHandler)
});

Though the execute action just takes the id in

[ControllerAction]
public void Execute(string id)

 

If I find that I actually need access to the Categories found in the path, I can access them using RouteData.Values. It also helps that I had decided that the examples should have a unique ID, that will be a lot like the friendly Urls used in Subtext. That way the directories are there more for aesthetics then utility. Though when the ability to override the route parsing, I will probably be changing it to this if possible later:

[ControllerAction]
public void Execute(string id, string[] categories)

So now that I have the site navigation working the way that I want it, with the exception of a helper method for writing out the longer category listings, its time to work on the maintenance parts of the site.

And so far it seems that this will be a better fit then WebForms was for the last few reworks of the site, since I can make the UserControls containing the examples completely isolated, including having the runat server form in the control. The only one I liked better was the JSON based one, using ASP.Net AJAX Extensions, which would have gone live if AdSense worked with AJAX/JSON sites.

Overall the more I use it the more I like the feel of the framework. Especially now that I realized which of these  I wanted to selected when using a master page.

And so far the best part of the framework is that I don’t have to ignore features to not see this dialog after refreshing the page I just navigated to.

Technorati Tags:

Anonymous Types and Interfaces

I finally had a chance to sit and work with LINQ for a bit, and while it is very cool, a little bit of its luster was lost when I can assign interfaces to the anonymous classes generated by the queries. Meaning I can not do something like:

public interface IExample
{
    string Name { get; set; }
    string Description { get; set; }
}
public class DataGenerator
{
    public IEnumerable<IExample> GetExamples(string categoryName)
    {
//Context Definition
IEnumerable<IExample> examples = from x in db.Examples
//LINQ to SQL Class
where x.Category.Name = categoryName select new IExamples { Name = x.Name
                                 , Description = x.Description } ; // There is probobly better syntax for this return examples; } }

Now I know I could instead do

public class Example
{
    public string Name { get; set; }
    public string Description { get; set; }
}
public class
DataGenerator
{
    public IEnumerable<Example> GetExamples(string categoryName)
    {
       
//Context Definition
        IEnumerable<Example> examples =
            from x in db.Examples
//LINQ to SQL Class,
            where x.Category.Name = categoryName
            select new Example{ Name = x.Name, Description = x.Description };

        return examples;
    }
}

or if I wanted to continue only exposing an interface

 

public interface IExample
{
    string Name { get; set; }
    string Description { get; set; }
}
internal class Example:IExample
{
    public string Name { get; set; }
    public string Description { get; set; }
}
public class DataGenerator
{
    public IEnumerable<IExample> GetExamples(string categoryName)
    {
        //LINQ to SQL Context Definition
        var examples =
            from x in db.Examples 
where x.Category.Name = categoryName select new Example{ Name = x.Name
                              , Description = x.Description }; return examples.OfType<IExample>(); } }

Over all this isn’t a big issue since the second method works just fine for what I am trying to accomplish for the most part. But it feels kind of off having to make a throwaway class to accomplish this.

Technorati Tags: , ,

LINQ, Anonymous Types and Interfaces Revisited

After making my previous post I worked on it a bit more, and found that the best way to accomplish what I was trying to do, creating quick data layer that I can change later, was to simply add the IExample interface to the partial class definition. I know I can create this with Subsonic or another  DAL generator, but currently they aren’t on the list of tech I want to learn at the moment.

And while this did work, I ran into an error in the dbmI designer when I clicked “View Code” from the context menu:

This seems to happen when the project has a default namespace assigned to it or possibly having an Entity Namespace set, since I have both. I haven’t looked to much into it since VS puts the partial class definition that it goes to in the cs file of the same name as the dbml file, so I doubt I will be using the view code much anyway.

Technorati Tags: , ,

HttpHandlers and directory authentication/HttpModules

I decided to implement  Admin Rss Feeds after a particularly draining Friday. For the most part it went pretty smoothly, and learned something about working with a different team too ;). I Implemented an HttpModule that looked for FormsAuthentication redirects for rss feeds and changed it over to use basic authentication so rss readers could authenticate.

And for a while all was good. In fact other then unit tests I had thought I was done.

Then just to be thorough I set up several subfolder blogs off of localhost, and everything stopped working. Apparently something in the way that the rss HttpHandlers in Subtext are called skips all of the HttpModules set up in the web.config. I have a fix for this, I loop through all of the modules in the application and initialize them. I dont think this is the right solution since it also stops the <authorization> section in the /Admin folder is not being looked at either.

 

Technorati Tags: , ,

Subtext Admin Rss Feeds

I just committed the changes to provide 3 administrative rss feeds:

  • Comments Needing Moderation
  • Referrals
  • Errors

The change also uses the HttpModule that will convert forms authentication into basic authentication so that the feeds can be viewed in an Rss reader. After seeing http://msdn2.microsoft.com/en-us/library/Aa479391.aspx I had thought about changing over to use it instead of the simple module I wrote. I decided not to however because it would have meshed well with Subtexts security model.

This was one of the more interesting things I have worked on in a while, though I am already thinking of several improvements that could be made (in the next version). These include:

  • A module for digest authentication.
  • A Configuration section for the new authentication modules to allow them to work with other file times outside of rss feeds.
  • A rework of the way that rss writers are done. Currently a new one needs to be added, along with a HttpHandler when a feed is going to serve up a new object type. It would be nice to be able to configure the feeds using the web.config or setting stored in database to be able to create feeds on any available subtext object collection.
  • Something seems off with the Error feed’s times. I think the local time is being stored without converting it to UTC or specifying a timezone.

 

Technorati Tags: , ,

HttpHandlers and web.config settings

I figured out what was happening in my previous post. It makes a bit more sense now that I have seen it, being able to just stop working on something is handy, basically the Rss feeds don’t do URL rewriting. So the call to /test1/Admin/ModeratedCommentRss.aspx uses the /web.config and would use the /test1/Admin/web.config, but it has no reason to look at the /Admin/web.config.

Not completely sure how I should change this. Right now I have the ModeratedCommentRss.aspx checking to see if the requestor is an Admin, and if not it calls FormsAuthentication.RedirectToLoginPage(). This works, but I would rather a solution that didn’t involve people needing to know to put the check in.

I also found this module helpful when I was figuring out where to do the conversion:  

public class DebugModule:System.Web.IHttpModule
{
    public EventHandler GetEventhandler(string name)
    {
        return new EventHandler(delegate(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            HttpContext context = app.Context;
            if (context != null)
                Debug.WriteIf(context.Response.StatusCode == 302, "Redirecting - ");
            Debug.WriteLine(name);
        });
    }

    public void Init(HttpApplication app)
    {

        Debug.WriteLine("---------------------------------");
        Debug.WriteLine("Module Init");
        Type appType = app.GetType();
        EventInfo[] events = appType.GetEvents();
        foreach (EventInfo eventInfo in events)
        {
            eventInfo.AddEventHandler(app, GetEventhandler(eventInfo.Name));
        }
    }
}

I used that class and a small test web project to figure out how to change the FormsAuthentication over to Basic authentication (seems like mixed authentication should have already been there though). 

Of course shortly after I figured most of it out I saw the link to the MSDN article Phil Haack had posted for the feature request.

I wish C# 3.0 was here already

I was reading IHttpContext And Other Interfaces For Your Duck Typing Benefit over on Haacked. It reminded my of something I did Thursday, which made me wish that .Net 3.5 was already usable.

I actually finally convinced my boss to let me try to automate at least some of the testing. So first order of business, change our the SQL installer program we use to allow it run without user interaction.

After a good amount of refactoring of the monolithic control function, I get that part working. It can now do everything it needs to do by passing in all the stuff I need on the command line. After answering some several questions from the junior developers, several of which they answered them selves during the course of the conversation, I start to move onto making it into something useful.

I decide to make a simple API that I could use to inside of programs, so I can make a quick proof of concept for my boss who is skeptical that it would be feasible to make tests for the SQL (business logic). Something along the lines of here are your options, start and let me know how it went. That’s when the fun started, its a single executable file and “needs” to stay that way (which I agree with overall). After my momentary amnesia about not being able to reference exe files, I decide that I am going to use reflection.

My first attempt went something like:

//The installer has a start method
interface IInstaller{void Start();} 
public IInstaller Bind()
{
	Assembly assembly = Assembly.LoadFile("<Path>");
	Type type = assembly.GetType("namespace.frm");
	ConstructorInfo constructorInfo = type.GetConstructor(new Type[]{});
	IInstaller installer = (IInstaller)constructorInfo.Invoke(new object[]{});
}

That didnt work so well, since while namespace.frm object had a Start method, it wasn’t from that interface, and shared no assemblies in common that I could use an interface from. In the end I decided to make a wrapper class that would take the object and make use a delegate to keep a reference to the Start method.

Something close to this:

public interface IInstaller{void Start();}
public class InstallerWrapper:IInstaller
{
	private delegate void StartMethod();
	StartMethod startDelegate;
	object _installer;
	public InstallerWrapper(object installer)
	{
		_installer = installer;
		startDelegate = (StartMethod)Delegate.CreateDelegate(typeof(StartMethod), installer, "Start");
	}
	public void Start()
	{
		startDelegate();	
	}	
}
public IInstaller Bind()
{
	Assembly assembly = Assembly.LoadFile("<Path>");
	Type type = assembly.GetType("namespace.frm");
	ConstructorInfo constructorInfo = type.GetConstructor(new Type[]{});
	return new InstallerWrapper(constructorInfo.Invoke(new object[]{}));
}

Thinking about what I have read about the implementation of it in C# 3.0 I would likely have needed to do it this way anyways, since atleast from what I have read it is a compile time feature. Haven’t tested it yet on my VS 2008 beta VM yet though, so I could be wrong.

Oh, and please forgive the formatting of the code, haven’t done it much yet.