Wednesday, May 31, 2006

Update: Custom Membership Provider

Yep, it was just that easy. It took me a total of 2 hours to write and test a custom provider that would solve the hack problem I wrote about last time. It would take longer if you wanted to implement a full provider, but I only needed CreateUser and DeleteUser for this issue.

The documentation and examples for creating a membership provider are good, so there's no need to cover the basics. I did run into a couple of gotcha's.

First, I really didn't need total customization so I simply extended the Framework's SqlMembershipProvider. If you are using SqlServer, this works fine. The strange thing here is that the connection string, _sqlConnectionString in the provider, is private. This being the case I had to override Initialize and get the value for myself before passing the config along to the base class. Not difficult, just a silly repeat of existing code in the base.

My only other issue was with providing a good message to users if my custom provider Create or Delete operations gave an exception. The basic model on Create is to trap significant issues and log the specific problem while returning null as the MembershipUser and setting the
MembershipCreateStatus to some error type. In this case, the only applicable status is 'ProviderError' which causes a generic message to be displayed to the user. Logging isn't really helpful in this case, so I wrote the real message to a Session variable on the current HttpContext. OnCreateError, i set the error message to this session value.

Otherwise, all I really did was move my code from the OnUserCreated method into the Create method of my new provider. Wired this provider up instead of the other one, and viola!

Hats off to MS on this strategy implementation.
Submit this story to DotNetKicks

Tuesday, May 30, 2006

Membership Forms Auth Integration with Community Server

I recently set out to solve the following problem:

I have a community site running CommunityServer as one application running under a sub-domain url and acting as part of the site (in a different app) running on the base domain (and www sub-domain). The community site is running Community Server 2.0 and is setup to use forms authentication for Membership, along with Roles, and Profiles. CS uses the new provider model for 2.0, utilizing custom CS providers. The primary site is also 2.0 and is using the built-in .net framework sql providers. I want to be able to login or create a user in either site, and have that login and profile carry over to the other site.

The basic information you need to make this happen in .net forms authentication is straightforward, and is covered here. Make those changes first, then there are just a few adjustments to make to the base site and two tweaks to Community Server to complete the fix.

First, Community Server. The CS providers are configured in the web.config of the CS site. In order to have CS draw from the same Membership data as your base site they must share the application name attribute. You can change this in the web.config section for each provider. However, this isn't likely to change anything in CS. You also need to go into the CS database and change the ApplicationName column value in the cs_SiteSettings table. I couldn't find this as a configurable setting anywhere else in CS. The only other change to make to CS is to set the cookie domain to the same domain as the other site. Obviously this only works if our problem space is dealing in different sub-domains. To do this in the CS control panel go to AdministrationMembershipCookie and Anonymous Settings area and set the Cookie Domain value.

Once you have made these changes to CS, you'll need to restart the application pool.

At this point the two sites should allow login's from one site to carry over to the other (if you have cookies from earlier attempts, you'll need to delete them). The only problem is that if you allow new logins to be created at both sites, you'll need to align new member creation on the base site with member creation on the CS site. One way to deal with this issue is to direct member creation over to your CS site. I wanted my primary site to be self-contained so I decided to create CS-capable logins in the primary site.

First, the CS profile configuration has numerous attributes defined. You should add these to your profile configuration section of your main site's web.config. Second, CS has its own user and profile tables in the database that store additional community data for the standard Membership user created by the provider. The "right" way to do this may well be to create a custom provider. Like any good developer I have a lazy streak that set me looking for a quicker way to accomplish this. I did try to just use the CS providers in my main site. Unfortunately, these providers depend on way too many configuration settings and file locations based on the CS site for this to be worthwhile. What I settled on for now is to add the additional rows to the database in a handler for the CreatedUser event of my CreateUserWizard control. This does require a hack that is probably better dealt with by creating a custom provider, but I'll do that when I have more time.

For now there are two CS tables: cs_Users and cs_UserProfile which need new rows based on the created Membership user. Most of the columns have default values, but you'll need to supply the Membership based ID to both tables, and the new CS based UserId to the UserProfile table (FK), along with the proper SettingsID. Along with this, you need to add this new user to the proper roles associated with CS. By default new users are in the 'Everyone' and 'Registered Users' roles. A simple call to Roles.AddUserToRoles will take care of that.

The hack centers around what to do if the CS database updates fail. The problem is that the Membership operation already completed succesfully (which it must for you to have the FK data for cs_Users) and you are in an event that can no longer cancel the operation. Thus, you need to deal with rolling back the new user yourself and editing the CreateUserWizard messages and diplay format which the website user sees. In theory, a custom membership provider would have access to the create operations in time to deal with providing an error to the control. In this case, I just run the updates in a transaction and delete the new membership user if there is a failure during the cs table inserts. Finally, you have to set custom error text, and eliminate the success message from the control. Good enough to get going. I'll write more when I create the custom provider.

PS: A good article on how the dynamic profile properties are provided to intellisense is here.
Submit this story to DotNetKicks

Tuesday, May 23, 2006

Cool Tool: Hydrus DataSet Toolkit

I am very excited about this great new tool from Hydrus Software that allows you to work with typed DataSet objects without DataAdapters or writing your own sql statements. Basically, it infers database schema from your typed dataset and uses classes called WhereConstraints that limit the results in various ways. It's even fairly easy to write your own constraints if the included constraints don't do the trick.

The DataSet Toolkit saves all the time you would normally spend writing specific queries, or maintaining the code you write for the adapters, etc. I can think of several projects where it was a daily job to update dataadapter statement code every time something changed about the database. With this tool, you can ignore that stuff. If you are used to working with the .net framework CommandBuilder objects, then you still have to write select statements. The DataSet toolkit removes this job too. Pretty cool.

Check it out.
Submit this story to DotNetKicks

Thursday, May 18, 2006

IFrame Gotcha not caught by VS05 parser

The VS'05 script parser is pretty good at catching tag violations when you work in the html view of an aspx page. However, it did not catch the need for an </iframe> closing tag for the iframe element. This is a requirement for the iframe tag in both IE and Mozilla. I didn't know that previously, and was getting the strangest results when I would inspect the DOM and find that everything below my iframe tag was consumed in the enclosing div.
Submit this story to DotNetKicks

Monday, May 08, 2006

Software development is like construction

I am going to depart from my usual facts-only entries because I thought this was funny. I was told again recently that software development is like construction, and that the software development profession should be able to "grow up" and create predictable bug-free software. This was the gist of my reply:

I have heard the comparison between software development and construction a number of times. It's an alluring analogy for those that don't really understand software development. Maybe you've heard it used by a manager looking to explain why getting software done on time, with all the features done correctly shouldn't be so hard.

Software development is like building a bridge. You have a need for which you make a plan, and then set about lining up the materials and manpower necessary to put the plan into action. Construction projects experience set-backs, but through proper project management (the assignment of new resources, overtime hours, etc) the project gets done - and you have a bridge every time.

Yes, software development is just like that. It's just like building a bridge where 4 months into the project the city engineer declares that no bridge shall be fewer than 200 feet, and you were building a 185 ft. bridge. And, after increasing the size of the bridge to 200 feet - resetting some of the primary support posts, adding a few people to the project, and purchasing new materials - you realize that good old Portland cement won't work for this bridge. You look for the proper materials in the marketplace, and find a few companies working on some experimental materials. They aren't selling anything yet, but are happy to let you have their recipes. So, you get your guys working on this new compound while you finish the foundation and supports. After 2 months, they decide they have it and you go back to work pouring the street. About this time, the mayor decides that no cars will be allowed in the city, and your bridge is no longer necessary.

Software development is just like construction. Another person once told me that we can call software developers 'engineers' when they can be sued for doing their job incorrectly. I say, you can sue me for the software when you can tell me exactly what you want.

Of course, there are lots of ways to improve the process of software development and in the last few years I think a lot of progress has been made. However, software is never a bridge. That's hardware. People like software because it becomes whatever they want. I think we just have to figure out how to help people better explain what they need.
Submit this story to DotNetKicks