This evening I posted an update to the AspDotNetMVC site that includes allowing voting on the ASP.NET MVC blog posts, buzz, articles and news that the site collects. You can see a page that uses it here: http://aspdotnetmvc.com/blogs. Rather than create my own rating system I decided to leverage Kigg instead. I mentioned in a previous post that I had been looking at Kigg and that I had upgraded it from ASP.NET MVC Preview 2 to Preview 3. Why not use Kigg? It has nearly everything that I need, it is open source, and it's built on ASP.NET MVC. It seems appropriate to use it for a site that's all about ASP.NET MVC. Additionally, my mucking around in the project is helping me learn ASP.NET MVC a little so I can convert the AspDotNetMVC site to it at some point.
This is the first of a couple of articles where I will describe some of the changes that I had to make to Kigg to allow it to support the way that I wanted to use it on AspDotNetMVC. My goal was to allow voting on items on the AspDotNetMVC site without requiring visitors to bounce over to a Kigg site to do so. Because Kigg only allows members to vote once on an article and I wanted the same for the main site I had to hook up ASP.NET Membership to the AspDotNetMVC site and have both sites use the same membership database. Yes, I probably could have just used cookies, session, IP address or something else to accomplish the same thing, but I will have site "members" in the future (for forums, etc) so I didn't want to invent something now just to end up rewriting it in the future when membership was really needed. I hear some of you mumbling "YAGNI" already, and I don't disagree, but in reality the path of least resistance was to go ahead and wire up membership now on the main site rather than rewrite Kigg to use something other than ASP.NET membership.
Today I'll talk about decoupling the ASP.NET membership database from the Kigg database and codebase (why will be explained below). The follow-up post will be about writing two WCF web services for Kigg to allow other sites to retrieve the count of votes for "articles" and to vote on articles. The final post of the three will show how easy it is to setup support for OpenID and integrate it with your ASP.NET Membership system to allow users the option of using OpenID or creating a traditional account on the site.
I wrote a paragraph or two above that I needed to create one ASP.NET membership store and let both the AspDotNetMVC site and the Kigg site use it. The Kigg site database already comes with the asp.net membership stuff baked into it. I didn't want the main site's membership to depend on the Kigg database, though. In fact I wanted to use a different database for membership, one that is solely for membership, that I use for several sites. Upon trying to use an external membership database for Kigg I found a couple of "issues". There are a couple of foreign key relationships setup between the Kigg tables and the aspnet_Users table and the Kigg data model actually includes the aspnet_Membership and aspnet_Users tables. I can't really lay too much blame on the original authors because I know I've done similar before: expect the membership tables to be local (as part of the primary database) but over the years I've learned better so I set about removing those dependencies. Following are the steps required to break the ASP.NET membership stuff away from Kigg to allow you to use an external data store for membership.
1. Remove foreign key constraints between Kigg tables and asp.net membership tables. If I remember correctly there are three: Story -> aspnet_Users, Comment - > aspnet_Users, Vote -> aspnet_Users.
2. [Optional] Remove asp.net membership tables, view, sprocs from the Kigg database. I did it manually, but you probably should use aspnet_regsql if you're going to remove them.
3. Remove the User and Membership tables from the model, Kigg.dbml. Just open up the designer, click on them and hit the delete key then save.
4. Update the UserItem model, adding the following public constructor. Rather than use the "else anonymous" branch you may want to throw a "user does not exist" exception or something like that, but setting it to a "generic" user was what I wanted for my situation.
public UserItem(Guid membershipId)
MembershipUser user = Membership.GetUser(membershipId);
if (user != null)
this.Name = user.UserName;
this.Email = user.Email;
this.Name = "Anonymous";
this.Email = "firstname.lastname@example.org";
5. Update the KiggDataContext model, replace any instance of:
PostedBy = new UserItem
Name = s.User.UserName,
Email = s.User.UserDetail.Email
with this: PostedBy = new UserItem(s.PostedBy)
6. Add a new connectionstring to your web.config, probably named something like "Membership" :), to point to your membership database.
7. Update the Membership, RoleManager, and Profiles providers sections of your web.config to use the new connection string. Remember to change the "ApplicationName" property if appropriate.
That's all you need to do. Now you can use Kigg in conjunction with any other existing applications and/or membership store you already have.