Sunday, February 28, 2010

Visual Studio 2010 features preview.

Today I got an opportunity to attend the “Visual Studio 2010 Pit stop” in Chennai. If I am not wrong it is a pre launch event which Microsoft is conducting in different cities to generate interest and gauge developers reaction on the various features which is getting introduced in VS 2010. I thought why not blog on some of the features which were demoed.

With the release of VS 2010 Microsoft is trying to make Visual Studio capable of managing all the phases of Application Lifecycle Management (ALM). They want VS 2010 to target developers, architects and testers. Lets see what VS 2010 brings to the table for different audience it targets.

For developers

The first feature which was demoed was simple yet powerful, which was targeted towards developers like me, who hate to drag and drop controls from the toolbox instead prefer writing the controls in HTML view. The feature is a kind of auto complete whereby when in the HTML view of an aspx page you need to type the name of the control and from the intellisense one need to select the control and press tab and VS 2010 will add the full tag with ID, Text and other properties. Though the beta release of VS 2010 which was used for the demo didn’t auto generate ID and other properties, it only auto generated “Text” property, but it was promised that the production version would generate most of the relevant properties like ID etc.

Another feature was a template kind of feature for creating web sites where you get pre-configured pages for login, user registration etc. One gets a full website with some color scheme applied and .mdf file in the “App_data” folder. The .mdf database has all the tables for user management and one can generate schema from this .mdf and recreate the tables in Sql Server 2005/2008. One can use the login, user registration pages without doing any changes or writing any code. Now creating a website is just few clicks away. Microsoft is making the life of a developer easy. Is it? I would say nah, with all these automatic features the developer will forget coding and he will become a lazy bum. Nowadays, when I do interviews many a developers don’t know many of the basics, with all these new changes the next generation developers job will have less of coding and more of configuration.

Next feature was the business layer code generation feature from the Entity framework. You need to follow some of the steps in a wizard and select the needed tables from the database and voila you have your Business Access Layer (BAL) auto generated code. If you tie this auto generated BAL layer to a grid and set some properties you have a grid which is capable of adding, deleting, updating, sorting records without writing a single line of code. One also gets search integration using new search extender control where you need set some properties and you can filter your records based on the search criteria.

IntelliTrace

The IntelliTrace feature like Intellisense is going to become a huge hit with developers. With intellitrace you can exactly pinpoint which line of the code threw the exception with values which were passed to the function. Intellitrace feature in combination with Test and Lab management features of VS 2010 is going to rock.

For Architects

For the architects visual studio supports UML diagram drawing capabilities. You can draw class diagrams, dependency diagrams, sequence diagrams etc which were earlier done in Visio or some other tool. The architects can have validation checks on dependency during compile time or whatever policies they have set. For e.g. take a simple e.g. where we have a Presentation layer, Business Access Layer and Data Access Layer. The architect doesn’t want the presentation layer to interact with DAL. He can set a constraint like this and during compile time the system will throw an error if the constraints is broken. Also you can restrict classes in a dll from interacting with other dlls. One can generate sequence diagram from methods by right clicking on the method and selecting some option from the context menu.

Visual Studio Test and Lab management

This feature of VS 2010 just blew my head away. This feature is aimed at the testers but I feel this feature will be a boon to the developers. In this feature the testers will have provision to add their manual test steps and record them and then replay them. The feature works like this. The testers will first enter all the manual test steps like the following in an UI.

1. “Open IE and type the following url: http://someone.com”.

2. Click register link from the UI.

3. Fill first name with “Sandeep”

4. Fill last name with “P.R”

5. Fill email field with “sndppr@gmail.com

6. Fill the password field with “123456”

7. Fill the confirm password field with “123456”

8. Click the “Register” button.

Once the steps are done the tester can now start recording the steps. The advantage of this feature is that if there is an error in step 8 the tester need not repeat all the steps manually. Once the recording is over he can replay the steps upto step 8 and he need not manually do all the above steps. Think of a situation where there are 50 steps and the error happens in the 45th step then once the bug is fixed the tester has to repeat all the 44 steps manually to reach the 45th step which threw error. Just by recording the steps he can play them back up to the 44th step and then carry on the recording from the 45th step. Can you imagine how much time will be saved on this? It will be huge. Hats off to Microsoft for this.

That’ not all. What happens when a tester posts a bug? Bang comes the reply from the developer, “it works on my system, so sorry boss its not a bug”. Some developer even go to the extend where they ask to reproduce the bug. Poor tester, he has to repeat the whole 44 steps to show the bug. Not any more. Once the tester finds a bug he can use the new features to post the bug while he is recording and can assign the bug to a developer. The developer when he gets the bug has got links to view the video of the recording where he can exactly see what the tester has done to reproduce the error. Now there will be no more tester developer acrimony. Also to top it all he will get the IntelliTrace link along with the link to view the video. When the developer clicks the IntelliTrace link, it goes to repository (Team Foundation Server [TFS]) and opens your code and shows you the exact place where the error occurred. Not only this, it gives you the exact values which were used by the tester and which were passed to the function. Just by clicking a link you can know the exact line number where the error has happened and the data which caused the problem. Isn’t it so cool. IntelliTrace is gonna ROCK. Developers, our life is going to be simple. :) With IntelliTrace, I think, the bug fixing time taken by the developers will be reduced to 1/4th of what a normal developer takes. Wow that’ really superb. After all time is money.

With the above features and many more, Visual Studio 2010 is going to ROCK. Waiting to get my hands dirty with VS 2010.

Try to know more….

Sandeep

Sunday, February 7, 2010

Extending the membership provider.

Lots of people ask this question on how to extend the “MembershipProvider” to make use of custom authentication like using existing database or make use of some other way of authentication. Many blogs show you how to extend the membership provider class but don’t give you what other class they have used in the backend. With this blog I would not only show how to extend MembershipProvider class but will give you the full source code of the web service which is used instead of a database as a backend.

I have made use of the MembershipProvider many a times but I would like to share my experience in one of my project where I had to extend the MembershipProvider class to make use of an already existing web service of the client to validate user, create user, change password etc. In this blog I will show how to extend MembershipProvider class and write your custom MembershipProvider class.

Since I cannot reveal what webserivce was used in the website developed for the client I will create my own webservice to mimic the exact behavior as in the website. So lets start by creating a webservice which will have methods to create a user, validate user credentials and finally change the user’ password. Once the webservice is done we will extend the membership class. Below is the code for the webservice.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebServiceAuthentication : System.Web.Services.WebService
{
    string xmlFilePath = string.Empty;
    public WebServiceAuthentication()
    {
        xmlFilePath = Server.MapPath("data/users.xml");       
    }

    [WebMethod]
    public bool ChangePassword(string username, string newpassword)
    {
        XDocument users = XDocument.Load(xmlFilePath);
        var user = from u in users.Elements("user")
                   where u.Element("username").Value.ToLower() == username.ToLower()
                   select u;
        if (user != null && user.Count() > 0)
        {
            user.First().Element("password").Value = newpassword;
            return true;
        }

        return false;
    }

    [WebMethod]
    public bool ValidateUser(string userName, string passWord)
    {
        XDocument users = XDocument.Load(xmlFilePath);
        var user = from u in users.Element("users").Elements("user")
                   where u.Element("username").Value.ToLower() == userName.ToLower() && u.Element("password").Value == passWord
                   select u;
        if (user != null && user.Count() > 0)
            return true;
        return false;
    }

    [WebMethod]
    public Customer CreateUser(string username, string password, string emailID)
    {
        XDocument users = XDocument.Load(xmlFilePath);
        var user = from u in users.Elements("user")
                   where u.Attribute("email").Value.ToLower() == username.ToLower()
                   select u.Attribute("email").Value;
        if (user.Count() <= 0)
            throw new Exception("Email already in user");
        else
        {
            users.Element("users").Add(new XElement("user",
                new XAttribute("email", emailID),
                new XElement("userName", username),
                new XElement("password", password)));
            users.Save(xmlFilePath);
        }

        return new Customer { UserName = username };
    }

    [WebMethod]
    public string ResetPassword(string username)
    {
        string newPass = string.Empty;
        XDocument users = XDocument.Load(xmlFilePath);
        var user = from u in users.Elements("user")
                   where u.Element("username").Value.ToLower() == username.ToLower()
                   select u;
        if (user != null && user.Count() > 0)
        {
            newPass = Guid.NewGuid().ToString().Substring(0, 10);
            user.First().Element("password").Value = newPass;
        }
        else
            throw new Exception("User not found.");

        return newPass;
    }

    [WebMethod]
    public bool UpdateCustomer(Customer customerToBeUdpated)
    {       
        XDocument users = XDocument.Load(xmlFilePath);
        var userToBeUpdated = (from u in users.Elements("user")
                              where u.Attribute("email").Value.ToLower() == customerToBeUdpated.UserName.ToLower()
                              select u);
        if (userToBeUpdated != null && userToBeUpdated.Count() > 0)
        {
            XElement user = userToBeUpdated.First();
            user.Element("password").Value = customerToBeUdpated.Password;
            if (user.Element("firstname") != null)
                user.Element("firstname").Value = customerToBeUdpated.FirstName;
            else
                user.Add(new XElement("firstname", customerToBeUdpated.FirstName));
            if (user.Element("lastname") != null)
                user.Element("lastname").Value = customerToBeUdpated.LastName;
            else
                user.Add(new XElement("lastname", customerToBeUdpated.LastName));
            if (user.Element("address") != null)
            {
                user.Element("address").Element("street").Value = customerToBeUdpated.Address.Street;
                user.Element("address").Element("city").Value = customerToBeUdpated.Address.City;
                user.Element("address").Element("state").Value = customerToBeUdpated.Address.State;
                user.Element("address").Element("country").Value = customerToBeUdpated.Address.Country;
            }
            else
            {
                user.Add(new XElement("address", new XElement("street", customerToBeUdpated.Address.Street),
                    new XElement("city", customerToBeUdpated.Address.City),
                    new XElement("state", customerToBeUdpated.Address.State),
                    new XElement("country", customerToBeUdpated.Address.Country)));
            }

            return true;
        }

        return false;
    }
}

In the above web service we have methods to change the password (ChangePassword), to validate user credential based on user name and password (ValidateUser), method to create a user (CreateUser), method to change the password (ResetPassword) and finally method to update user details (UpdateCustomer). All these methods work on a XML file and make use of LINQ to XML to create user, change password etc. As mentioned before all the data is stored and retrieved from a XML file called Users.xml. If a new user has to be added or user details needs to be updated or password needs to be changed everything is done in the xml. The webservice makes use of a XML as a storage medium.The XML is pasted below.

<?xml version="1.0" encoding="utf-8" ?>
<users>
  <user email="a@a.com">
    <username>sandeep</username>
    <password>pass</password>
    <firstname>Sandeep</firstname>
    <lastname>P.R</lastname>
    <address>
      <street>Blah blah</street>
      <city>City</city>
      <state>State</state>
      <country>Country</country>
    </address>
  </user>
  <user email="sndppr@gmail.com">
    <username>sndppr@gmail.com</username>
    <password>pass</password>
    <firstname>Sandeep</firstname>
    <lastname>P.R</lastname>
    <address>
      <street>Blah blah</street>
      <city>City</city>
      <state>State</state>
      <country>Country</country>
    </address>
  </user>
</users>

The xml is pretty much straight forward. The XML stores the user name, password and other user related details like his address and email id. In the XML the password is saved as plain text which is not a good way of storing password. So while storing password in a physical file do use some sort of encryption.

Some of the methods of the web service return “Customer” as an object. The “Customer” class is also very straight forward. It derives from the “System.Web.Security.MembershipUser” class and adds some of its own properties. The “Customer” class code is pasted below.

public class Customer : System.Web.Security.MembershipUser
{
    public Customer()
    {

    }

    private string userName = string.Empty;

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string UserName { get; set; }
    public string EMail { get; set; }
    public string Password { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
}

That’s about web serivce and the XML file which form the backend, now lets try to extend System.Web.Security.MembershipProvider to work in conjunction with the above web service. Before that lets keep some things in mind. If you are not planning to make use of question and answer to reset the password then make sure that you return false from “RequiresQuestionAndAnswer” property. If you leave the default implementation then the system will throw “Sysmtem.NotImplementedException” with the following error message.

The method or operation is not implemented.

Now lets see the code where I have extended the System.Web.Security.MembershipProvider class. Some of the methods which have not been implemented have been removed for brevity.

public class CustomMembershipProvider : System.Web.Security.MembershipProvider
{
    public CustomMembershipProvider() : base()
    {
    }

    public override string ApplicationName
    {
        get
        {
            return "Authentication";
        }
        set
        {
            throw new NotImplementedException();
        }
    }
    //Non implemented methods have been removed for brevity.
    public override bool ChangePassword(string username, string oldPassword, string newPassword)
    {
        WebServiceAuthentication wsa = new WebServiceAuthentication();
        return wsa.ChangePassword(username, newPassword);
    }

    public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
    {
        MembershipUser cust = this.GetUser(username, false);       

        if (cust == null)
        {
            WebServiceAuthentication wsa = new WebServiceAuthentication();
            cust = wsa.CreateUser(username, password, email);
            status = cust != null ? MembershipCreateStatus.Success : MembershipCreateStatus.UserRejected;
        }
        else
            status = MembershipCreateStatus.DuplicateUserName;
        return cust;
    }

    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        WebServiceAuthentication wsa = new WebServiceAuthentication();
        MembershipUser mu = wsa.GetUser(username);
        return mu;
    }

    public override int MinRequiredNonAlphanumericCharacters
    {
        get { return 0; }
    }

    public override int MinRequiredPasswordLength
    {
        get { return 2; }
    }

    public override bool RequiresQuestionAndAnswer
    {
        get { return false; }
    }

    public override bool RequiresUniqueEmail
    {
        get { return false; }
    }
    //Non implemented methods have been removed for brevity

    public override void UpdateUser(MembershipUser user)
    {
        WebServiceAuthentication wsa = new WebServiceAuthentication();
        wsa.UpdateCustomer(user);
    }

    public override bool ValidateUser(string username, string password)
    {
        WebServiceAuthentication wsa = new WebServiceAuthentication();
        if (wsa.ValidateUser(username, password))
        {
            FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), true, string.Empty);
            HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat));
            HttpContext.Current.Request.Cookies.Add(authCookie);
            return true;
        }
        else
        {
            return false;
        }
    }
}

In above code I have extended the MembershipProvider class and overridden “ChangePassword”, “CreateUser”, “GetUser”, “UpdateUser” and “ValidateUser” methods. All these methods are pretty straight forward, the methods create an instance of our web service class and call the web methods to change password, create user etc. The before mentioned membership methods are called by the various login controls provided in ASP.NET. “ChangePassword” is called by the “ChangePassword” control when you click the “Change Password” button. “CreateUser” method of the MembershipProvider class is executed when you click the “Create User” button of “CreateUserWizard” control. “ValidateUser” method is called whenever the user request needs validation or when you click the “Log in” button of the “Log in” control.

Once you have extended the MembershipProvider class add the following tag in the web.config file of your website.

<membership defaultProvider="CustomProvider" >
      <providers>       
        <add name="CustomProvider" type="CustomMembershipProvider" />       
      </providers> 
</membership>

In the above markup the "defaultProvider" is an optional parameter. If not provided it will default to "AspNetSqlProvider", the default provider provided by Microsoft. The "type" attribute has the name of the extended membership provider class. In our case, since the class is placed in the "App_Code" folder, we have only specified the class name. If your membership provider class lies in some different dll and you have added it as a reference to your website then one has to specify the full name preceded by the namespace as well. Also using the “add” tag in “providers” tag you can set the membership properties like "connectionStringName", "enablePasswordRetrieval", "enablePasswordReset" etc in the. Sample tag is pasted below.

<connectionStrings>
    <add name="connStr" connectionString="Data Source=testserver;Initial Catalog=Northwind;Persist Security Info=True;User ID=sa;Password=sa"/>
</connectionStrings>
<!--Membership provider configuration with membership properties set in web.config. The connectionStringName should match with the one provided in you web.config.-->
<membership defaultProvider="CustomProvider" >
      <providers>       
        <add name="CustomProvider" type="CustomMembershipProvider" applicationName="TestApp" connectionStringName="connStr" enablePasswordRetrieval="false" enablePasswordReset="true" maxInvalidPasswordAttempts="5"  />       
      </providers> 
</membership>

With this done you can drag and drop the various ASP.NET login controls like the CreateUserWizard, ChangePassword, Login etc and you can create users, change password and login into the application without writing any code in the code behind files where these ASP.NET login controls are used. Isn’t it so easy to use the various ASP.NET login controls once you have extended the MembershipProvider class.

Try to know more

Sandeep