I need an external application that helps deployment of the web application. The application should create the aspnetdb
database on the dedicated SQL server. Then it creates the wanted roles for the web application, and it defines the initial users, assigns their roles and properties. The code below works, but it probably needs some cleaning/refactoring (see the questions below the code):
The App.config
:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<connectionStrings>
<add name="aspnetdbConnectionString"
connectionString="Data Source=COMPUTER\SQL2014INSTANCE;Initial Catalog=aspnetdb;Persist Security Info=True;User ID=sa;Password=saSQL2014INSTANCE"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<membership defaultProvider="SqlMembershipProv">
<providers>
<clear />
<add name="SqlMembershipProv"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="aspnetdbConnectionString"
enablePasswordRetrieval="false"
enablePasswordReset="false"
requiresQuestionAndAnswer="false"
passwordFormat="Hashed"
/>
</providers>
</membership>
<profile defaultProvider="profileProv">
<providers>
<clear />
<add name="profileProv"
type="System.Web.Profile.SqlProfileProvider"
connectionStringName="aspnetdbConnectionString"
applicationName="web_app" />
</providers>
<properties>
<add name="FullName" />
<add name="Phone" />
</properties>
</profile>
</system.web>
</configuration>
The Program.cs
:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Web.Management;
using System.Web.Profile;
using System.Web.Security;
namespace InitAspnetdbAppRolesAndUsers
{
class Program
{
static void Main(string[] args)
{
// The connection string of the following name used throughout the application.
const string conStrName = "aspnetdbConnectionString";
// Web application name.
const string webAppName = "web_app";
// Extract the info from the connection string.
string conString =
ConfigurationManager.ConnectionStrings[conStrName].ConnectionString;
SqlConnectionStringBuilder cns = new SqlConnectionStringBuilder(conString);
string SQLServerName = cns.DataSource; //@"COMPUTER\SQL2014INSTANCE";
string SQLServerUser = cns.UserID;
string SQLServerPassword = cns.Password;
string ASPNETdbName = cns.InitialCatalog;
// Create the aspnetdb database. It does not harm if it exists.
SqlServices.Install(SQLServerName,
SQLServerUser, SQLServerPassword,
ASPNETdbName, SqlFeatures.All);
// The RoleProvider could be removed from the App.config with using this.
SqlRoleProvider roleProvider = new SqlRoleProvider();
NameValueCollection roleProvConfig = new NameValueCollection {
{ "connectionStringName", conStrName},
{ "applicationName", webAppName }
};
roleProvider.Initialize("SqlRoleProv", roleProvConfig);
// The roles to be created for the application.
string[] roles = { "admin",
"poweruser",
"export" };
foreach (var role in roles)
{
if (!roleProvider.RoleExists(role))
roleProvider.CreateRole(role);
}
// I was unsuccessfull to remove the <membership> part from the App.config.
SqlMembershipProvider memProv = new SqlMembershipProvider();
NameValueCollection memProvConfig = new NameValueCollection {
{ "connectionStringName", conStrName},
{ "applicationName", webAppName },
{ "enablePasswordRetrieval", "false" },
{ "enablePasswordReset", "false" },
{ "requiresQuestionAndAnswer", "false" },
{ "passwordFormat", "Hashed" },
};
memProv.Initialize("SqlMembershipProv", memProvConfig);
// I was unsuccessfull to replace the <profile> in the App.config.
// For example, I would like to use the code for the more general
// application, where the properties can differ for different web applications,
// and it would not be hardwired in App.config.
//
// SqlProfileProvider profileProv = new SqlProfileProvider();
// NameValueCollection profProvConfig = new NameValueCollection {
// { "connectionStringName", conStrName},
// { "applicationName", webAppName },
// };
// profileProv.Initialize("SqlProfileProv", memProvConfig);
// It is possible to create the users; however, I would like to get cleaner code.
List<string[]> userList = new List<string[]>
{
new[] { "admin", "adminPa$$word1234", "admin@example.com", "admin poweruser export", "Jan Admin", "111 222 333" },
new[] { "admin2", "admin2Pa$$word1234", "admin2@example.com", "admin poweruser", "Petr Admin", "222 333 444" },
new[] { "admin3", "admin3Pa$$word1234", "admin3@example.com", "admin", "Lenka Admin", "333 444 555" },
};
foreach (var info in userList)
{
string name = info[0];
string password = info[1];
string email = info[2];
// Get the list of the roles. If no role is defined, the array
// of the lenght 1 with empty string inside is created.
// See the testing below.
string[] rolenames = info[3].Split();
string fullname = info[4];
string phone = info[5];
if (memProv.GetUser(name, false) == null)
{
MembershipCreateStatus status = new MembershipCreateStatus();
memProv.CreateUser(name, password, email,
"none", // password question
"none", // password answer
true, // isApproved
null, //
out status);
Debug.Assert(status.Equals(MembershipCreateStatus.Success));
// Assign the roles to the user.
if (rolenames[0] != "")
roleProvider.AddUsersToRoles(new[] { name }, rolenames);
// Set the user properties.
ProfileBase pb = ProfileBase.Create(name);
pb.SetPropertyValue("FullName", fullname);
pb.SetPropertyValue("Phone", phone);
pb.Save();
}
}
}
}
}
I am rather new to C# (coming from C++, desktop native application to ASP.NET, C# web applications) -- I am not very strong in C#, and the ASP.NET infrastructure.
My initial idea was to have a kind of general application that reads some proprietary form of the config text file with the definitions to be interpreted. I tried to get rid of the App.config
, but it seems like to pee against the wind.
So, the second idea was let the App.config
exist, but not to use it for storing any information that can be specific for the deployment case. I tried to implement the most of the functionality in the Program.cs
(here hardwired, but think in terms of getting the info say from another text file, imported from the Excel table, etc.). I was able to fully remove the role provider part from the App.config
. However, I was not able to replace the membership provider, and the profile provider (including the properties definitions) from the App.config
.
I was not able also to get rid of the connection string definition in the App.config
. So, I have taken approach at least not to duplicate the information in the App.config
and in the Program.cs
(e.g. the connection string from the App.config
parsed to get the information).
Anyway, notice the profile provider part of the App.config
-- I do not know how to avoid duplication of the name of the web application. (I was able to do that for the other providers.)
I appreciate any help and idea how to improve the code and the approach.