SubSonic ASP.NET Providers Revisited

It's been quite a while (2 months actually) since I announced the creation of the SubSonic ASP.NET Providers. Since then, I haven't really done much with the code base, mostly since I have to spread my time out between a lot of things now a-days. But I am about to do some major testing. This is mostly due to the fact that I will plug the SubSonic Membership and Role providers that I built into the new SubSonic Forums built on ASP.NET MVC (appropriately code-named Halawai).

When I did my initial commit on the providers into the Halawai code-base, I had to really step back and do some refactorings on-the-fly just to get the thing to compile! The only problem that I really have is the Principal and Identity module that shoves the current user's Principal and Identity into HttpContext. It was a mess, let me tell you.

This post isn't going to be just about the wrongdoings of the Beta, but rather what the current state of the features are, how they work, and how we will precede.

 

Be nice to SubSonic!

The main and ultimate goal of the project is to take SubSonic's generated objects and use them for the membership functionality. At the moment, we use 3 tables: Users, Roles, UsersInRoles. The Users tables stores the users's username, email, password, all that good stuff. Then the Roles stores just the simple role names. UsersInRoles is a map between the Users and Roles, associating a user to a role or roles. The membership and role providers are coded out to use the generated classes thanks to SubSonic.

Currently, we have 3 Stored Procedures to simply cut down on the number of pings to the database. Hopefully we can export some of that logic out to Transactions. Although we have a few SPs in the database, we leverage most of the other data access entirely through SubSonic.

 

IPrincipal and IIdentity

The IPrincipal (SubSonicPrincipal class)  and IIdentity (SubSonicIdentity class)  interfaces are interfaces that tell ASP.NET about your users. They get shoved into the HttpContext.Current.User property. SubSonicIdentity just contains your username and whether the current user is authenticated. IPrincipal stores the Identity and roles (if enabled). I will detail how we push this in in the SubSonic Security Module Section.

 

SubSonic Security Module

Once the SubSonic Security module is loaded, the HttpApplication is told that it should fire the OnPostAuthenticateRequest method. The OnPostAuthenticateRequest method basically checks if the user is indeed supposed to be authenticated (indicated from the HttpContext.Current.User IIdentity). If the user is supposed to be authenticated, it pulls the password and username stored in a cookie and actually validates them again. Then it sets the IPrinciapal accordingly. To fix the new SubSonic Forums, I just cut out some of the functionality of this method to get a quick fix. Just today I went back in and fixed the logic and made double sure that the user was authenticated. I will check that code in and it will be all good :)

 

Nice Config

To wrap this baby up in the most customizable way, we define our own Membership configuration with the typical properties such as password format, password length, etc but also some of our custom settings such as caching the user. It's not real hard to plug into your application, it just takes some thinking on your part as to how you want your membership provider configured.

 

Why use the ASP.NET Provider model for Membership and Roles

The ASP.NET Provider model for Membership and Roles allows a standard way of coding out membership functionality into a web application. Some people have recently just stopped using the Membership provider and have just used their own logic. I completely understand why (mostly because of ASP.NET's built-in membership controls). Use of the ASP.NET Provider model for Membership, Roles, Sitemaps, whatever, is an architectural choice! The aim for this project is to simply offer the option in a clean and customizable way using SubSonic. It offers something a little different from the typical SQL Membership/Roles/Profiles route.

 

Coming up next....

Hopefully I can go in and really tune the providers up to a better quality than they currently reside. That said, if any SubSonic devs, Halawai devs, or developers who know SubSonic, let me know and I will add you as a contributor.

Next on the agenda is some controls. Hopefully I can create controls that generate simple HTML and use JavaScript. Basically all you would have to do is drop them into your web app and you would just have them work. They would imitate the functionality of the ASP.NET Membership controls but also have containable markup, lower level of abstraction (whatever that means :-) ) and a cleaner, non-viewstatey way of doing Membership. I will probably incorporate MVC into this. We will probably go to all .NET 3.5 and make some extension methods (especially to the MembershipUser class) for some added coolness.

 

 

I will give another update to the progress once I sit down and start to extend the SubSonic providers a little more. Hopefully this gives you a better view architecturally to the SubSonic providers that I built. Hopefully you will find them as useful as I do :) So hop on over to the project site and give it a try.



kick it on DotNetKicks.com

4 Comments

  • What is the advantage of SubSonic providers instead of using the standard providers from ASP.NET?

    I would see some benefits to reimplement something in SubSonic for asp.net profiles for example, but why reimplement Roles and Users providers?

    Great article, thanks.

  • @EtienneT

    The advantage to using the providers I've coded out is that you are using SubSonic (which is slightly more performant than ADO.NET), you can have it in the same database as your other tables, and you have the source code at your disposal. Don't think of it as a reimplementation but rather a reconstruction of the data access logic.

  • public override string ResetPassword(string username, string answer)
    {
    User u = new User(User.Columns.Username, username);

    int ret = SubSonic.Sugar.Numbers.Random(11111111, 99999999);

    if (u.IsLoaded && u.SecurityAnswer == answer)
    {
    u.Password = Helper.FormatPassword(ret.ToString(), this.PasswordFormat);
    return ret.ToString();
    }
    else
    throw new MembershipPasswordException("The answer you provided is not valid");
    }


    new password is never saved to database

  • I downloaded Setup-SubSonic-2[1].1-Final.exe and installed on my computer (OS: XP Service Pack 3), when I try to run SubSonic from VS 2005, I got the following error message, does anyone have any idea what may cause this problem? Thanks in advance!

    ERROR: Trying to execute generate
    Error Message: System.ArgumentException: Format of the initialization string does not conform to specification starting at index 0.
    at System.Data.Common.DbConnectionOptions.GetKeyValuePair(String connectionString, Int32 currentPosition, StringBuilder buffer, Boolean useOdbcRules, String& keyname, String& keyvalue)
    at System.Data.Common.DbConnectionOptions.ParseInternal(Hashtable parsetable, String connectionString, Boolean buildChain, Hashtable synonyms, Boolean firstKey)
    at System.Data.Common.DbConnectionOptions..ctor(String connectionString, Hashtable synonyms, Boolean useOdbcRules)
    at System.Data.SqlClient.SqlConnectionString..ctor(String connectionString)
    at System.Data.SqlClient.SqlConnectionFactory.CreateConnectionOptions(String connectionString, DbConnectionOptions previous)
    at System.Data.ProviderBase.DbConnectionFactory.GetConnectionPoolGroup(String connectionString, DbConnectionPoolGroupOptions poolOptions, DbConnectionOptions& userConnectionOptions)
    at System.Data.SqlClient.SqlConnection.ConnectionString_Set(String value)
    at System.Data.SqlClient.SqlConnection.set_ConnectionString(String value)
    at System.Data.SqlClient.SqlConnection..ctor(String connectionString)
    at SubSonic.SqlDataProvider.CreateConnection(String newConnectionString) in C:\svn\subsonicproject\trunk\SubSonic\DataProviders\SqlDataProvider.cs:line 220
    at SubSonic.SqlDataProvider.CreateConnection() in C:\svn\subsonicproject\trunk\SubSonic\DataProviders\SqlDataProvider.cs:line 210
    at SubSonic.AutomaticConnectionScope..ctor(DataProvider provider) in C:\svn\subsonicproject\trunk\SubSonic\Sql Tools\Connection.cs:line 172
    at SubSonic.SqlDataProvider.GetReader(QueryCommand qry) in C:\svn\subsonicproject\trunk\SubSonic\DataProviders\SqlDataProvider.cs:line 357
    at SubSonic.SqlDataProvider.GetTableNameList() in C:\svn\subsonicproject\trunk\SubSonic\DataProviders\SqlDataProvider.cs:line 866
    at SubSonic.DataService.GetTableNames(String providerName) in C:\svn\subsonicproject\trunk\SubSonic\DataProviders\DataService.cs:line 666
    at SubSonic.SubCommander.Program.GenerateTables() in C:\svn\subsonicproject\trunk\SubCommander\Program.cs:line 894
    at SubSonic.SubCommander.Program.Main(String[] args) in C:\svn\subsonicproject\trunk\SubCommander\Program.cs:line 78
    Execution Time: 119ms

Comments have been disabled for this content.