Targeting multiple environments and machines – part 2/2

by 24. May 2011 20:28

In the previous post we looked at why targeting multiple environments and machines can be a problem, and our approach to solving this problem. In this post I will walk you through our solution from a more technical perspective.

Our solution to this problem is achieved primarily by the use of NAnt 1 and some extensions we have built on top of that. Our setup allows us to support different configurations on not only a per-environment basis, but also on a per-machine basis in each environment. Our solution is stand-alone, not requiring any external references, and it is easy to setup. Best of all? We are offering it to you, free of charge! A download link for an example project can be found at the bottom.

Let us dive right into it and take a look at an example project setup, see the figure below:

image

The interesting bit here is the “Valtech.ConfigFramework” project. The project is divided into a number of folders:

  • configfiles/dynamic: This holds all the files that needs to be dynamically altered based on the target environment and/or machine.
  • configfiles/include: This holds all configuration properties for each target environment and/or machine, needed to alter the files in “configfiles/dynamic”.
  • configfiles/static: This holds all files that need to be included for a specific build configuration, but does not need to be altered, e.g. license files.
  • framework: This holds all necessary files needed.

When building the project, the include files are merged with the dynamic files, and output along with any static files to the output folder( s). The actual folder structure looks like this:

iphysical folder structure-1

Here, there is only one application named “ConfigFrameworkTestApp”, but you could have any number of applications using the same Valtech.ConfigFramework.

Dynamic and static files

In the previous solution overview, I showed an application alongside the configuration framework. In the figure below, two folders “License” and “Log” have been added to the application, and the “configfiles/dynamic” folder and “configfiles/static” folder for the Valtech.ConfigFramework project have both been expanded:

image

As you can see, there is one top-level folder below the “configfiles/dynamic” folder named “ConfigFrameworkTestApp”. This is simply the name of the application being targeted (see the physical folder structure figure above). One level below the application folder is a folder named @Log. Files from the ConfigFramework project are output to the application root directory, and the prefix @ tells the ConfigFramework to copy any files in this folder to the corresponding folders in the application

Below the “configfiles/static/ConfigFrameworkTestapp” folder there are three folders: development, prod and qa. These folders each act as containers for files specific to an environment. In the figure above, the qa-environment folder is expanded, and below this folder is a folder named @License containing a license file specific to this environment.

So in this case the following files are copied from the ConfigFramework project to the application:

From (\configfiles\)To (\Applications\ConfigFrameworkTestapp\)
dynamic\ConfigFrameworkTestapp\@Log\Log.configLog\Log.config
dynamic\ConfigFrameworkTestapp\App.configApp.config
static\ConfigFrameworkTestapp\qa\@License\license.txtLicense\license.txt

The files from the dynamic folder, are altered based on configuration properties from the “configfiles/include” folder (more on this below) whereas the static files are simply copied to the application.

In this example we are only modifying and copying .xml and .txt files, but the ConfigFramework is not restricted to working with any specific file types, so you could for example add external .dll files as static files to be copied.

Include files

Now that we have an idea of what is going on, let us take a look at some actual examples. First take a look at the following figure:

image

As you can see, the “configfiles/include” folder is expanded, once again revealing the application “ConfigFrameworkTestApp” as the top-level folder. Below that there are a number of folders: development, prod and qa, each used for targeting a specific environment. However, as evident below the “configfiles/include/ConfigFrameworkTestApp/development” folder (which holds all properties for configuring the application in the development environment), there are two more folders: dk-lt-jani64 and dk-lt-jeth64. These are both actual machine names, and any properties defined for a machine will automagically override properties defined on the environment (i.e. development in this case) level.

Sounds confusing? Hopefully not so much if we look at an example. Below are a few lines from the App.config of the application:

<appSettings>
<add key="HelloWorld" value="${HelloWorld}"/>
<add key="GlobalWorld" value="${GlobalWorld}"/>
</appSettings>

And next are the contents of the configfiles\include\ConfigFrameworkTestApp\development\developer.properties file in the Valtech.ConfigFramework project:

<?xml version="1.0" encoding="utf-8"?>
<target xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
<property name="HelloWorld" value="Hello World. I'm from development"/>    
</target>

Notice the property with the name “HelloWorld”. When you build the Valtech.ConfigFramework, the value of this property will automatically replace the ${HelloWorld} token in any files under the configfiles\dynamic folder. Now, what if my personal HelloWorld value differ from the general development settings? Answer: I simply override the property in my machine specific developer.properties file – i.e. configfiles\include\ConfigFrameworkTestApp\development\dk-lt-jani64\my.properties:

<?xml version="1.0" encoding="utf-8"?>
<target xmlns="http://nant.sf.net/release/0.86-beta1/nant.xsd">
<property name="HelloWorld" value="Hello World. I'm from my own machine"/>
</target>

But the ConfigFramework is not restricted to simply having key-value pairs. Supposing you only want to include parts of a Web.config for specific build configurations, and not others, you can do that too. For example, if I include the following line in my App.config file:

${SMTP}

The ConfigFramework will look for a SMTP.property file under “configfiles/include”, and replace the line above with the contents of the file, e.g.:

<system.net>
<mailSettings>
<smtp>
<network host="mailserver.mydomain.com" />
</smtp>
</mailSettings>
</system.net>

Putting it all together

So how does all this work? The keen eyed reader may have noticed a number of .cmd files in the figures above, and this is where all the action is happening. Well, that and solution configurations in Visual Studio.

physical folder structure-1

The figure above shows the different solution configurations for this solution. Depending on the active solution configuration, the application@environment (in this case ConfigFrameworkTestApp@development) will be built by calling the application@environment.cmd file. To create a new configuration, you simply need to copy one of the existing application@environment.cmd files, and rename it according to your Visual Studio solution configuration.

Wrapping things up

This solution, allow us to automatically build configurations for any number of environments and machines. It does require some initial setup, but once that bit is over, you don’t ever have to worry about manually merging config files from one environment with another. Secondly, if you pair this approach with a continuous integration package (e.g. TeamCity) you can automate the entire process of building and deploying your applications across multiple environments.

Now, for what you’ve all been scrolling to:

Make sure to replace the folder names “your-machine-hostname” with the name of your machine and try to play around with the different build configurations:
  • ConfigFrameworkTestApp@development
  • ConfigFrameworkTestApp@prod
  • Manual configuration update

The first two work as explained above, where as the third “Manual configuration update” bypasses the configuration builder which is useful if you would like to build your application without also building all configuration files.

Notes:

1. NAnt is a (free) .NET build tool based on Apache Ant, designed for developing software across multiple platforms. There are tons of examples on how to use NAnt on the projects homepage, so if you are new to NAnt I’d advise you to take a look at the project’s homepage here: http://nant.sourceforge.net/.

Tags: , , ,

.NET | Development

Targeting multiple environments and machines - part 1/2

by 7. March 2011 21:14

A classic problem, or challenge if you are a glass half-full type of person, in software development is how to target multiple environments. By environments, I really mean different machines. Machines for development, for testing, for staging and for production, e.g.:

blog

The problem

In some areas of software development, this is a problem of supporting different hardware setups, but in web development we (most often) only have to worry about different database connection strings, mail server setup, paths to e.g. upload folders, and similar configuration differences on different environments.

Several solutions to this problem have been suggested, but common for all solutions is a goal to automate this process of supporting multiple environments without having to manually figure out which configuration bits that needs to be flipped.

Furthermore, as developers we are used to working by contracts. At a very lowest level we have an unspoken contract with the compiler, making sure that we keep to the rules of our language of choice. Our types and methods too define contracts on what we are allowed to do. Webservices rely heavily on contracts, just as any services in the real world do. Surprisingly though, while our applications and websites more often than not rely on configuration files to keep them running, our configuration files are just flat files. In other words, there is no contract preventing us or at least warning us from making errors that in worst case scenarios could bring everything crashing down around our ears. Solving this part of the problem is hard. While you can build procedures to automatically check that everything looks nice, actually validating the data is a topic worthy of a book in itself.

Solution

Automatically building environment specific configurations is nothing new, in fact a tool is built right into Visual Studio 2010.

The Web.config transformation tool in Visual Studio allows you to:

  • set attributes
  • remove attributes
  • delete nodes
  • replace nodes
  • insert nodes

in your Web.config file. This is great.. as long as all you require is the ability to modify your Web.config file from environment to environment but if either of the following statements are true:

  • I keep certain configuration bits outside the Web.config file
  • I’m not .NET web developer using Visual Studio

this solution is not for you. Not to mention, this does not ensure that aconfig file for a specific environment is not missing a vital property.

Our solution

While our work primarily involves ASP.NET web development, the projects we work on require a much high degree of configuration customization than the Web.config transformation tool described above facilitates.

Our goal was to create a configuration framework that would provide us with a high degree of customization and at the same time be statically compiled, preventing us from building and then deploying a bad environment configuration.

We ended up with a solution looking something like the figure below:

overview-1

In the figure above, the boxes on the left (“Templates” and “Environment specific properties”) are especially interesting.
“Templates”, surprisingly, contains templates. For example, it might contain a Web.config template in which connection string values are not defined. It could also contain a license file template, in which the license key was not defined. Instead of defining the environment specific values, that is connection string and license key values, a uniquely named property is written. This property will then be substituted for an environment specific value by the configuration framework when the project is built.

At build time, we then invoke the configuration framework, which collects all the templates and attempts to populate them with properties from a specific environment configuration. Now, if a property is not found, the build will fail and warn us about the missing property. This is great, since we only have to maintain one configuration file (the template), but can still modify and build it across multiple environments and at the same time ensure that all configuration files contain all required properties.

The figure below illustrates this process:

process-1

Hopefully this has piqued your interest. If so, stay tuned for the next post in which we will go into the technical details of our solution and best of all, offer it to you free of charge.

Tags: , , ,

.NET | Development

Changing Membership, Profile and Role provider connection string based on environment

by 14. August 2010 00:59

You’re probably no stranger to having to target different environments for your project, e.g. development, qa, staging and production. Often your application relies on a configuration file, web.config for ASP.NET applications, and in this file you will usually have one or more connection strings, targeting a specific server based on the environment. There are several ways to solve this problem, e.g. by using nANT or pre-build events in Visual Studioand indeed I would recommend either of these approaches over the one I’m going to describe below.

However, what do you do if you are deploying your web-application to a deployment server, and that server is then responsible for deploying to two or more environments, e.g. staging and production, and you have now way to tell the server to distinguish between the different environments. In other words, you are stuck with one web.config file. This is not necessarily a problem, unless you are using the membership provider which in its configuration looks something like this:

<add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SqlServices" applicationName="MyApplication" />

Notice the connectionStringName property. Turns out solving this is pretty straightforward, and Brendan Caffrey over at devspade.com posted an excellent article on how to do it: Using dynamic connection strings with the Asp.Net Sql Membership Provider

However, he only posted the solution for the Membership provider. What if you’re using the Role and profile provider as well? Luckily, the solution is just as simple. I won’t go into details, but just point to Brendan’s article, but here’s the entire source code.

First the actual source code:

using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Reflection;using System.Text;using System.Web;using System.Web.Configuration;using System.Web.Profile;using System.Web.Security;namespace YourNameSpace
{
public class CustomMembershipProvider : SqlMembershipProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);// Update the private connection string field in the base class.
string connectionString = //DetermineYourConenctionString();
// Set private property of Membership provider.
var connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
}
public class CustomProfileProvider : SqlProfileProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);// Update the private connection string field in the base class.
string connectionString = //DetermineYourConenctionString();
// Set private property of Membership provider.
var connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
}
public class CustomRoleProvider : SqlRoleProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);// Update the private connection string field in the base class.
string connectionString = //DetermineYourConenctionString();
// Set private property of Membership provider.
var connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
}
}

And then the web.config:

<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear/>
<add name="CustomMembershipProvider" type="YourNameSpace.CustomMembershipProvider, YourNameSpace" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="/"/>
</providers>
</membership>
<profile defaultProvider="CustomProfileProvider">
<providers>
<clear/>
<add name="CustomProfileProvider" type="YourNameSpace.CustomProfileProvider, YourNameSpace" connectionStringName="ApplicationServices" applicationName="/"/>
</providers>
</profile>
<roleManager enabled="true" defaultProvider="CustomRoleProvider">
<providers>
<clear />
<add connectionStringName="ApplicationServices" applicationName="/" name="CustomRoleProvider" type="YourNameSpace.CustomRoleProvider, YourNameSpace" />
<add applicationName="/" name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</roleManager>

Have fun!

Tags: , , , ,

ASP.NET

ASP.NET MVC 2 and OpenID

by 5. August 2010 01:10

In this post I’ll be showing you how to integrate OpenID authentication on your ASP.NET MVC 2 site. To this end, I’ll be integrating DotNetOpenAuth and openid-selector, so go ahead and download the latest versions of both and extract them to a folder of your choosing.

First, open Visual Studio 2010 and create a new project by going to File > New > Project > Web > ASP.NET MVC 2 Application:

newproject

Click “OK” and you will be asked if you want to create a unit test project or not. For the purpose of this article I’ll select no, but feel free to create one.

Now, after Visual Studio is done creating your project, you can hit F5 and you should see this page in your browser:

initialpage

This is actually a fully functioning ASP.NET MVC site, but our aim is to replace the default “Log On” mechanism with an OpenID approach. To do this, we’ll be integrating DotNetOpenAuth as mentioned earlier. DotNetOpenAuth is a free library that allows you to integrate OpenID by simply adding an ASP.NET control to your page.

To use DotNetOpenAuth, right click “References” in the Solution Explorer, choose “Add Reference” and browse to where you extracted DotNetOpenAuth to. Go to the bin folder, select “DotNetOpenAuth.dll” and click “Open”, then “Add” and finally “Close”. Now we are almost ready to use DotNetOpenAuth, but first we need to add some lines to the Web.config file.

Add the following lines inside the <configuration> element in your Web.config file:

<configSections>
<section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true"/>
</configSections>
<uri>
<idn enabled="All"/>
<iriParsing enabled="true"/>
</uri>
<dotNetOpenAuth>
<openid>
<relyingParty>
<security requireSsl="false"/>
<behaviors>
<!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible
with OPs that use Attribute Exchange (in various formats). -->
<add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth"/>
</behaviors>
</relyingParty>
</openid>
<messaging>
<untrustedWebRequest>
<whitelistHosts>
<!-- since this is a sample, and will often be used with localhost -->
<add name="localhost"/>
</whitelistHosts>
</untrustedWebRequest>
</messaging>
<!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. -->
<reporting enabled="true"/>
</dotNetOpenAuth>

Now we are all set to use DotNetOpenAuth, but first we’ll integrate openid-selector. openid-selector is a simple JavaScript, where users instead of entering their full OpenID identifier, simply clicks the logo of their OpenID provider and then enter their username. Luckily it is really simple to integrate.

There are 4 steps to integrate openid-selector with your ASP.NET MVC 2 project:

  1. go to where you extracted it in the first place and open the css folder. Inside this you’ll find a file named openid.css. Copy the contents of this file and insert it at the end of the Site.css file in your Visual Studio project under the “Content” folder.
  2. Add a new folder to your project called “images”. Then, right click it and select “Add” > “Existing item…” and then browse to the openid-selector/images folder. Select all the files and click “Add”.
  3. In your Visual Studio project, right click the “Scripts” folder and select “Add” > “Existing Item…”  and then browse to the openid-selector/js folder, select the “openid-jquery.js” file and click “Add”. Before moving on, you need to open the newly added “openid-jquery.js” file and scroll down to the line (line number 88 in the version I tested):

    img_path: 'images/',

    and replace it with the line:

    img_path: '../images/',
  4. Finally, go to “Views” > “Shared” and open the “Site.Master” file and insert the following lines in the <head>-tag:
    <script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script>
    <script type="text/javascript" src="../../Scripts/openid-jquery.js"></script>
    <script type="text/javascript">
    $(document).ready(function () {
    openid.init('openid_identifier');
    });
    </script>

Your project should now look something like this in the Solution Explorer:

folderstructure

Now, after all the configuration is done, it’s finally time to setup the OpenID integration. Open the “LogOn.aspx” in “Views” > “Account” and replace the <asp:Content ID=”loginContent”..> with this:

<asp:Content ID="loginContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>Log On</h2>
<p>
Please enter your username and password. <%: Html.ActionLink("Register", "Register") %> if you don't have an account.
</p>
<form action="Authenticate?ReturnUrl=<%=HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"]) %>" method="post" id="openid_form">
<input type="hidden" name="action" value="verify" />
<%: Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.") %>
<div>
<fieldset>
<legend>Account Information</legend>
<div class="openid_choice">
<p>Please click your account provider:</p>
<div id="openid_btns"></div>
</div>                               
<div id="openid_input_area">
<%: Html.TextBox("openid_identifier") %>
<input type="submit" value="Log On" />
</div>
<noscript>
<p>OpenID is service that allows you to log-on to many different websites using a single indentity.
Find out <a href="http://openid.net/what/">more about OpenID</a> and <a href="http://openid.net/get/">how to get an OpenID enabled account</a>.</p>
</noscript>
</fieldset>
</div>
</form>
</asp:Content>

Click F5 and browse to the “Log On” page, and you should see this:

logon

Almost done now. Now we just need to hook up the Controller, so back in Visual Studio, expand “Controllers” and open AccountController.cs.

First we need to add several using statements, in order to use the DotNetOpenAuth library:

using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.RelyingParty;

Next, in the top of your class, add this static variable:

private static OpenIdRelyingParty openid = new OpenIdRelyingParty();

And then add the following method:

[ValidateInput(false)]
public ActionResult Authenticate(string returnUrl)
{
var response = openid.GetResponse();
if (response == null)
{
// Stage 2: user submitting Identifier
Identifier id;
if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
{
try
{
var request = openid.CreateRequest(Request.Form["openid_identifier"]);
//Ask user for their email address
ClaimsRequest fields = new ClaimsRequest();
fields.Email = DemandLevel.Request;
request.AddExtension(fields);
return request.RedirectingResponse.AsActionResult();
}
catch (ProtocolException ex)
{
ViewData["Message"] = ex.Message;
return View("LogOn");
}
}
ViewData["Message"] = "Invalid identifier";
return View("LogOn");
}
// Stage 3: OpenID Provider sending assertion response
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
MembershipUser user = MembershipService.GetUser(response.ClaimedIdentifier);
if (user == null)
{
MembershipCreateStatus membershipCreateStatus;
//Get custom fields for user
var sreg = response.GetExtension<ClaimsResponse>();
if (sreg != null)
membershipCreateStatus = MembershipService.CreateUser(response.ClaimedIdentifier, "12345", sreg.Email);
else
membershipCreateStatus = MembershipService.CreateUser(response.ClaimedIdentifier, "12345", "john@doe.com");
if (membershipCreateStatus == MembershipCreateStatus.Success)
{
FormsService.SignIn(response.ClaimedIdentifier, false /* createPersistentCookie */);
return RedirectToAction("Index", "home");
}
ViewData["Message"] = "Error creating new user";
return View("LogOn");
}
FormsAuthentication.SetAuthCookie(user.UserName, false);
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
case AuthenticationStatus.Canceled:
ViewData["Message"] = "Canceled at provider";
return View("LogOn");
case AuthenticationStatus.Failed:
ViewData["Message"] = response.Exception.Message;
return View("LogOn");
}
return new EmptyResult();
}

Before this will compile, you need to make a change to the “Models” > “AccountModels.cs” file.

Add this line to the IMembershipService interface:

MembershipUser GetUser(string userName);

and then add the method implementation to  the AccountMembershipService class:

public MembershipUser GetUser(string userName)
{
MembershipUser currentUser = _provider.GetUser(userName, false /* userIsOnline */);
return currentUser;
}

And we’re done! Note: this code is based on the provided samples you get when downloading DotNetOpenAuth, and it is only a guide to get it setup with an ASP.NET MVC 2 project, not a ready-for-production solution.

Tags: , , , ,

ASP.NET 4 and MySQL Membership Provider

by 5. August 2010 01:07

Recently I had to setup an ASP.NET MVC 2 project which would utilize the built-in membership of ASP.NET. However, I didn't have access to a MS SQL database, so I had to use a MySQL data provider instead. The following is a quick guide on how to get it setup.

First, you need to download the MySQL Connector/Net from this page. This makes it possible to connect to MySQL databases from .NET applications and gives you access to the ADO.NET interfaces. I choose to download the latest development release (at the time of writing) Connector/Net 6.3.3 beta, as this fully integrates with Visual Studio 2010 which the latest public release (6.2.3 at the time of writing) does not. Download BOTH the source (mysql-connector-net-6.3.3-src.zip) and the installation file (mysql-connector-net-6.3.3.zip). I will explain why in a second.

Once you've downloaded both files, extract them and install the connector. Now, normally when using the membership provider, the database tables/schemas are automatically created. The MySQL membership provider does this as well, unfortunately it just doesn't do it right. At least it didn't work for me. Instead, you have to create the databases semi-manually. Go to the location where you extracted the source and browse to the following folder "\MySql.Web\Providers\Properties". In this folder you will see a number of .sql files: schema1.sql, schema2.sql, schema3.sql, schema4.sql, schema5.sql and schema6.sql. Run each of these, in turn and starting with schema1.sql, against your MySQL database.

Now, fire up Visual Studio 2010 and open your application. Add a reference to MySql.Web.dll which can be found in the directory you installed the Connector, e.g. C:\Program Files\MySQL\MySQL Connector Net 6.3.3\Assemblies\v2.0

Next, unless you haven’t done this already, add your MySQL connection string to the configuration/connectionStrings element in the Web.config, e.g.:

<connectionStrings>
<add name="MySQLConn" connectionString="Server=SERVERADDRESS;Database=DATABASENAME;Uid=USERNAME;Pwd=PASSWORD;"/>
</connectionStrings>

Finally, open up your Web.config and add these lines to the <system.web> element:

<membership defaultProvider="MySqlMembershipProvider">
<providers>
<clear/>
<add name="MySqlMembershipProvider" 
type="MySql.Web.Security.MySQLMembershipProvider,MySql.Web,Version=6.3.3.0, Culture=neutral,PublicKeyToken=c5687fc88969c44d" 
autogenerateschema="true" connectionStringName="MySQLConn" 
enablePasswordRetrieval="false" enablePasswordReset="true" 
requiresQuestionAndAnswer="false" requiresUniqueEmail="false" 
passwordFormat="Hashed" maxInvalidPasswordAttempts="5" 
minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" 
passwordAttemptWindow="10" passwordStrengthRegularExpression="" 
applicationName="/" />
</providers>
</membership>
<profile defaultProvider="MySqlProfileProvider">
<providers>
<clear/>
<add name="MySqlProfileProvider" 
type="MySql.Web.Profile.MySQLProfileProvider,MySql.Web,Version=6.3.3.0,Culture=neutral,PublicKeyToken=c5687fc88969c44d" 
connectionStringName="MySQLConn" applicationName="/" />
</providers>
</profile>
<roleManager enabled="true" defaultProvider="MySqlRoleProvider">
<providers>
<clear />
<add name="MySqlRoleProvider" 
type="MySql.Web.Security.MySQLRoleProvider,MySql.Web,Version=6.3.3.0,Culture=neutral,PublicKeyToken=c5687fc88969c44d" 
connectionStringName="MySQLConn" applicationName="/" />
</providers>
</roleManager>

Now, you've (hopefully) got a fully working MySQL membership provider. To test it, go to Project > ASP.NET Configuration and go to the Security tab. Here you should be able to manage users and roles.

NOTE: Make sure you enter the correct connectionstring name, version number and PublicKeyToken in the Web.config. The version number is self-explanatory, but the PublicKeyToken can be a little trickier to figure out. The PublicKeyToken value is most likely the same, but to make sure see this link on how to find the PublicKeyToken for a .dll

Tags: , ,

ASP.NET | MySQL