Tallan's Technology Blog

Tallan's Top Technologists Share Their Thoughts on Today's Technology Challenges

Modularizing .NET Training Part 2

Karl Schwirz

In our previous post we introduced the idea of sub-modularizing our training modules. In the course of developing for this blog posting our focus shifted to a bigger picture for this type of training.  Instead of presenting the differences between training in ASP.NET and MVC, we are now focused on proper architecture of an application, code reusability, and a focus on unit testing throughout our training.

Sub-modularizing our training modules allows for new trainees to think about how we can architect a layered application.  Building our current ASP.NET application, the trainee can know that their workflow and data access will be reused in the future.  This will allow him/her to develop with the flexibility required to reuse the function down the road.

A requirement of our current ASP.NET application builds a login screen which prompts the user for a User Name and Password.  Our current module calls for the development of the UI first then the workflow and data access second.  This is fine for single purpose pages, but in an application where many calls for user information will be made the developer might be tempted to create narrow data access methods to suite their current needs in the code behind instead of creating broader data access and workflow classes.

For example, in the button click event on a login page, a trainee may be tempted to use the following code:

 protected void login_Click(object sender, EventArgs e)
{
using (var connection = new SqlConnection("Data Source= localhost; Initial Catalog=BingoDB;User Id=BingoApp;Password= Uconn#1"))
{
connection.Open();
const string commandText = "select * from gameUser where UserName=@userName AND Password=@password;";
var command = new SqlCommand(commandText, connection);
command.Parameters.AddWithValue("@userName", userName);
command.Parameters.AddWithValue("@password", password);
var reader = command.ExecuteReader();
User user = reader.Read();
}
if (user.UserName != null && user.Password != null)
{
Session.Add("currentUser", user);
Response.Redirect("Home.aspx");
}
else
{
}
}

Although this code does work, next time the user is on a page and needs to perform data access the developer will have to create another method very similar to this setup, which doesn’t promote efficient programming.

Instead, since we’re going to be building our module to accommodate sub-modules, be it in ASP.NET web forms, MVC, etc. , we will have the developer create data access and workflow classes.  This enables the developer to complete other sub-modules as they will not only be able to reuse the code but will be able to truly see where the differences lie in the methodologies.  (i.e. web forms vs MVC)

Here we have an example of the data access being implemented,


public User GetUserByUsernamePassword(string userName, string password, string connectionString)

{

using (var connection = new SqlConnection(connectionString))

{

connection.Open();

var commandText = "select * from gameUser where UserName=@userName AND Password=@password;";

var command = new SqlCommand(commandText, connection);

command.Parameters.AddWithValue("@userName", userName);

command.Parameters.AddWithValue("@password",password);

var reader = command.ExecuteReader();

if(reader==null)

{

return null;

}

User returnUser = new User();

while (reader.Read())

{

returnUser.UserName = reader["UserName"].ToString();

returnUser.Password = reader["Password"].ToString();

returnUser.Email = reader["Email"].ToString();

}

return returnUser;

}

}

And the subsequent workflow which will use this data access:


public User  ValidateUser(string userName, string password, string connectionString)

{

User user = UserDAO.GetUserByUsernamePassword(userName, password, connectionString);

if (user == null)

{

return null;

}

return user ;

}

This is much better in terms of reusability as well as adding additional functionality.  In addition, now that we want to implement it in our projects we can see the true differences.

(ASP.NET)


<%@ Page Language="C#" MasterPageFile="~/masterPage.Master" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="Bingo.Login" Title="Tallan Bingo Login" %>


</pre>
<table class="style10">
<tbody>
<tr>
<td class="style12" colspan="2">
Please Enter Login Information</td>
</tr>
<tr>
<td class="style11" align="right" valign="top">


User Name:

Password:</td>
<td></td>
</tr>
</tbody>
</table>
<pre>

protected void login_Click(object sender, EventArgs e)
{
var wf = (UserWorkflow) ContextRegistry.GetContext().GetObject("UserWorkflow");
User user = wf.ValidateUser(userName.Text, password.Text);
if(user.UserName!=null && user.Password!=null)
{
Session.Add("currentUser", user);
Response.Redirect("Home.aspx");
}
}

Here we see that the code behind is calling the work flow which implements the data access methods.  The web form then executes all its own data form validation.

More importantly we can see the interactions between the web form and code behind.

(MVC)

Model

</pre>
<div id="_mcePaste">public class LogOnModel { [Required] [DisplayName("User name")] public string UserName { get; set; } [Required] [DataType(DataType.Password)] [DisplayName("Password")] public string Password { get; set; } [DisplayName("Remember me?")] public bool RememberMe { get; set; } }</div>
<pre>
View
</pre>
<pre>
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>



Log On




</pre>
<h2>Log On</h2>
<pre>
Please enter your username and password. <%: Html.ActionLink("Register", "Register") %> if you don't have an account.

<% using (Html.BeginForm()) { %>

<%: Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.") %></pre>
<div class="editor-label">
<%: Html.LabelFor(m => m.UserName) %></div>
<div class="editor-field">
<%: Html.TextBoxFor(m => m.UserName) %>

<%: Html.ValidationMessageFor(m => m.UserName) %></div>
<div class="editor-label">
<%: Html.LabelFor(m => m.Password) %></div>
<div class="editor-field">
<%: Html.PasswordFor(m => m.Password) %>

<%: Html.ValidationMessageFor(m => m.Password) %></div>
<div class="editor-label">
<%: Html.CheckBoxFor(m => m.RememberMe) %>

<%: Html.LabelFor(m => m.RememberMe) %></div>
<pre>
<input id="logOn" type="submit" value="Log On" />

<% } %>



Controller


public ActionResult LogOn()

{

return View();

}

[HttpPost]

public ActionResult LogOn(LogOnModel model, string returnUrl)

{

var wf = (UserWorkflow)ContextRegistry.GetContext().GetObject("UserWorkflow");

var user = wf.ValidateUser(model.UserName, model.Password);

Session["currentUser"] = user;

if (!String.IsNullOrEmpty(returnUrl))

{

return Redirect(returnUrl);

}

else

{

return RedirectToAction("Index", "Home");

}

}

Here we can see that the work flow is again utilized but in a different fashion.  The controller is doing all the leg work of retrieving the user data, somewhat like in the ASP.NET app.  While the view uses the model to construct and validate the form which the user fills out. Another advantage to modularization is in the realm of unit testing.  The trainee needs to be aware from the start of the module that unit testing is a very important part of proper application development.  Using this structure for training will help facilitate proper Unit Testing in that it will separate out the testing form the code behind, which is very difficult to test in. For example, we’ve seen trainees write the whole login stack just to test the data access functionality.  Whereas with proper unit testing practices we can avoid having the trainee start the project and debug the entire process to ensure one piece is working properly, such as login.  Instead, we can have them write the tests after the code and they can simply work until the unit test passes.  They will then know that the code will work properly and will not need to debug through this process.  They could write the following functional data access test:


public void GetUserByUsernamePasswordTest()

{</pre>
<blockquote>
UserDAO userDao = new UserDAO();

var username = "JDoe";

var password = "password";

var user = userDao.GetUserByUsernamePassword(username, password,connectionstring);

Assert.IsNotNull(user,"User should not be null");

Assert.AreEqual(user.UserName,username,"Usernames should be equal");

Assert.AreEqual(user.Password,password,"Passwords should be equal");

user = userDao.GetUserByUsernamePassword("Nouser", "nopassword",connectionstring);

Assert.IsNull(user,"User should be null when no match is found");</blockquote>
<pre>
}

Assuming that the database contains the user “JDoe” then the test will pass and the trainee can move on to testing the workflow. The workflow test may look something like this:


[TestMethod]

public void ValidateUserTest()

{

    MockRepository _mock = new MockRepositoy();

    IUserDAO mockDAO = _mock.Stub();

    var userWorkflow = new UserWorkflow();

    userWorkflow.UserDAO = mockDao;

    var userName = "JDoe";

    var password = "password";

    var expectedUser = new User();

    expectedUser.UserName = userName;

    expectedUser.Password = password;

    using (_mock.Record())

{

    Expect.Call(mockDao.GetUserByUsernamePassword(userName, password,connectionString)).Return(expectedUser);

}

    var result = userWorkflow.ValidateUser(userName, password);

    Assert.IsNotNull(result, "Result should not be null");

    Assert.AreEqual(result.UserName,userName, "");

    result = userWorkflow.ValidateUser("Tasdf", "asdf");

    Assert.IsNull(result,"");

}

With just these two examples between the architecture layers and unit testing facilitation we believe a point can be made for modularizing our training structure.  Training in this manner allows the developer to think about the architecture of the application as well as learn how to properly unit test an application.

 

No comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

\\\