Building N-Layered Applications With ASP - NET 4.5 PDF
Building N-Layered Applications With ASP - NET 4.5 PDF
public class PeopleRepository : Repository<Person>, IPeopleRepository
{
public IEnumerable<Person> FindByLastName(string lastName)
{
return DataContextFactory.GetDataContext().Set<Person>().Where(
x => x.LastName == lastName).ToList();
}
}
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 83 of 162
Your UI code (such as the MVC project) programs against the interface and thus sees this new method automatically,
ready to be used.
With the model and the repository layer almost done, there are a few small tasks that need to be completed. In the
next section you see how to let the model and EF layer automatically track the creation and modification dates of your
entities. In the final section you see how to improve the error messages that the ContactManagerContext raises to
make debugging easier.
Implementing a Unit of Work
If youve been looking at the code for the repository and the DbContext, you may be wondering how on earth you can
save changes to the DbContext. The repositories (such as PersonRepository) are hiding the fact that a DbSet is being
used under the hood. In addition, no SaveChanges method is defined on IRepository or any of the other repository
types. While you could add this method to the IRepository interface and implement it in Repository<T> for example
(and have it call return DataContextFactory.GetDataContext().SaveChanges(); ), there are better ways to
implement it.
With a unit of work, you typically want to execute one or more updates to the data context, and then save them to the
database at once. Heres an example:
With the implementation of a unit of work in the sample project, you can change this code to this:
At the end of the closing bracket }, SaveChanges is called automatically. So how does this work? You may remember
that a using block in .NET works on objects that implement IDisposable. Under the hood, .NET expands such a block
to a try / finally block and calls Dispose in the finally block. In order to make this code unit testable, you dont
want to hard code a Unit of Work that works with the Entity Framework only. Instead, the sample application has two
interfaces, both located in the Infrastructure project: the IUnitOfWorkFactory and an IUnitOfWork. The
IUnitOfWorkFactory and IUnitOfWork interfaces work together to determine the scope of a unit of work enabling you
send a batch of changes to the database as a single action. The Create method of the factory returns an instance of a
type that inherits IUnitOfWork. This interface in turn inherits IDisposable which forces inheriting types to implement
this method. In the EFUnitOfWork class the Dispose method is used to save the changes to the DbContext. The
IUnitOfWork interface also has Commit and Undo methods to explicitly commit changes to the database, or to get rid
of them. Heres the code for the two interfaces:
1
2
3
4
5
6
var repository = new PeopleRepository();
var person1 = CreatePerson();
repository.Add(person1);
var person2 = CreatePerson();
repository.Add(person2);
repository.SaveChanges(); // Doesn't work because SaveChanges doesn't exist.
1
2
3
4
5
6
7
8
using (new EFUnitOfWorkFactory().Create())
{
var repository = new PeopleRepository();
var person1 = CreatePerson();
repository.Add(person1);
var person2 = CreatePerson();
repository.Add(person2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Spaanjaars.Infrastructure
{
public interface IUnitOfWorkFactory
{
IUnitOfWork Create();
IUnitOfWork Create(bool forceNew);
}
public interface IUnitOfWork : IDisposable
{
void Commit(bool resetAfterCommit);
void Undo();
}
}
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 84 of 162
In the Repositories.EF project you find the following concrete implementations:
Notice how the Create method of the factory class forwards the value of the forceNew parameter to the constructor of
EFUnitOfWork. This value is then used to determine if the context should be cleared or not. Remember the
DataContextStorage I showed earlier? The Clear method ensures that the current DbContext is cleared, so a new
one is created the next time GetDataContext is called. I typically use the parameterless version of Create in
production code while I use the second overload in integration testing. By ensuring that any data context is cleared
when you run an integration test, you make sure that a problem caused in one test is not causing other tests to fail.
This in turn makes it easier to find issues in your tests and fix them.
To see how the changes are saved, take a look at the Dispose method:
When Dispose is called, SaveChanges is called on the same DbContext that the repository has been using.
To see how it all fits together, take another look at the same code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
namespace Spaanjaars.ContactManager45.Repositories.EF
{
public class EFUnitOfWorkFactory : IUnitOfWorkFactory
{
public IUnitOfWork Create()
{
return Create(false);
}
public IUnitOfWork Create(bool forceNew)
{
return new EFUnitOfWork(forceNew);
}
}
public class EFUnitOfWork : IUnitOfWork
{
public EFUnitOfWork(bool forceNewContext)
{
if (forceNewContext)
{
DataContextFactory.Clear();
}
}
public void Dispose()
{
DataContextFactory.GetDataContext().SaveChanges();
}
public void Commit(bool resetAfterCommit)
{
DataContextFactory.GetDataContext().SaveChanges();
if (resetAfterCommit)
{
DataContextFactory.Clear();
}
}
public void Undo()
{
DataContextFactory.Clear();
}
}
}
1
2
3
4
public void Dispose()
{
DataContextFactory.GetDataContext().SaveChanges();
}
1 using (new EFUnitOfWorkFactory().Create())
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 85 of 162
Because of the using block, .NET expands this to the following (pseudo code):
The IUnitOfWork interface also exposes a Commit method and an Undo method. Commit calls SaveChanges as well and
can be used if you want to explicitly save the changes without relying on the automatic save behavior of the unit of
work. You see this method at work in Part 9 of this series which discusses a command line import tool. Undo can be
used to undo any pending changes which can be useful if you change your mind about the data (for example, when an
entity is in an invalid state) while in a unit of work. Undo simply clears the DbContext from the container which means
a new DataContext instance is created the next time GetDataContext is called.
The code in the try block creates a new PeopleRepository and adds two people to them. The Add method uses
DataContextFactory.GetDataContext() under the hood to get a reference to the DbContext stored in HttpContext
or the current thread. When the finally block is hit, Dispose is called which gets a reference to the same DbContext
and then calls SaveChanges on it to send all the updates to the database.
Because the unit of work is based on interfaces, its easy to use them in unit testable environments. For example, you
could use them as follows in an MVC controller:
In this example, the controller receives an instance of IPeopleRepository and an instance of IUnitOfWorkFactory.
Both parameters to the constructor are based on interfaces so its easy to pass other types during unit testing. At
run-time, in production, a dependency framework injects concrete EF-based versions of the classes that access your
production database. You see how to enable dependency injection in the next article in this series.
2
3
4
5
6
7
8
{
var repository = new PeopleRepository();
var person1 = CreatePerson();
repository.Add(person1);
var person2 = CreatePerson();
repository.Add(person2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
var uow = new EFUnitOfWorkFactory().Create();
try
{
var repository = new PeopleRepository();
var person1 = CreatePerson();
repository.Add(person1);
var person2 = CreatePerson();
repository.Add(person2);
}
finally
{
uow.Dispose();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HomeController : Controller
{
private readonly IPeopleRepository _peopleRepository;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public HomeController(IPeopleRepository peopleRepository,
IUnitOfWorkFactory unitOfWorkFactory)
{
_peopleRepository = peopleRepository;
_unitOfWorkFactory = unitOfWorkFactory;
}
[HttpDelete]
public ActionResult Delete(int id)
{
using (_unitOfWorkFactory.Create())
{
_peopleRepository.Remove(id);
}
return RedirectToAction("Index");
}
}
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 86 of 162
In the Delete method, the call to Create on _unitOfWorkFactory returns an instance of a class that implements
IUnitOfWork. In a production application, that would be an EFUnitOfWork which persists the changes to the database
(deleting a Person based on the id parameter) by calling SaveChanges.
Just like other code from this article, this may seem like a lot of work to implement something simple as a call to
SaveChanges. However, once again, a lot of it is plumbing code so you only need to write it once. And once the code is
written, saving changes in a single step is now as simple as wrapping some code in a using block.
Using the model and repositories presented in this and the precious article makes your application a lot easier to
change. In the previous version of my N-Layer design, when you wanted to add a property to a class, you needed to
update the following:
Add the property to the dumb data object.
Modify the DB class and add support for the new property in the Save and FillDataRecord methods.
Modify multiple stored procedures to include the field in the various select and insert / update procedures.
Use the field in the UI.
With the new implementation, you can bring this down to two steps:
Add the property to the model class.
Use the new field in the UI.
Obviously you may still need to write some code to configure the property and its validation rules, but the fact you
don't have to mess with multiple stored procedures (or any data access code at all) is a big time saver.
Managing Relationships
In Part 4 of this series I mentioned a problem with the collections and removal of the contact details. I showed the
following example:
Now that the repositories are done, I can rewrite the code as follows:
Both these example have the same problem. When the changes are saved, any e-mail address that belonged to this
person is not deleted from the database. Instead, the OwnerId is set to null, effectively orphaning the e-mail address
record.
To work around this issue you need to tell EF what to do with these objects. The logic to determine which objects to
delete is relatively simple: e-mail addresses and phone numbers that no longer have an owner. Thats the reason I
implemented the IHasOwner interface which you can use for entities that are owned by other entities, and which
should be removed from the database whenever they are removed from the parent collection. Once youve found the
objects you want to delete, setting their State to EntityState.Deleted is then enough for EF to delete them from the
database. The best place to implement this code is in an override of SaveChanges on the ContactManagerContext
which is called by EF whenever it saves the changes. Heres the code from the sample application:
1
2
3
var person = myContext.People.First(x => x.Id = id);
person.EmailAddresses.Clear();
myContext.SaveChanges();
1
2
3
4
5
using (_unitOfWorkFactory.Create())
{
var person = _peopleRepository.FindById(id, x => x.EmailAddesses);
person.EmailAddresses.Clear();
}
1
2
3
4
5
6
7
8
9
10
public override int SaveChanges()
{
var orphanedObjects = ChangeTracker.Entries().Where(
e => (e.State == EntityState.Modified || e.State == EntityState.Added) &&
e.Entity is IHasOwner && e.Reference("Owner").CurrentValue == null);
foreach (var orphanedObject in orphanedObjects)
{
orphanedObject.State = EntityState.Deleted;
}
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 87 of 162
This code finds all entities that are either modified or added in the current session. It then tries to cast them to an
IHasOwner which will only work for EmailAddress and PhoneNumber instances. It also checks the reference called
Owner to see if its null (and thus an orphaned entity). I dont like the hardcoded string of Owner much, but since
the interface IHasOwner guarantees that the related entities have a property called Owner, I am OK with this magic
string. Finally, the code sets the State of all entities found to Deleted.
You can find the article I used to come up with this solution here: http://www.phdesign.com.au/programming/delete-
dependent-entities-when-removed-from-ef-collection/ while an alternative solution using a attribute can be found
here: http://wimpool.nl/blog/DotNet/extending-entity-framework-4-with-parentvalidator.
With these changes, the objects are now deleted from their associated tables in the database. For more details about
this solution, check out this blog post: http://www.phdesign.com.au/programming/delete-dependent-entities-
when-removed-from-ef-collection/.
Note that for this to work correctly, the related entities must be loaded into the Person instance. Entities that werent
loaded when you call Clear() wont be deleted from the database.
Implementing IDateTracking
As I mentioned earlier, a common requirement in a data driven application like the Contact Manager is to automatically
track when an entity was created and when it was last updated. In Part 4 you saw how I created the IDateTracking
interface to force entities to implement DateCreated and DateModified properties. While you could manually set
these properties, it would be much nicer if they were updated automatically for you.
Using EF, this is pretty simple to implement in the model and data layer, with a minimum amount of code. All you need
to do is override SaveChanges in the ContactManagerContext class, find all added and modified entities, update their
dates and then let EF save the changes to the database. Heres how the code in SaveChanges looks (in the sample
project its wrapped in a try block to improve error messages as youll see in the next section of this article):
This code uses the Entries collection of the built-in ChangeTracker to find all objects that have been added or
changed in the latest session (e.g. since the data context was created, or since SaveChanges was called last). The
code then loops over these entries and tries to cast them to the IDateTracking interface using the as keyword. If that
works, the class implements this interface and the two properties can be set. The code uses the entitys State
property to determine if its a new entity or an existing entity thats being modified. Only in the former case is the
DateCreated property set; in both cases the DateModified property is filled with the current date. If you dont like
this and prefer to have new entities that have an empty (null) DateModified, you can change the code in the
SaveChanges method and make the property nullable to accommodate for this.
With this change, all entities that implement this interface will now automatically track their creation and modification
11
12
13
... Other code here
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public override int SaveChanges()
{
... Other code here
var modified = ChangeTracker.Entries().Where(
e => e.State == EntityState.Modified || e.State == EntityState.Added);
foreach (DbEntityEntry item in modified)
{
var changedOrAddedItem = item.Entity as IDateTracking;
if (changedOrAddedItem!= null)
{
if (item.State == EntityState.Added)
{
changedOrAddedItem.DateCreated = DateTime.Now;
}
changedOrAddedItem.DateModified = DateTime.Now;
}
}
return base.SaveChanges();
}
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 88 of 162
dates. Figure 5-6 shows the People table in the database with the two new properties:
(6) See Links in this Document at the end for the full URL of this image.
Figure 5-6 The People table
Improving Error Messages Generated by the DbContext
Whenever an error occurs while saving entities, the Entity Framework throws a rather cryptic error message. For
example, when you save an entity that is missing a required field, you get an error like the following:
(7) See Links in this Document at the end for the full URL of this image.
Figure 5-7 The Standard EF Error Message
This message tells you nothing about the actual validation error, why validation failed, or which entity was invalid. With
a bit of code you can make the error message read as follows:
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 89 of 162
(8) See Links in this Document at the end for the full URL of this image.
Figure 5-8 Improved Error Messages
Notice how the error message now mentions the invalid entitys type, ID (if it exists) and the reason it was invalid.
This becomes even more useful if youre updating multiple entities at the same time with multiple validation errors:
(9) See Links in this Document at the end for the full URL of this image.
Figure 5-9 Multiple Error Messages
In order to get the more detailed information, I added the following code to the SaveChanges method in the
ContactManagerContext class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public override int SaveChanges()
{
try
{
// Other code here
return base.SaveChanges();
}
catch (DbEntityValidationException entityException)
{
var errors = entityException.EntityValidationErrors;
var result = new StringBuilder();
var allErrors = new List<ValidationResult>();
foreach (var error in errors)
{
foreach (var validationError in error.ValidationErrors)
{
result.AppendFormat("\r\n Entity of type {0} has validation error \"{1}\" for
property {2}.\r\n", error.Entry.Entity.GetType().ToString(),
validationError.ErrorMessage, validationError.PropertyName);
var domainEntity = error.Entry.Entity as DomainEntity<int>;
if (domainEntity != null)
{
result.Append(domainEntity.IsTransient() ? " This entity was added
in this session.\r\n" : string.Format("The Id of the entity is {0}.\r\n",
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 90 of 162
This code uses a try/catch block to catch an EF DbEntityValidationException. It then loops over the validation
errors, building up a StringBuilder with the error messages and type information. Notice how it uses the
IsTransient method of the DomainEntity base class to determine if the object is new or already existed in the
database. In the end, the code raises a new exception of type ModelValidationException, wraps the original
exception and provides the detailed error information as the ErrorMessage. The ModelValidationException class is a
custom Exception type defined in the Infrastructure project. Its mostly just a standard Exception but has a
constructor overload that accepts an IEnumerable<ValidationResult> and that exposes those again using a
read-only property. This property can be queried by calling code to find out what went wrong during a database
update. You see a concrete example of this in Part 6 of this series.
Note: Using error handling like this may expose more data than you want. Make sure you never show errors like this
on the production server using custom errors or a framework like Elmah. You could also modify this code so it only
runs on a developers machine, or in debug mode.
Stuff I Like to Do
Write integration tests for the Repository classes. Even though they are based on a generic base class that I
can test separately, its still useful to have test methods that ensure you can correctly retrieve data from the
database and send it back again.
Write integration tests that test the behavior of related entities. Setting up an EF model can be tricky especially
when multiple related entities are involved. By writing tests that assert that data gets deleted when you expect
it helps to ensure the correctness of your model and the interaction with the database.
Summary
This article covered a lot of ground. Youve seen how to build the data context (that inherits from DbContext) and how
to store it in a container appropriate for the type of application (web versus other types of applications). You also saw
how to implement validation rules using attributes, the fluent API, and using object level validation using the
IValidatableObject interface.
A fair part of this article was spent discussing repositories. You saw how to implement a generic Repository<T> that
can be used as a base class, without limiting you in the options to expand your specific repositories with more
specialized behavior. You also saw how to implement a Unit of Work to send a batch of changes to the database in one
fell swoop.
At the end you saw how to override the SaveChanges method of the DbContext to implement date tracking and
improve EFs error messages.
This is also the end of the part of this article series that deals with the model and repositories; i.e. the part of the
application you dont see but thats used a lot under the hood of the UI projects. In the following four parts you see
how to make use of this framework in an MVC 4 project, a Web Forms 4.5 project, a WCF service project and a
Console application.
Links in this Document
(1) http://nhforge.org/
(2) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05/Figure5-1_NuGet_Install_EF.png
(3) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05/Figure5-2_Row_Inserted.png
(4) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05
/Figure5-3_Class_Diagram_Context_Storage.png
(5) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05/Figure5-4_Error_Running_UnitTest.png
(6) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05/Figure5-6_People_Table_IDateTracking.png
(7) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05/Figure5-7_EF_Validation_Error.png
25
26
27
28
29
30
31
32
33
domainEntity.Id));
}
allErrors.Add(new ValidationResult(validationError.ErrorMessage, new[] {
validationError.PropertyName }));
}
}
throw new ModelValidationException(result.ToString(), entityException, allErrors);
}
}
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 91 of 162
(8) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05/Figure5-8_EF_Better_Validation_Error.png
(9) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part05/Figure5-9_EF_Multiple_Validation_Errors.png
ASP.NET N-Layered Applications - Implementing a Repository using EF Code First (Part 5)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 92 of 162
ASP.NET N-Layered Applications - Implementing an ASP.NET
MVC 4 Frontend (Part 6)
This is Part 6 in a series of 10 that show you how to build N-Layered applications using ASP.NET 4.5 and Entity
Framework 5 Code First. In this part youll see how to build a Web UI using ASP.NET MVC 4 and make use of the model
and repository projects I have shown in the past five articles.
Introduction
In the past couple of years, ASP.NET MVC has become a very popular framework for building web sites on the
Microsoft platform. The current version is ASP.NET MVC 4 and it ships with Visual Studio 2012 although you can also
download it separately for Visual Studio 2010.
This article is not meant as an introduction to ASP.NET MVC. If youre new to MVC, I recommend checking out the
following resources:
Getting Started with MVC (http://www.asp.net/mvc)
Intro to ASP.NET MVC 4 (http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/intro-
to-aspnet-mvc-4)
Pro ASP.NET MVC 4, Adam Freeman (Apress, 2013, http://www.amazon.com/Pro-ASP-NET-MVC-
Adam-Freeman/dp/1430242361/)
In this article, Ill show you how to use the repositories and model from an ASP.NET MVC 4 application. Ill cover the
following topics:
How to use Dependency Injection to determine the type of the repository at run-time.
How to use the validation functionality of the model in the UI.
How to use the Unit of Work pattern to save changes to the database as a batch.
In Part 2 of this article series you saw how to add the MVC 4 project to the solution. I used the Internet application
type as the starting point which gives you a bunch of controllers and views that define the global look and feel of the
site. To make it easy to see whats going on, I havent changed much in the original layout files that ship with a new
ASP.NET MVC 4 Razor project. I made a few minor changes to align the design with the one from the Web Forms
sample application but thats it. If youre familiar with the ASP.NET MVC 4 standard projects, youll feel right at home.
In Part 1, Ive provided a brief overview of the sample application and showed you a few screenshots. Heres a brief
recap of what the application can do. Remember: the purpose of this article series and this demo web site is to show
you how to design an application from an architecture point of view. The demo does not offer a complete feature set
as you would normally build in real-world web sites such as a fancy UI.
When you start up the application you see the home screen with a short welcome text. When you click the People
menu, you see all the contact people in the system with links to edit and delete them, and to manage their contact
data:
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 93 of 162
(1) See Links in this Document at the end for the full URL of this image.
Figure 6-1 The People List
Using the Edit link you can modify the details of an individual contact person, using the following page:
(2) See Links in this Document at the end for the full URL of this image.
Figure 6-2 Editing a Single Contact Person
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 94 of 162
When you click one of the address links (shown in Figure 6-1), you see a screen that lets you manage address details.
In this case, the user already pressed the Save button and the validation (from the Address class in the Model project)
kicked in:
(3) See Links in this Document at the end for the full URL of this image.
Figure 6-3 Validation at Work when Editing an Address
When you click the Email addresses or Phone numbers link (shwon in Figure 6-1) you see a list of associated contact
records for that user:
(4) See Links in this Document at the end for the full URL of this image.
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 95 of 162
Figure 6-4 Managing E-mail Addresses
From here, you can manage the existing addresses (view details, edit and delete) as well as create new addresses for
this contact person.
The About page provides more background on the application and the Contact page can be used to get in touch with
me. The Register and Login links at the top come from the standard MVC template. They are fully functional, but not
used in the sample application.
Under the hood, this MVC application uses the PeopleRepository targeting the Entity Framework for all data access.
The list of people is retrieved using FindAll, a details page uses FindById, and the insert pages use Add, and so on.
To see how it all fits together, heres the architecture diagram showing the MVC frontend and how its related to the
other components in the system:
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 96 of 162
(5) See Links in this Document at the end for the full URL of this image.
Figure 6-5 The N-Layer Architecture Diagram
To make your MVC controllers unit testable, its important that none of them have direct ties with the Entity
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 97 of 162
Framework implementation of the PeopleRepository. Direct access from controllers to EF would mean that you need
a real database when running the tests which makes things slower and harder to test. The Controller classes in the
project should exchange model entities from the Model project through an abstraction (the IPeopleRepository
interface) instead, as you see how to do in the next section.
Making your Controllers Testable
To see why you want to work with an abstraction rather than a concrete implementation of the PeopleRepository,
consider this (fictitious) action method in the PeopleController:
This method instantiates a PeopleRepository, and then retrieves a sub set of the contact people based on the sorting
and paging data. A unit test for this method could look like this:
At first glance, this may look perfectly reasonable. The code creates a new controller, executes the List method and
then asserts that the total number of records is 10 (the default page size for the system) and that the first person in
the list (the youngest contact person in the system because the code sorted on DateOfBirth in descending order), is
born on December 1st, 2007.
If you ran this test, it might just work. However, it only works under special conditions: you must have a least 10
contact people in your database, and the youngest person must be born on December 1st, 2007. While you could
certainly insert default data (using the Seed method of the database initializer, using Red Gates Data Generator or in
the constructor or method body of the unit test), this is asking for trouble, What if you have other unit tests that test
changing the date of birth or deleting all contact people from the system? If those tests were run before
ListSortsAndPagesCorrectly, things would surely break.
On top of that, you dont really care about testing the behavior of the PeopleRepository as youre doing that
elsewhere already (in the Unit and Integration tests projects). What you care about here is the List method: given an
IPeopleRepository, you want this method to call FindAll, and then apply the proper sorting and paging settings.
The solution to this problem is to provide a fake repository that provides temporary data. You could have only one for
your entire test project, or you could have many, each serving a distinct purpose. Heres how you could write such an
IPeopleRepository:
1
2
3
4
5
6
public ActionResult List(int page = 1, string sort = "Id", string sortDir = "ASC")
{
IQueryable<Person> allPeople = new PeopleRepository().FindAll().OrderBy(
BuildOrderBy(sort, sortDir)).Skip((page * pageSize) - pageSize).Take(pageSize);
return View(allPeople);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[TestMethod]
public void ListSortsAndPagesCorrectly()
{
// Arrange
var controller = new PeopleController();
// Act
var result = controller.List(1, "DateOfBirth", "DESC") as ViewResult;
// Assert
IEnumerable<Person> modelData = ((IEnumerable<Person>)result.Model).ToList();
modelData.Count().Should().Be(10);
modelData.First().DateOfBirth.Year.Should().Be((2007));
modelData.First().DateOfBirth.Month.Should().Be((12));
modelData.First().DateOfBirth.Day.Should().Be((1));
}
1
2
3
4
5
6
7
8
internal class FakePeopleRepository : IPeopleRepository
{
public IQueryable<Person> FindAll(
params System.Linq.Expressions.Expression<Func<Person, object>>[] includeProperties)
{
var temp = new List<Person>();
var youngestPerson = new DateTime(2007, 12, 1);
for (int i = 0; i < 23; i++)
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 98 of 162
This fake repository returns a list of 24 people when you call the FindAll method. The youngest person in the list is
born on December 1st, 2007; all the other people are slightly older. All the other methods are not implemented as I
dont care about them for this particular test. For other tests, I could implement these methods, or create another fake
repository where I only implement the methods used by my test code.
Using this fake PeopleRepository, its now much easier to test the List method. And even better: you only test the
List method. You no longer have a dependency on the real EF PeopleRepository, nor do you require a database with
the correct data in it. This frees you from a lot of unnecessary dependencies, so you can let your test method focus on
one thing, and one thing only: the code inside the List method of your MVC controller.
In order for the List method to use this repository, you need a way to pass it to the controller. Ill show you a quick
and dirty way first (referred to as poor mans dependency injection) so you understand the core principles. In a later
section you see a better solution by using a dependency injection framework that resolves all dependencies for you
automatically.
As I mentioned earlier, the controller needs access to the FakeRepository so you need to find a way to pass it in.
Passing it to the List method wont work as the MVC runtime wouldnt know how to supply one. You could instantiate
one directly in your code inside the action method (just as I did previously with the EF PeopleRepository) but that
doesnt make the situation any better. How would you know when to supply which one? For this to work, you would
need a way to differentiate between a regular run-time, and the unit testing run-time which really isnt the direction
you want to take things.
Fortunately, there is a much easier way: simply pass the required dependencies through an overloaded constructor of
the controller. In the default constructor (the parameterless version that gets called by the MVC framework) you then
instantiate an EF PeopleRepository directly. Something like this would do the trick:
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
temp.Add(new Person { FirstName = i.ToString(), LastName = i.ToString(),
DateOfBirth = youngestPerson.AddDays(-i) });
}
temp.Insert(11, new Person { FirstName = "Youngest", LastName = "Youngest Lastname",
DateOfBirth = youngestPerson });
return temp.AsQueryable();
}
public void Add(Person entity)
{
throw new NotImplementedException();
}
public void Remove(Person entity)
{
throw new NotImplementedException();
}
// Other methods (none of them implemented) go here
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class PeopleController : Controller
{
private readonly IPeopleRepository _peopleRepository;
public PeopleController(IPeopleRepository peopleRepository)
{
_peopleRepository = peopleRepository;
}
public PeopleController() : this(new PeopleRepository())
{
}
public ActionResult List(int page = 1, string sort = "Id", string sortDir = "ASC")
{
IQueryable<Person> allPeople = _peopleRepository.FindAll().OrderBy(BuildOrderBy(
sort, sortDir)).Skip((page * pageSize) - pageSize).Take(pageSize);
return View(allPeople);
}
}
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 99 of 162
The parameterless version ensures everything keeps working like normal. When a URL like /People/List is requested,
the constructor sends a new instance of PeopleRepository into the overloaded constructor which stores that object in
the _peopleRepository variable. Then, when List is called, it uses that variable to call the FindAll method which
eventually queries the SQL Server database using the Entity Framework.
For your unit test you can now use the overloaded constructor, and pass in your fake repository like this:
Now the controller receives an instance of your FakeRepository. Since you can control the objects returned from
FindAll method in the fake repository, its easy to set up the correct asserts for things like the data being returned.
This effectively decouples your unit test (and your MVC controller) from the database dependency. This in turn makes
life a lot simpler, leading to easier unit testing, and minimizing the chances that your tests fail for the wrong reasons.
In the sample application you see this implemented in a very similar way. Rather than List, the action method is
called Index, and its accompanying test method is called IndexSortsAndPagesCorrectly inside the
PeopleControllerTests class. The implementation differs in that the controllers constructor expects another
dependency: an IUnitOfWorkFactory. Since thats not used for this test, the test method simply passes null:
Youll see more of the IUnitOfWorkFactory dependency later.
While the solution with the parameterless constructor that forwards an instance of the EF PeopleRepository is nice,
theres an even better way: you can tell an external framework to provide the concrete instances at runtime for you.
You see how this works in the next section.
Injecting Repositories using StructureMap
Manually modifying your constructors to support dependency injection is quite labor intensive, and prone to errors.
Every time you add another dependency, you should not forget to update the parameterless constructor. In addition,
since youre testing another constructor than the one that MVC calls, theres still a slim chance of bugs in your system
that go unnoticed in your tests. Fortunately, theres a solution to the problem: a Dependency Injection (DI) framework.
ASP.NET MVC has been designed with testability in mind and as such its pretty easy to plug a DI framework into the
runtime. This DI framework then ensures that when a controller has dependencies (such as the IPeopleRepository in
the controllers constructor), they are automatically instantiated. And even cooler, if the dependency itself has another
dependency, the framework will ensure proper initialization of that dependency as well. So in the sample application,
whenever a controller is instantiated, it automatically receives concrete instances of the IPeopleRepository and
IUnitOfWork interfaces. You see how the framework can determine which concrete type to instantiate in a later
section.
Many different DI frameworks exist, including Unity, Castle Windsor, StructureMap, Ninject, and AutoFac. For a
deep-dive into DI, and the different DI frameworks, check out:
Dependency Injection in .NET (Mark Seemann, Manning, 2011)
(6)
http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-dependency-injection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[TestMethod]
public void ListSortsAndPagesCorrectly()
{
// Arrange
var controller = new PeopleController(new FakePeopleRepository());
// Act
var result = controller.List(1, "DateOfBirth", "DESC") as ViewResult;
// Assert
IEnumerable<Person> modelData = ((IEnumerable<Person>)result.Model).ToList();
modelData.Count().Should().Be(10);
modelData.First().DateOfBirth.Year.Should().Be((2007));
modelData.First().DateOfBirth.Month.Should().Be((12));
modelData.First().DateOfBirth.Day.Should().Be((1));
}
1 var controller = new PeopleController(new FakePeopleRepository(), null);
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 100 of 162
In my sample application, I am using StructureMap
(7)
as the DI container. I chose it as I find it pretty much
hassle-free to work with. However, I could have easily chosen a different framework as the DI requirements for the
sample application can be fulfilled by all of the major DI frameworks.
To add and configure StructureMap in an MVC app, you need to do the following:
Add the StructureMap.MVC4 package to your MVC project using NuGet. This also brings in the dependent
StructureMap package.
1.
Write some code to tell the DI component how to resolve types like IPeopleRepository into their concrete
counterparts.
2.
Adding the StructureMap.MVC4 Package using NuGet
To add StructureMap to your MVC project, follow these steps:
Open up the Package Manager Console by choosing Tools | Library Package Manager | Package Manager
Console
1.
Select your MVC project from the Default project drop-down. 2.
At the command, type Install-Package StructureMap.Mvc4 and hit enter. 3.
This package makes the following modifications:
In App_Start it adds StructuremapMvc.cs which is a class that initializes the StructureMap framework and
then registers itself as the dependency resolver for the MVC framework.
1.
It adds the folder DependencyResolution with three code files with code needed to setup StructureMap. Of
those three, you only need to modify IoC.cs for your application.
2.
Write Code to Tell the DI Component how to Resolve Types
Whenever the DI container needs to resolve a dependency it needs to understand what to do. For example, when an
IPeopleRepository is needed, the container needs to know it should return an instance of the PeopleRepository
from the EF project. The exact type to be returned is configurable to give you the greatest flexibility. To see how this
works, look in IoC.cs in the DependencyResolution folder that has been added to your project. You should see the
following Initialize method:
Although the code looks a bit cryptic with all the lambdas, its relatively straightforward. The call to
TheCallingAssembly tells the framework to scan the assembly that is making the call (which by default is the MVC
project to which you added StructureMap). The call to WithDefaultConventions means: whenever an ISomething is
requested, try to return a Something (that should then implement the ISomething interface). In other words, it tries
to find a concrete type named after the interface by dropping the I from the interface name.
This is a good setup if you have all your code inside the MVC project and if all your classes and interfaces follow the
default conventions. In the sample application, the concrete types are not located in the MVC assembly, but rather in
the one from the Repositories.EF project. You could include that assembly specifically, or for maximum flexibility you
can call AssembliesFromApplicationBaseDirectory as youll see shortly. Note that IUnitOfWorkFactory is not
implemented by a class called UnitOfWorkFactory, but rather by one called EFUnitOfWorkFactory. You can use the
For method to explicitly link base types and interfaces to concrete types. My final Initialize method now looks like
this:
1
2
3
4
5
6
7
8
9
10
11
12
13
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
// x.For<IExample>().Use<Example>();
});
return ObjectFactory.Container;
}
1
2
3
public static IContainer Initialize()
{
ObjectFactory.Initialize(scanner =>
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 101 of 162
For more information about the scanning process and conventions, check out the documentation for StructureMap:
http://docs.structuremap.net/ScanningAssemblies.htm.
With this code in place, I can now simplify the code of the PeopleController so it only has a single constructor:
Now, whenever a new instance of the PeopleController is needed, StructureMap kicks in and supplies concrete
instances of the PeopleRepository and UnitOfWorkFactory.
Note: rather than directly inheriting Controller, this class inherits BaseController which in turn inherits the MVC
Controller class. By using a centralized base class for your controllers its easy to add behavior that applies to all
controllers in your project.
With the dependencies taken care of, the next step is implementing the methods inside the PeopleController as well
as the implementation of the views. Again, since this is not an article series about MVC, I wont dig into each and
every method or code sample. Youre encouraged to download and inspect the source for this article series so you get
a better understanding of what it does and how.
Building the PeopleController and its Views
In this section youll see how I implemented the PeopleController with methods to list, display, create, edit, and
delete contact people. The other controllers follow similar patterns, although the exact implementation differs here and
there.
Index / List
In this section, Ill show you different versions of an Index method, each one adding more functionality. At the end, Ill
show the final version that is used in the PeopleController of the sample application. The final action method
supports a pageable and sortable display of contact people.
If all you want to show on your List page are all items of a specific type such as people (usually shown using the Index
method), implementing the action method would be extremely simple. All you would need is something like this:
4
5
6
7
8
9
10
11
12
13
14
{
scanner.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.WithDefaultConventions();
});
scanner.For<IUnitOfWorkFactory>().Use<EFUnitOfWorkFactory>();
});
return ObjectFactory.Container;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PeopleController : BaseController
{
private readonly IPeopleRepository _peopleRepository;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
const int pageSize = 10;
public PeopleController(IPeopleRepository peopleRepository, IUnitOfWorkFactory unitOfWorkFactory)
{
_peopleRepository = peopleRepository;
_unitOfWorkFactory = unitOfWorkFactory;
}
public ActionResult Index(int page = 1, string sort = "Id", string sortDir = "ASC")
{
// Implementation goes here; uses _peopleRepository
}
}
1
2
3
4
public ActionResult Index()
{
return View(_peopleRepository.FindAll());
}
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 102 of 162
This would work fine if you only have a few records to show, and if you dont care about sorting the data. A simple
view like the following would suffice:
However, this controller and view have a few issues:
The view is using the Person entity from the Model project directly, While this works, I typically try to stay
away from it as I dont want my views to know about the actual model; I could be exposing too much data to
the view, or the view could be making changes to the entity if it was in an updateable state. Instead, I prefer
to create a View Model that has only the fields I want to expose in my view, and then use a mapping
framework to automatically convert my entities (such as a Person) into the corresponding View Model (such
as a DisplayPerson, dedicated to displaying contact people).
1.
There is no support for paging; if your database table contains hundreds or thousands or more records, the
list becomes really hard to manage and the page will load much slower.
2.
There is no way to sort data. 3.
To see where View Models are used, consider this architecture diagram that shows the four frontend applications:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@model IEnumerable<Spaanjaars.ContactManager45.Model.Person>
@{
ViewBag.Title = "Index";
}
<h2>@ViewBag.Title</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
@Html.DisplayNameFor(model => model.FullName)
</th>
<th>
@Html.DisplayNameFor(model => model.DateOfBirth)
</th>
<th></th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.FullName)
</td>
<td>
@Html.DisplayFor(modelItem => item.DateOfBirth)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
@Html.ActionLink("Details", "Details", new { id = item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</table>
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 103 of 162
(8) See Links in this Document at the end for the full URL of this image.
Figure 6-6 View Models in the Architecture Diagram
In the MVC project, View Models are used to pass data from the controllers to the views and vice versa. This means
that a view never has to know of the existence of one of the Model types; it only needs to know of the View Model
types. In the WCF project, View Models are used to exchange data between the service methods and the external
system that call the service methods so there is never a direct dependency on the Model types and the external
applications. The other two frontend applications dont use View Models although you could introduce them there as
well if you have the need.
You see how to implement View Models in MVC next; the WCF View Models are discussed in Part 8.
Creating a View Model
To determine how a View Model for a specific type needs to look, you need to think about the data you want to expose,
and what the View Model is used for. Although you could create one View Model per entity, I prefer to create multiple
View Models per entity to support different models for different actions. For example, a View Model used to display a
Person has different requirements than a View Model used to create or edit a Person.
When displaying a Person, you most likely want to display the FullName and the DateOfBirth in a list. For a details
page, you may also want to display things like the type, the creation and modification dates and more. In a more
complex application, you could decide to create two separate models for this. In the sample application, I chose to
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 104 of 162
combine them in single View Model called DisplayPerson:
Notice how I left out the two collections (EmailAddresses and PhoneNumbers) and the two Address properties as you
typically dont display that information in a list or details page for a Person.
To convert a Person (or even a collection of Person instances) into a DisplayPerson you could write a lot of manual
code that instantiates a new DisplayPerson and then copies over the property values one by one. However, this is
quite a bit of work, and its likely to break when you add new properties to the Person type or the DisplayPerson.
Instead, I prefer to use a framework called AutoMapper
(9)
. Ill show the code for the view that displays instances of
the DisplayPerson View Model later.
Mapping with AutoMapper
To see what problem AutoMapper solves, imagine you have an entity type called Car, like this:
If you want to display cars in a list, its corresponding View Model could look like this:
This model is designed to just hold the cars ID and name. Inside your controller you could map a Car to a
CarViewModel like this:
This is pretty straightforward code. A Car instance is retrieved from a repository, a new instance of CarViewModel is
created, the values for the members exposed by CarViewModel are copied from the Car instance and finally the View
Model is fed to the view. In this example, things are relatively simple because CarViewModel has only two properties,
but this gets messy pretty quickly when the View Model has many members. With AutoMapper, this can be changed to
this:
1
2
3
4
5
6
7
8
9
10
11
public class DisplayPerson
{
public int Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
public DateTime DateOfBirth { get; set; }
public PersonType Type { get; set; }
}
1
2
3
4
5
6
7
8
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public int NumberOfDoors { get; set; }
public EngineType { get; set; }
// Lots of other properties here
}
1
2
3
4
5
public class CarViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
1
2
3
4
5
Car car = _carRepository.FindById(id);
CarViewModel carViewModel = new CarViewModel();
carViewModel.Id = car.Id;
carViewModel.Name = car.Name;
return View(carViewModel);
1
2
3
Car car = _carRepository.FindById(id);
CarViewModel carViewModel = new CarViewModel();
Mapper.Map(car, carViewModel);
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 105 of 162
In this example, you save only one line of code. However, even with complex types with many members, this is all you
need, saving you from lots of typing overhead and greatly reducing the risk of errors during the conversion.
AutoMapper is able to automatically map members of two types if they have the same name and type. If your View
Model uses different names, or if you want to exclude members from one type or the other, or if you want to change
how data is mapped, you can configure AutoMapper using its API. This is code you need to write once for each type
and then AutoMapper will use that configuration every time it needs to map a type. To see how this works, you need to
add AutoMapper to your project first. Once again, a NuGet project is available to make this dead-simple:
Open up the Package Manager Console window. In the Default project drop-down select the MVC project.
At the command prompt, type Install-Package AutoMapper and hit enter.
For the sample application I also created a code file called AutoMapperConfig inside the App_Start folder and
added the following code to map from a Person to a DisplayPerson (as per the code shown earlier):
Since all fields of DisplayPerson also exist in Person, this is all I need to add for AutoMapper to figure out the
mapping rules. Youll see a more extensive example later when editing a contact person is discussed.
Notice the call to AssertConfigurationIsValid. This triggers AutoMapper to validate all mappings, and throw an
error when it finds unmatched members or other errors in your mappings. This helps to catch errors as soon as
possible.
Finally, I added the following line of code in Global.asax:
With this code, whenever the application starts, AutoMapper is initialized; all mappings are created and then validated.
From then on, all you need is a call to Map to have AutoMapper create the mapping.
With AutoMapper setup in the sample project I could now rewrite my Action method like this:
Just as in the previous example, I get all contact people from the database using FindAll method. Whats different is
that I now use AutoMapper to map from a Person instance to a DisplayPerson instance. And not only that,
AutoMapper also understands how to deal with collections. So, even though the people variable is actually an
IQueryable<Person>, AutoMapper knows how to convert that into a List<DisplayPerson> which is eventually passed
to the view.
For more information on AutoMapper, check out the following resources:
4 return View(carViewModel);
1
2
3
4
5
6
7
8
9
10
11
public static class AutoMapperConfig
{
public static void Start()
{
Mapper.CreateMap<Person, DisplayPerson>();
// Other mappings go here
Mapper.AssertConfigurationIsValid();
}
}
1
2
3
4
5
protected void Application_Start()
{
...
AutoMapperConfig.Start();
}
1
2
3
4
5
6
7
public ActionResult Index()
{
var people = _peopleRepository.FindAll();
var model = new List<DisplayPerson>();
Mapper.Map(people, model);
return View(model);
}
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 106 of 162
https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
https://github.com/AutoMapper/AutoMapper/wiki
http://automapper.org/
Before I show you the code that would be needed for the view, lets make a few more changes to the Index method to
support paging and sorting, as this also effects the View Model.
Sorting Data
LINQ to EF has great support for sorting. Just call OrderBy and OrderByDescending to order a collection based on a
lambda expression, For example, this code sorts all people on their birth date:
What LINQ to EF, or actually, LINQ in general is not good at is sorting based on strings. Quite often, in a UI driven
application, the sort order comes from a string entered by the user (for example, by clicking the header of a grid).
Ideally, you would like to apply sorting as follows:
Out of the box, this wont work as there is no overload of OrderBy that accepts a string. However, Microsoft
developed and released the DynamicQuery library that lets you perform string based ordering, searching and more.
And it probably comes as no surprise: its available as a NuGet package
Note: if youre familiar with SQL Injection a method to attack an application through holes in the way user input is
injected into SQL statements then dont worry. Under the hood, the sort string is parsed into an expression tree
which is eventually converted to the proper SQL statements in the same way as would occur with the lambda-based
versions of OrderBy and OrderByDescending. In other words, this does not open up your application to SQL Injection
attacks.
To install the DynamicQuery package, execute the following command in the Package Manager Console window:
Once the package is installed, you need the following using statement to bring the extension methods for sorting into
scope:
Now you can sort the list of contact people by passing in the proper sort and sortDir parameters as query string
values. You can find out more about this package at: http://nuget.org/packages/DynamicQuery.
Youll see the complete implementation of the Index method with sorting after I discussed paging.
Paging Data
Paging is another important feature for a web application. Trying to display hundreds or thousands or more records at
the same time will surely annoy your users and slow down your application. Fortunately, paging is easy using LINQs
Skip and Take methods.
Combining sorting and paging could give you the following Index method:
1 var sortedPeopleList = _peopleRepository.FindAll().OrderBy(x => x.DateOfBirth);
1
2
3
4
5
6
public ActionResult Index(string sort = "Id", string sortDir = "ASC")
{
var sortedPeopleList = _peopleRepository
.FindAll().OrderBy(string.Format("{0} {1}", sort, sortDir));
// Other code here
}
1 Install-Package DynamicQuery
1 using System.Linq.Dynamic;
1
2
3
4
5
6
public ActionResult Index(int page = 1, string sort = "Id", string sortDir = "ASC")
{
var allPeople = _peopleRepository.FindAll()
.OrderBy(string.Format("{0} {1}", sort, sortDir)).Skip((page * PageSize) - PageSize)
.Take(PageSize);
var model = new List<DisplayPerson>();
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 107 of 162
You can now pass in the requested page number (one-based) in a query string parameter called page. So, for
example, calling something like /People?page=2&sort=Id&sortDir=DESC would give you the second page of the
people list, with the records sorted in descending order based on their ID.
The Skip method skips ((page * PageSize) - PageSize) number of records. For page 1 with a page size of 10, that
would mean 0 (1x10 10 = 0). For the second page, it would skip 10 rows, giving you row 11 to 20, and so on. The
Take method then takes the requested number of items, defined by the PageSize constant. In a real world application,
you probably want to make the PageSize configurable, or even user-selectable, but for the sample the hardcoded
value works fine.
The final Index action method in the sample application looks slightly different. Heres its full code:
There are a few twists in this version. First, a count of all records is retrieved using Count(). This returns the total
number of people in the system, which is used in the view to build up the proper paging links. Next, the model is
different. Rather than just a list of DisplayPerson instances, the View Model now also contains a page number (the
currently displayed page), a page size, and the total number of rows. Heres how the PagerModel looks:
Notice that I made it generic, which means you can use it for other View Model types that need to support paging as
well.
The final piece of the puzzle is the view that displays the data. In the sample application, I chose to use the MVC
WebGrid as it ships with MVC. However, there are many other alternatives such as:
jQuery UI (http://jqueryui.com/)
Kendo UI (http://www.kendoui.com/ from Telerik)
DXTREME (http://www.devexpress.com/Products/HTML-JS/ from Dev Express)
The WebGrid code looks like this:
7
8
9
Mapper.Map(allPeople, model);
return View(model);
}
1
2
3
4
5
6
7
8
9
10
11
12
public ActionResult Index(int page = 1, string sort = "Id", string sortDir = "ASC")
{
int totalRecords = _peopleRepository.FindAll().Count();
var data = new List<DisplayPerson>();
IQueryable<Person> allPeople = _peopleRepository.FindAll()
.OrderBy(BuildOrderBy(sort, sortDir)).Skip((page * PageSize) - PageSize)
.Take(PageSize);
Mapper.Map(allPeople, data);
var model = new PagerModel<DisplayPerson>
{ Data = data, PageNumber = page, PageSize = PageSize, TotalRows = totalRecords };
return View(model);
}
1
2
3
4
5
6
7
public class PagerModel<T> where T: class
{
public IEnumerable<T> Data { get; set; }
public int PageSize { get; set; }
public int PageNumber {get; set;}
public int TotalRows {get; set;}
}
1
2
3
4
5
6
7
8
9
10
11
var grid = new WebGrid(null, defaultSort: "FirstName", columnNames:
new[] { "Id", "FullName", "DateOfBirth", "Type" }, rowsPerPage: Model.PageSize);
grid.Bind(Model.Data, rowCount: Model.TotalRows, autoSortAndPage: false);
@grid.GetHtml(columns: grid.Columns(
grid.Column("Id"),
grid.Column(header: "Full name", columnName: "FullName", format:
(item) => Html.ActionLink(((string)item.FullName), "Details", new { item.id })),
grid.Column("DateOfBirth", header: "Date of Birth", format:
(item) => item.DateOfBirth.ToString("d")),
grid.Column("Type", canSort: false),
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 108 of 162
Notice how I am passing the PageSize (from the View Model) to the constructor, and the actual data and number of
rows to the Bind method. The grid then uses that information to display the appropriate number of rows, as well as
paging links below the grid. The remainder of the code sets up the various columns for the persons ID, full name, date
of birth, and a bunch of links to edit addresses, e-mail addresses and phone numbers. This results in the following
screen:
(10) See Links in this Document at the end for the full URL of this image.
Figure 6-7 The List with Contact People
Most of the links in this screenshot are discussed in the remainder of this article.
If you run the sample application, youll notice you can click various headers to perform sorting. Most of them are
straightforward and sort directly on the property associated with the column. The exception is the FullName column.
Since that property only exists in the Model and is not present as a column in the database, sorting will fail. The
BuildOrderBy method in the PeopleController takes care of this as follows:
When the sortOn parameter equals fullname (which is what the WebGrid sends to the server when you click the Full
name column), sorting is done by combining the first name and the last name of the person. Obviously you can modify
this any way you want.
For a lot more background information on the MVC WebGrid, check out the following links:
http://msdn.microsoft.com/en-us/magazine/hh288075.aspx
http://weblogs.asp.net/andrebaltieri/archive/2010/11/01/asp-net-mvc-3-working-with-webgrid.aspx
http://weblogs.asp.net/andrebaltieri/archive/2010/11/02/asp-net-mvc-3-working-with-webgrid-part-2.aspx
12
13
14
15
16
17
18
19
20
21
22
23
grid.Column(header: "Addresses", format: item => new HtmlString(
Html.ActionLink("Home", "Edit", "Addresses", new { personId = item.Id,
contactType = (int)ContactType.Personal }, null).ToString() + " | " +
Html.ActionLink("Work", "Edit", "Addresses", new { personId = item.Id,
contactType = (int)ContactType.Business }, null).ToString())
),
grid.Column(format: (item) => Html.ActionLink("E-mail addresses", "List",
"EmailAddresses", new { personId = item.id }, null)),
grid.Column(format: (item) => Html.ActionLink("Phone numbers", "List", "PhoneNumbers",
new { personId = item.id }, null)),
grid.Column(format: (item) => Html.ActionLink("Edit", "Edit", new { item.id })),
grid.Column(format: (item) => Html.ActionLink("Delete", "Delete", new { item.id }))
1
2
3
4
5
6
7
8
private string BuildOrderBy(string sortOn, string sortDirection)
{
if (sortOn.ToLower() == "fullname")
{
return String.Format("FirstName {0}, LastName {0}", sortDirection);
}
return string.Format("{0} {1}", sortOn, sortDirection);
}
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 109 of 162
http://www.campusmvp.net/blog/webgrid-in-mvc-step-by-step-part-two
With the list of people done, its time to take a look at the Details action method.
Details
This method is executed when someone clicks a persons name in the grid. This requests /People/Details/personId
where personId is the ID of a person in the database. The action method is really simple, and it looks like this:
The method receives an ID of the person on the database which it uses to query the contact person from the
_peopleRepository using the FindById method. When that method returns null, the code returns an HttpNotFound
ActionResult which eventually results in a 404 Not Found error message in the client.
If the person does exist, its mapped to a DisplayPerson using AutoMapper and sent to the view where its displayed
using this code:
Because the Model type is a DisplayPerson, you can use all the properties of that class in the view. At the bottom
theres a simple action link that takes the user back to the list of people.
Create
In MVC, a Create action method usually has two versions: one that renders an empty form, and one that processes
the form after the user sends it back to the server. First, take a look at the action method that renders the view:
Action methods dont get any simpler than this. The view then looks as follows:
1
2
3
4
5
6
7
8
9
10
11
public ActionResult Details(int id)
{
Person person = _peopleRepository.FindById(id);
if (person == null)
{
return HttpNotFound();
}
var data = new DisplayPerson();
Mapper.Map(person, data);
return View(data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@model Spaanjaars.ContactManager45.Web.Mvc.Models.DisplayPerson
@{
ViewBag.Title = "Details";
}
<h2>@ViewBag.Title</h2>
<fieldset>
<legend>Person</legend>
<div class="display-label">
@Html.DisplayNameFor(model => model.FirstName)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.FirstName)
</div>
... Other fields go here
</fieldset>
<p>
@Html.ActionLink("Back to List", "Index")
</p>
1
2
3
4
public ActionResult Create()
{
return View();
}
1
2
3
@using Spaanjaars.ContactManager45.Web.Mvc.Helpers
@model Spaanjaars.ContactManager45.Web.Mvc.Models.CreateAndEditPerson
@{
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 110 of 162
All of this is standard MVC code so hopefully this looks familiar. Notice how the Model type in the view is set to
CreateAndEditPerson which I created specifically in the Models folder of the MVC project to create new and edit
existing people. Just as with the DisplayPerson, the code leaves out the complex properties of Person as they are
handled elsewhere:
Note: in the sample solution, the CreateAndEditPerson View Model implements the IValidatableObject interface to
enable complex validation capabilities. You see how this works later.
Notice how the properties have data annotation attributes applied to them. The DisplayName attribute influences the
name in the UI, so labels such as First name are displayed rather than FirstName. Youll see the required attributes
in the section on validation.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
ViewBag.Title = "Create new contact person";
}
<h2>@ViewBag.Title</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
... Other fields go here
<div class="editor-label">
<label>Type</label>
@Html.ValidationMessageFor(model => model.Type)
</div>
<div class="editor-field">
@Html.EnumDropDownListFor(model => model.Type)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CreateAndEditPerson : IValidatableObject
{
public int Id { get; set; }
[Required, DisplayName("First name")]
public string FirstName { get; set; }
[Required, DisplayName("Last name")]
public string LastName { get; set; }
[DisplayName("Date of birth")]
public DateTime DateOfBirth { get; set; }
public PersonType Type { get; set; }
// Validation code here
}
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 111 of 162
Just as with DisplayPerson, you need a mapping in AutoMapper, but this time in both directions. You need to map
from CreateAndEditPerson to a Person in the Create action method when the form is submitted to the server.
Mapping in the opposite direction is done by the Edit method as youll see in a bit.
Your first attempt at the mapping code might look like this:
However, if you were to run this, AssertConfigurationIsValid would signal the following exception:
(11) See Links in this Document at the end for the full URL of this image.
Figure 6-8 An AutoMapper Exception Pointing out Issues with the Mapping Configuration
What this error message tells you is that the source type (CreateAndEditPerson) does not supply a value for the six
properties listed. In this case, this is exactly what you want. The two date properties are set by EF automatically when
saving the entity, and the four contact details are managed elsewhere. You can let AutoMapper know youre fine with
this using the Ignore method, like this:
Notice how the Ignore method is used to indicate you really want these properties ignored. You dont have to specify
these for the reverse mapping because CreateAndEditPerson doesnt have any properties that Person doesnt have.
With all of this set up, handling the Create action method is now pretty straight forward:
1
2
Mapper.CreateMap<CreateAndEditPerson, Person>();
Mapper.CreateMap<Person, CreateAndEditPerson>();
1
2
3
4
5
6
7
Mapper.CreateMap<CreateAndEditPerson, Person>()
.ForMember(d => d.DateCreated, t => t.Ignore())
.ForMember(d => d.DateModified, t => t.Ignore())
.ForMember(d => d.EmailAddresses, t => t.Ignore())
.ForMember(d => d.PhoneNumbers, t => t.Ignore())
.ForMember(d => d.HomeAddress, t => t.Ignore())
.ForMember(d => d.WorkAddress, t => t.Ignore());
1
2
3
4
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateAndEditPerson createAndEditPerson)
{
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 112 of 162
Based on the Model type of the Create view, the model binder in ASP.NET MVC constructs a CreateAndEditPerson
instance for you and fills it with the data from the form. If the object is considered valid (e.g. the Required attributes
and other validation rules in that View Model are satisfied), the model is mapped to a Person from the Model project
and added to the data context using the repositorys Add method. Since the code is wrapped in a using block that
creates a unit of work, the changes are saved automatically at the end of the code block.
If updating the database fails, the data context throws a ModelValidationException with the validation errors in its
ValidationErrors property. You saw how the data context created and filled the exception in Part 5 that discussed
ways to improve the error messages from the data context. Here, I am using those messages to load them in the
ModelState so they nicely show up in the UI, even though they are raised somewhere deep down in the EF data layer.
With Create covered, Edit is now pretty easy as it follows the same pattern.
Edit
Edit also consists of two action methods: one for GET and one for POST. Heres the GET version:
Hopefully, this is easy code by now. A Person is retrieved from the database using the PeopleRepository. Its then
mapped into a CreateAndEditPerson View Model and sent off to the view. That view is almost the same as the one for
create, except for the field for the ID of the person being edited. Rather than creating two separate, but almost
identical views, you can use partial views and extract the part of the views that is the same to a separate view file and
reference that from the Create and Edit views. This is the route I have taken for the e-mail addresses and phone
numbers (and which the tooling in Visual Studio creates by default), so check out those views if you want to learn
more.
On postback, the following Edit method to save the contact person in the database is executed:
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if (ModelState.IsValid)
{
try
{
using (_unitOfWorkFactory.Create())
{
Person person = new Person();
Mapper.Map(createAndEditPerson, person);
_peopleRepository.Add(person);
return RedirectToAction("Index");
}
}
catch (ModelValidationException mvex)
{
foreach (var error in mvex.ValidationErrors)
{
ModelState.AddModelError(
error.MemberNames.FirstOrDefault() ?? "", error.ErrorMessage);
}
}
}
return View();
}
1
2
3
4
5
6
7
8
9
10
11
public ActionResult Edit(int id)
{
Person person = _peopleRepository.FindById(id);
if (person == null)
{
return HttpNotFound();
}
var data = new CreateAndEditPerson();
Mapper.Map(person, data);
return View(data);
}
1
2
3
4
5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CreateAndEditPerson createAndEditPerson)
{
if (ModelState.IsValid)
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 113 of 162
This is almost the same code as the POST version of the Create method. Here, an existing person is retrieved from
the database and then updated with the values from the submitted View Model. In this case, I had to explicitly pass
the source and target types to the Map method. The reason for this is that although the personToUpdate looks like a
plain Person instance, its actually a class derived from it. EF has created this so-called dynamic-proxy type to enable
features such as lazy loading. Without the explicitly defined types, AutoMapper will complain there is no mapping
between the dynamically created proxy and the CreateAndEditPerson class. More info about this can be found here:
http://stackoverflow.com/questions/13416816/how-to-update-an-existing-entity-from-viewmodel-using-automapper-
and-ef4-dbconte
The final action method in the PersonController is Delete, discussed next.
Delete
Delete also consists of a GET and a POST version. Heres the code for both:
In the GET version, I retrieve the person from the database and convert it into a DisplayPerson. This is useful if you
want to show a confirmation screen that lists the details of the entity youre about to delete.
In the POST version, the ID of the person is submitted which is then fed into the Remove method of the
PeopleRepository. This deletes the person from the database, as well as its related contact data such as phone
numbers and email records. The changes are saved automatically as the code is wrapped in a using block that creates
a unit of work.
Handling Validation
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
try
{
using (_unitOfWorkFactory.Create())
{
Person personToUpdate = _peopleRepository.FindById(createAndEditPerson.Id);
Mapper.Map(createAndEditPerson, personToUpdate, typeof(CreateAndEditPerson),
typeof(Person));
return RedirectToAction("Index");
}
}
catch (ModelValidationException mvex)
{
foreach (var error in mvex.ValidationErrors)
{
ModelState.AddModelError(error.MemberNames.FirstOrDefault() ?? "", error.ErrorMessage);
}
}
}
return View();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public ActionResult Delete(int id)
{
Person person = _peopleRepository.FindById(id);
if (person == null)
{
return HttpNotFound();
}
var data = new DisplayPerson();
Mapper.Map(person, data);
return View(data);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
using (_unitOfWorkFactory.Create())
{
_peopleRepository.Remove(id);
}
return RedirectToAction("Index");
}
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 114 of 162
Although youve already seen some validation at work, I want to briefly list a few important implementation details.
First, take a look at the full code for the CreateAndEditPerson class:
Depending on how you look at things, there may be some stuff you dont like. For example, the Validate method
seems like a replication of a part of the Validate method in the Person class (and it is.) Also, the Required attributes
seem duplicated between Person and its CreateAndEditPerson View Model. This is a consequence of the separation
using the View Models; if you were using a Person directly, you could directly access its Validate method. In my
opinion, the benefits of using separate View Models outweigh any issues associated with it, so I dont mind duplicating
some of the validation. In fact, it turns out that sometimes I have different validation rules in my view model than in
my model. For example, when working with a legacy database, the model could have relaxed validation to let old data
go in and out without a problem. However, new data entered through an ASP.NET MVC UI needs to stick to stricter
rules. This is easily accomplished with a separate View Model for Create and Edit, each with their own validation rules.
Note: the validation at the model level is carried out as well when the entities are saved to the database. This means
that View Models never jeopardize the quality of your data; your model always stays in control and has a final say
about the data.
In the EditAddress view model that is used to edit Address instances (there is only an Edit model as you cant create
new addresses in the UI), I avoided duplication of validation code by leveraging the Validate method of the
associated Address class, like this:
This way, you dont need to duplicate any code; you just ask Address if it can make a valid instance out of the fields
passed to it, and use that to validate the EditAddress instance. I left both concepts in the sample application as I find
theres room for both options.
Youre encouraged to look at the other controllers in the application, along with their View Models and views. Although
the implementation is slightly different here and there, a lot of the code is based on the same principles that youve
just seen.
The final topic that needs to be discussed is how the application is able to display nice drop-down lists based on the
various enums in the application. Youll see how this is done next.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CreateAndEditPerson : IValidatableObject
{
public int Id { get; set; }
[Required, DisplayName("First name")]
public string FirstName { get; set; }
[Required, DisplayName("Last name")]
public string LastName { get; set; }
[DisplayName("Date of birth")]
public DateTime DateOfBirth { get; set; }
public PersonType Type { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Type == PersonType.None)
{
yield return new ValidationResult("PersonType can't be None", new[] { "Type" });
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class EditAddress : IValidatableObject
{
public int PersonId { get; set; }
public string Street { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public ContactType { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return new Address(Street, City, ZipCode, Country, ContactType).Validate();
}
}
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 115 of 162
Implementing Drop Down Lists
You may have noticed that when you add a contact person, you get a nice drop-down to select a type:
Figure 6-9 A Drop-Down List with Values from an Enum
Neither ASP.NET nor MVC have built in support for converting enums to a drop down list. However, its quite easy to
implement.
When you look at the Create or Edit view for a Person, you see something like this:
A nice and clean implementation using an extension method, as the EnumDropDownListFor method is able to infer the
underlying enum type to build the drop down, and use the models value to preselect the current value if it exists.
The implementation of EnumDropDownListFor is based on a few articles I found on the Internet, where the following
one looked like the most complete and stable implementation:
http://www.jonegerton.com/dotnet/dropdownlist-helper-for-enums/. For more background, and a way to localize these
drop-downs, check out these resources:
http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-
for-enums.aspx
http://stackoverflow.com/questions/4656758/mvc3-razor-dropdownlistfor-enums/4
http://ruijarimba.wordpress.com/2012/02/17/asp-net-mvc-creating-localized-dropdownlists-for-enums/
In my implementation I added the SplitString method (and renamed it to PascalCaseToSpaces) as suggested by
Nick in the blog post from Stuart Leeks to change PascalCasedEnumItems to a version with spaces (Pascal Cased
Enum Items) in the drop-down list.
Stuff I Like to Do
Implement a BaseController. Although certainly not required, I find it useful if all my controllers inherit a
common and custom base controller (called BaseController in the sample project). This makes it easy at any
stage of the application to add behavior to all controllers at once.
Call Mapper.AssertConfigurationIsValid at the end of your AutoMapper configuration code to ensure all
mappings are valid. Without that call, you code appears to run fine, but you may run into problems later when
the faulty mapping is used in your code.
Give your View Models logical names so you can easily find them. I typically use a naming convention like
action followed by the type (such as ListPerson to display people in a list) or something like DisplayType,
CreateType, EditType or CreateAndEditType, to indicate what they are used for.
Although not shown in this project, its easy (and recommended) to use UI frameworks like jQuery UI or Kendo
UI. These frameworks make it easy to create good-looking UIs with little effort.
Summary
In this article you saw how to use the model and repositories developed in earlier parts in the series for an ASP.NET
MVC 4 application. In particular, you saw how to use a dependency framework to inject concrete instances of the
IPeopleRepository and IUnitOfWork interfaces into the controllers of the project. A good part of the article was then
spent discussing the implementation of the PeopleRepository and its methods to implement CRUD (Create, Read,
Update and Delete) operations. At the end of the article, you saw how validation is handled and how enums are used
to create better looking drop-down lists via extension methods.
In the next article youll see how to implement the same frontend UI, this time using ASP.NET 4.5 Web Forms.
1 @Html.EnumDropDownListFor(model => model.Type)
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 116 of 162
Links in this Document
(1) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-1_MVC_People_list.png
(2) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-2_MVC_Edit_Person.png
(3) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-3_MVC_Edit_Address.png
(4) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-4_MVC_EmailAddresses.png
(5) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-5_Model_Architecture.png
(6) http://www.manning.com/seemann/
(7) http://docs.structuremap.net/
(8) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-6_Architecture_Diagram.png
(9) http://automapper.org/
(10) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-7_People_list.png
(11) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part06/Figure6-8_AutoMapper_Exception.png
ASP.NET N-Layered Applications - Implementing an ASP.NET MVC 4 Frontend (Part 6)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 117 of 162
ASP.NET N-Layered Applications - Implementing a Web Forms
4.5 Frontend (Part 7)
This is Part 7 in a series of 10 that show you how to build N-Layered applications using ASP.NET 4.5 and Entity
Framework 5 Code First. In this part youll see how to build a Web UI using ASP.NET Web Forms and make use of the
model and repository projects I have shown in the first five articles in this series.
Introduction
ASP.NET Web Forms has been around for a long time (since early 2002), and its the framework of choice for a large
number of developers around the world. Partially modeled after desktop applications, ASP.NET Web Forms applications
are event driven and use a post back model where each (or at least most) pages post back to themselves.
In this article, Ill show you how to use the repository and models created in the earlier parts of this series in an
ASP.NET Web Forms application. Ill cover the following topics:
How to use the new Model Binding capabilities of ASP.NET 4.5 to access a repository.
How to use the validation functionality of the model in the UI.
How to use the Unit of Work pattern to save changes to the database as a batch.
How to centralize creation of the repository and unit of work instances as an alternative to dependency
injection you saw in Part 6.
This article is not meant as an introduction to ASP.NET Web Forms. If youre new to Web Forms, I recommend checking
out the following resources:
Getting Started with Web Forms (http://www.asp.net/web-forms/overview/getting-started)
My book Beginning ASP.NET 4.5 in C# and VB (Wrox, 2012) - http://www.amazon.com/Beginning-ASP-NET-
4-5-C-VB/dp/1118311809/
Professional ASP.NET 4.5 in C# and VB (Gaylord et. al. Wrox, 2013) - http://www.amazon.com/Professional-
ASP-NET-4-5-C-VB/dp/1118311825/
In Part 2 of this article series you saw how to add the Web Forms project to the solution. I used the ASP.NET Web
Forms Application template as the starting point which gives you a bunch of pages, a master file and some content
files that define the global look and feel of the site. To make it easy to see whats going on, I havent changed much in
the original layout files that ship with a new ASP.NET Web Forms project. I made a few minor changes to align the
design with the one from the ASP.NET MVC sample application, but thats it. If youre familiar with the ASP.NET Web
Forms standard project, youll feel right at home.
In Part 1, Ive provided a brief overview of the sample application and showed you a few screenshots. Heres a brief
recap of what the application can do. Remember: the purpose of this article series and this demo web site is to show
you how to design an application from an architecture point of view. The demo does not feature a complete feature set
(such as a fancy UI) as you would normally build in real-world web sites.
NOTE: these screenshots are similar to the ones from the article about ASP.NET MVC. I implemented the exact same
functionality in both applications so you can see how to make use of the repositories in each application type.
When you start up the application you see the home screen with a short welcome text. When you click the People
menu, you see all the contact people in the system with links to edit and delete them, and to manage their contact
data:
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 118 of 162
(1) See Links in this Document at the end for the full URL of this image.
Figure 7-1 The Web Forms Site Showing all Contact People
When you click Edit for a contact person, you see the following page:
(2) See Links in this Document at the end for the full URL of this image.
Figure 7-2 Editing a Single Contact Person
When you click one of the address links on the main People page (shown in Figure 7-1), you see a screen that lets you
manage address details. In this case, the user already pressed the Save button and the validation (from the Address
class in the Model project) kicked in:
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 119 of 162
(3) See Links in this Document at the end for the full URL of this image.
Figure 7-3 Validation at Work when Editing an Address
When you click the Email addresses or Phone numbers link in the main list of People (shown in Figure 7-1), you see a
list of associated contact records for that person:
(4) See Links in this Document at the end for the full URL of this image.
Figure 7-4 Managing E-mail Addresses
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 120 of 162
From here, you can manage the existing data (edit and delete) as well as create new e-mail addresses for this user.
The About page provides more background on the application and the Contact page can be used to get in touch with
me. The Register and Login links at the top come from the standard Web Forms template. They are fully functional,
but not used in the sample application.
Under the hood, this Web Forms application uses the PeopleRepository targeting the Entity Framework for all data
access. The list of people is retrieved using FindAll, a details page uses FindById, the insert pages use Add, and so
on. To see how it all fits together, heres the architecture diagram showing the Web Forms frontend application and
how its related to the other components in the system:
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 121 of 162
(5) See Links in this Document at the end for the full URL of this image.
Figure 7-5 The Architecture Diagram
Since its pretty difficult today to unit test your ASP.NET Web Forms pages, I wont attempt to decouple the Entity
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 122 of 162
Framework based versions of the PeopleRepository and UnitOfWork and the Web Forms. However, I will centralize
the creation of these classes using a factory method so you only have one place where these classes are instantiated.
This makes it easy to use another repository implementation if you choose so.
Ill use the new Model Binding capabilities introduced in ASP.NET 4.5 for all my data access as youll see in the next
section.
Building the Pages to Manage Contact People and Related Data
If youve worked with model binding in ASP.NET 4.5 Web Forms before, the following sections will look very familiar to
you. If youre new to model binding, be sure to check out the following resources first:
http://blogs.msdn.com/b/webdev/archive/2013/03/28/tutorial-series-on-model-binding-with-asp-net-web-
forms.aspx
http://www.codeguru.com/csharp/.net/net_asp/controls/using-model-binding-in-asp.net-data-controls.htm
With model binding, you dont use the traditional data source controls to get data from a database and hand it over to
a data-bound control. Instead, you create one or more methods to handle CRUD operations in your code behind, and
then link your data-bound control to these methods using properties of the control. Heres the markup of a rather
simplified example that displays people in a GridView and lets you edit them:
Notice how the GridView and DetailsView together have four methods configured to support all CRUD operations:
SelectMethod, UpdateMethod, DeleteMethod and InsertMethod.
These four methods are configured in the code behind and could look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<asp:GridView ID="GridView1" runat="server" DataKeyNames="Id"
ItemType="Spaanjaars.ContactManager45.Model.Person" SelectMethod="FindAll"
UpdateMethod="UpdatePerson" DeleteMethod="DeletePerson">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
</Columns>
</asp:GridView>
<asp:DetailsView ID="DetailsView1" runat="server"
ItemType="Spaanjaars.ContactManager45.Model.Person" SelectMethod="FindAll"
InsertMethod="InsertPerson" DefaultMode="Insert">
<Fields>
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public IEnumerable<Person> FindAll()
{
return RepositoryHelpers.GetPeopleRepository().FindAll();
}
public void InsertPerson()
{
var person = new Person();
TryUpdateModel(person);
using (RepositoryHelpers.GetUnitOfWorkFactory().Create())
{
RepositoryHelpers.GetPeopleRepository().Add(person);
}
}
public void UpdatePerson(int id)
{
using (RepositoryHelpers.GetUnitOfWorkFactory().Create())
{
var person = RepositoryHelpers.GetPeopleRepository().FindById(id);
TryUpdateModel(person);
}
}
public void DeletePerson(int id)
{
using (RepositoryHelpers.GetUnitOfWorkFactory().Create())
{
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 123 of 162
Each method works with the PeopleRepositiory (retrieved from the RepositoryHelpers class which youll see
shortly) to get data from the database, and send changes back.
Note: this code serves only as an example; its missing some real-world validation and exception handling (although
the model still takes care of the validation before entities are saved to the database) so you shouldnt use this as-is.
Instead, read on to see how to improve the situation with only a minimum amount of code.
The Web Forms project contains a People folder that has web forms to manage contact people and their associated
contact details. Figure 7-6 shows the Solution Explorer for the project:
Figure 7-6 Solution Explorer for the Web Forms project
Ill discuss the files in this folder in the next few sections.
List
As you saw at the beginning of the article, the applications main page has a list of all the contact people in the
system. The list is shown using the following code (inside /People/Default.aspx):
29
30
31
RepositoryHelpers.GetPeopleRepository().Remove(id);
}
}
1
2
3
<asp:GridView ID="categoriesGrid" runat="server" AutoGenerateColumns="false"
AllowSorting="true" AllowPaging="true" PageSize="10"
ItemType="Spaanjaars.ContactManager45.Model.Person"
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 124 of 162
There are a few important details in this code. First, the GridView is set up to get its data using ListPeople in its
SelectMethod attribute. Likewise, DeletePerson will be called whenever a contact person needs to be deleted.
Furthermore, the GridView is set up to use sorting and paging by the attributes AllowSorting and AllowPaging.
Since the SelectMethod returns an IQueryable<T>, the control is able to figure how to do (efficient) paging and
sorting, by executing the correct Skip, Take and OrderBy methods for you.
Note the attribute ItemType that is set to Spaanjaars.ContactManager45.Model.Person. This attribute is also new in
ASP.NET 4.5 and enables you to strongly type your GridView to a specific model type. This allows you to do cool stuff
like this:
Notice how this code refers to Item.Id to append the ID of the contact person to the navigation URL. Youll get full
IntelliSense support because Item in this example is a real Person type. This makes it a lot easier to configure
your controls and make fewer mistakes. Item has BindItem as its counterpart for insert and update behavior as youll
see later.
In the code behind, you find the ListPeople method that looks like this:
This code retrieves a PeopleRepository from the RepositoryHelpers class. It then calls FindAll followed by a call to
OrderBy to do an initial sort of the data. This is required because Entity Framework doesnt support calling Skip and
Take (which is what the GridView will call to support paging) on an unsorted set of data. The RepositoryHelpers is
another poor mens implementation of Dependency Injection and its code looks like this:
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
SelectMethod="ListPeople" DeleteMethod="DeletePerson" GridLines="None"
CellSpacing="5" EmptyDataText="No contact people found.">
<Columns>
<asp:BoundField DataField="Id" HeaderText="ID" SortExpression="Id" />
<asp:HyperLinkField DataNavigateUrlFields="Id" HeaderText="Full name"
SortExpression="FirstName" DataNavigateUrlFormatString="Details.aspx?Id={0}"
DataTextField="FullName"></asp:HyperLinkField>
<asp:BoundField DataField="DateOfBirth" HeaderText="Date of Birth"
SortExpression="DateOfBirth" DataFormatString="{0:d}" />
<asp:BoundField DataField="Type" HeaderText="Type" SortExpression="Type" />
<asp:TemplateField HeaderText="Addresses">
<ItemTemplate>
<asp:HyperLink runat="server" NavigateUrl='
<%# string.Format("EditAddress.aspx?Id={0}&ContactType=2", Item.Id) %>' Text="Home">
</asp:HyperLink>
|
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl='
<%# string.Format("EditAddress.aspx?Id={0}&ContactType=1", Item.Id) %>' Text="Work">
</asp:HyperLink>
</ItemTemplate>
</asp:TemplateField>
<asp:HyperLinkField DataNavigateUrlFields="Id" HeaderText=""
DataNavigateUrlFormatString="EmailAddresses.aspx?Id={0}" Text="E-mail addresses">
</asp:HyperLinkField>
<asp:HyperLinkField DataNavigateUrlFields="Id" HeaderText=""
DataNavigateUrlFormatString="PhoneNumbers.aspx?Id={0}" Text="Phone numbers">
</asp:HyperLinkField>
<asp:HyperLinkField DataNavigateUrlFields="Id" HeaderText=""
DataNavigateUrlFormatString="AddEditPerson.aspx?Id={0}" Text="Edit">
</asp:HyperLinkField>
<asp:CommandField ShowDeleteButton="True" />
</Columns>
</asp:GridView>
1
2
3
<asp:HyperLink runat="server" NavigateUrl='
<%# string.Format("EditAddress.aspx?Id={0}&ContactType=2", Item.Id) %>' Text="Home">
</asp:HyperLink>
1
2
3
4
5
public IQueryable<Person> ListPeople()
{
var repo = RepositoryHelpers.GetPeopleRepository();
return repo.FindAll().OrderBy(x => x.Id);
}
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 125 of 162
I wrote this class to abstract the creation of instances of the IPeopleRepository and IUnitOfWorkFactory interfaces.
The calling code (my ASPX pages) doesnt know of the existence of concrete implementations like PeopleRepository
in the EF project. All it needs to understand is the IPeopleRepository abstraction. This makes it easy to swap your EF
PeopleRepository for another one (using NHibernate or some other data access technology): just reference the new
type, change the type returned from the two methods inside the RepositoryHelpers class, and youre done.
Since deleting entities is done in the same page, Ill discuss that next.
Delete
For the GridView to support deleting using your repositories, all you need is a DeleteMethod in the code behind and a
CommandField with its ShowDeleteButton property set to true. Then when you click the Delete link for an entity in the
GridView, this code in the Code Behind is executed:
The GridView automatically passes the persons ID to the method so deleting the entity is as simple as creating an
instance of UnitOfWork, and then calling Remove on the PersonRepository.
If you look at the code for the GridView you see that the Full Name column links to a Details page. This page is
discussed next.
Details
The frontend part of Details.aspx is pretty simple. I used an old skool <table> element to present some details of
the person. Heres an example of the row that shows the last name:
In code behind, these controls are given a value with this code:
1
2
3
4
5
6
7
8
9
10
11
12
public static class RepositoryHelpers
{
public static IPeopleRepository GetPeopleRepository()
{
return new PeopleRepository();
}
public static IUnitOfWorkFactory GetUnitOfWorkFactory()
{
return new EFUnitOfWorkFactory();
}
}
1
2
3
4
5
6
7
public void DeletePerson(int id)
{
using (RepositoryHelpers.GetUnitOfWorkFactory().Create())
{
RepositoryHelpers.GetPeopleRepository().Remove(id);
}
}
1
2
3
4
5
6
<tr>
<td>Last name</td>
<td>
<asp:Label ID="LastName" runat="server"></asp:Label>
</td>
</tr>
1
2
3
4
5
6
7
8
9
10
11
12
public partial class Details : System.Web.UI.Page
{
private int _personId;
protected void Page_Load(object sender, EventArgs e)
{
string personIdAsString = Request.QueryString.Get("Id");
if (string.IsNullOrEmpty(personIdAsString) || !int.TryParse(personIdAsString, out _personId))
{
Response.Redirect("~/");
}
LoadPerson();
}
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 126 of 162
The code in Page_Load ensures there is an ID for a person in the Query String and that it can be converted to an
integer. If either condition fails, the user is sent back to the root of the site.
In the LoadPerson method, the code accesses the RepositoryHelpers to get a repository and then uses the familiar
FindById method to find the requested person. If the person is found, its properties are used to populate the various
controls on the page.
Theres no action for the situation where the person is not found, but its easy to add. Depending on your
requirements, you could redirect to the list of people page, show an error page, or simply tell the user you cant find
the requested person by updating a Label or so. In the last case, you also want to send out a HTTP 404 (not found)
error code to tell search engines to stop indexing the page:
With the list and details pages done, the next step is creating and editing contact people.
Create, Edit and Building DropDownList Controls from Enums
To create contact people, I chose a different implementation than for the contact records. For the contact people, I
chose a concept I refer to as hand coding data access. It comes down to a bunch of regular input controls (text
boxes, drop down lists etc.) whose values are used to fill an instance of, in this case, a Person. I prefer this solution
over data-bound controls such as the DetailsView for complex pages. Rather than messing with the various insert
and edit templates, you can have a single page that handles both. In the case of the contact manager, the page
AddEditPerson.aspx looks like this:
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void LoadPerson()
{
var peopleRepository = RepositoryHelpers.GetPeopleRepository();
var person = peopleRepository.FindById(_personId);
if (person != null)
{
FirstName.Text = person.FirstName;
LastName.Text = person.LastName;
DateOfBirth.Text = person.DateOfBirth.ToString("d");
Type.Text = person.Type.ToString();
}
}
}
1
2
3
Response.Status = "404 Not Found";
Response.StatusCode = 404;
Response.TrySkipIisCustomErrors = true;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ Page Title="ContactManager" Language="C#" MasterPageFile="~/Site.Master"
AutoEventWireup="true" CodeBehind="AddEditPerson.aspx.cs"
Inherits="Spaanjaars.ContactManager45.Web.WebForms.People.AddEditPerson" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
<style type="text/css">
.auto-style1 {
width: 100%;
}
</style>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
<table class="auto-style1">
<tr>
<td>First name</td>
<td>
<asp:TextBox ID="FirstName" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>Last name</td>
<td>
<asp:TextBox ID="LastName" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 127 of 162
This is pretty standard ASP.NET code: a table for layout (again, an old skool solution but fine for this demo) with
standard ASP.NET controls. At the bottom of the table you see a DropDownList with a SelectMethod called GetTypes
which youll see in a minute. The ValidationSummary at the end is used to show model errors that may occur when
saving an entity.
In the Code Behind, Page_Load has the same defensive code as Details.aspx. However, this time its OK when no ID
is passed to the page, as that means the page is used to create a new Person instead of editing an existing one. When
theres an ID, LoadPerson gets the person and fills the controls like this:
This is more or less the same as in the details page, except for the code that selects the persons Type. In order to
ensure the DropDownList for the type has all its items, the code first calls the DataBind method on the DropDownList
for the Type property. This forces it to call the GetTypes method that looks like this:
The EnumToListItems method looks like this:
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<td>Date of birth</td>
<td>
<asp:TextBox ID="DateOfBirth" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>Type</td>
<td>
<asp:DropDownList ID="Type" runat="server" SelectMethod="GetTypes"
DataValueField="Value" DataTextField="Text">
</asp:DropDownList>
</td>
</tr>
<tr>
<td> </td>
<td>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Save" />
<a href="Default.aspx">Back to List</a></td>
</tr>
</table>
<asp:ValidationSummary ID="ValidationSummary1" runat="server" ForeColor="Red"
ShowModelStateErrors="true" HeaderText="Please check the following errors:" />
</asp:Content>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void LoadPerson()
{
var peopleRepository = RepositoryHelpers.GetPeopleRepository();
var person = peopleRepository.FindById(_personId);
if (person != null)
{
FirstName.Text = person.FirstName;
LastName.Text = person.LastName;
DateOfBirth.Text = person.DateOfBirth.ToString("d");
Type.DataBind();
var item = Type.Items.FindByValue(((int)person.Type).ToString());
if (item != null)
{
item.Selected = true;
}
}
}
1
2
3
4
public IEnumerable<ListItem> GetTypes()
{
return Helpers.EnumToListItems<PersonType>();
}
1
2
3
4
5
internal static IEnumerable<ListItem> EnumToListItems<T>()
{
Type type = typeof(T);
var result = new List<ListItem>();
var values = Enum.GetValues(type);
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 128 of 162
I have taken a slightly simpler approach than in the ASP.NET MVC version of the site. The code gets the type of the
generic T parameter and feeds it into Enum.GetValues. This returns an array with the available options that the code
loops over. I used the PascalCaseToSpaces method again to split and pascal case values and add spaces. I duplicated
that code in both projects. In a real-world project, I would probably add another class library to the project, called
Spaanjaars.Toolkit for example, move all this shared code into that project and then reference it from both UI
projects.
When the Save button is clicked, the following code is executed:
This code gets an existing person or creates a new one and adds it to the underlying data context using the
repositorys Add method. It then gets the values from the controls and assigns them to the properties of the Person
instance.
In this example, I am relying on model validation completely. So, only when EF rejects the values does the code show
errors to the user. If you wanted to, you could call Validate on the Person and manually add validation errors to the
ModelState object. You would need to do this outside the unit of work to avoid the changes being submitted to the
database even though you know they are valid. Alternatively, you could call the Undo method on the unit of work to
cancel all open changes.
The Create and Edit pages for e-mail addresses and phone numbers are implemented differently. Because the data is
quite simple, I used the standard data-bound controls along with model binding. (Note: I could just as easily have
applied the same principles as used for the AddEditPerson.aspx page; but I chose the built-in data controls to show
you different ways to implement data-driven pages).
6
7
8
9
10
11
12
13
foreach (int value in values)
{
string text = Enum.GetName(type, value);
result.Add(new ListItem(text, value.ToString().PascalCaseToSpaces()));
}
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
protected void SaveButton_Click(object sender, EventArgs e)
{
try
{
using (RepositoryHelpers.GetUnitOfWorkFactory().Create())
{
var repository = RepositoryHelpers.GetPeopleRepository();
Person person;
if (_personId == 0)
{
person = new Person();
repository.Add(person);
}
else
{
person = repository.FindById(_personId);
}
person.FirstName = FirstName.Text;
person.LastName = LastName.Text;
person.DateOfBirth = Convert.ToDateTime(DateOfBirth.Text);
person.Type = (PersonType)Enum.Parse(typeof(PersonType), Type.SelectedValue);
}
Response.Redirect("Default.aspx");
}
catch (ModelValidationException mvex)
{
foreach (var error in mvex.ValidationErrors)
{
ModelState.AddModelError(error.MemberNames.FirstOrDefault() ?? "",
error.ErrorMessage);
}
}
}
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 129 of 162
As an example, take a look at the InsertPhoneNumber method in the Code Behind of PhoneNumbers.aspx:
Although the phoneNumber parameter in the method isnt used in the code, its important to have it there. Without it,
model validation isnt triggered and ModelState.IsValid returns true. With the parameter there, the model binder
fills in the values coming from the form which in turn updates the IsValid state. So, if you omit the number or the
type, the code in the if block wont attempt to add the phone number to the users PhoneNumbers collection and
shows the validation errors to the user with the ValidationSummary control. An alternative solution is to remove the
parameter from the methods signature and then rely on TryUpdateModel to set the value for IsValid. In this case,
its important to carry out this check before you enter the using block of the unit of work or call the Undo method,
otherwise the unit of work will still try to apply your changes.
The code also uses a try/catch block to catch and display any validation exceptions that may have been triggered by
the EF data context.
Most of this code is pretty straightforward and similar to how you would do it without a repository and regular model
binding. Theres one area that needs a bit more attention though: handling the enums in the GridView. To implement
enum support in the GridView, you need the following steps:
Create a TemplateField in the GridView for the ContactType. 1.
Inside the ItemTemplate, write out the text representation of the enum. 2.
Inside the EditItemTemplate, create a DropDownList control that shows the available options and preselects
the correct item
3.
When the item is being saved, retrieve the value from the DropDownList and assign it to the item. 4.
Youll find the details of these steps in the next sections:
Create a TemplateField in the GridView for the ContactType.
This is simple to do: just remove the BoundField for the ContactType property and replace it with something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public void InsertPhoneNumber([QueryString("Id")] int personId, PhoneNumber phoneNumber)
{
if (ModelState.IsValid)
{
try
{
using (RepositoryHelpers.GetUnitOfWorkFactory().Create())
{
var repo = RepositoryHelpers.GetPeopleRepository();
var person = repo.FindById(personId, x => x.PhoneNumbers);
var userNumber = new PhoneNumber { OwnerId = personId };
TryUpdateModel(userNumber);
person.PhoneNumbers.Add(userNumber);
}
}
catch (ModelValidationException mvex)
{
foreach (var error in mvex.ValidationErrors)
{
ModelState.AddModelError(error.MemberNames.FirstOrDefault() ?? "",
error.ErrorMessage);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
<asp:GridView ID="PhoneNumbersGrid" runat="server" AutoGenerateColumns="False"
... Other code here. OnRowUpdating is used in step 4.
OnRowUpdating="PhoneNumbersGrid_RowUpdating">
<Columns>
<asp:BoundField DataField="Id" HeaderText="ID" SortExpression="Id" ReadOnly="True" />
... Other fields here
<asp:TemplateField HeaderText="ContactType" SortExpression="ContactType">
... templates go here
</asp:TemplateField>
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 130 of 162
Inside the ItemTemplate, Write Out the Text Representation of the Enum.
Inside the ItemTemplate, you can display the ContactType for an e-mail address with a Label. The following code
does the trick:
When called, this displays the string representation of the enum, so it would display something like Personal or
Business.
Inside the EditItemTemplate, Create a Drop-Down lList
The EditItemTemplate is a little trickier. When using plain integer values you would probably do something like this:
Unfortunately, this doesnt work. The enum has a value (such as 0, 1, 2) which is different from the text that gets
displayed. In the example above, the code would try to preselect an item by its text (like Personal) rather than by its
value. My next attempt was to cast the value to an int. However, that means you can no longer use BindItem. For
BindItem to work in two directions (to display and to edit a value) you need to use BindItem as-is. The final solution I
came up with is this:
Notice how I am using Item rather than BindItem. Item is only used to display the value and does not try to bind the
value again when the item is being edited. The cast to an int then ensures that the numeric value of the ContactType
is returned which is exactly whats needed to preselect the item in the DropDownList control. The effect of this is that
the GridView is no longer able to use this data after a postback when the item needs to be changed. However, its
easy to manually do this as you see next.
When the item is being saved, retrieve the value from the DropDownList and assign it to the item.
When the GridView is about to call the Update method, it fires its Updating event first. You can hook into this event,
find the selected value of the ContactType DropDownList and assign it to the NewValues collection like this:
The value you assign to the NewValues collection carries over to the data in the model binder, so by the time
UpdateEmailAddress gets called, this value is available and assigned to the e-mail address that is about to be saved in
the database.
13
14
15
16
... Other fields here
</Columns>
</asp:GridView>
1
2
3
4
5
<asp:TemplateField HeaderText="ContactType" SortExpression="ContactType">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("ContactType") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
1
2
3
4
5
6
<EditItemTemplate>
<asp:DropDownList runat="server" ID="ContactType"
SelectedValue='<%# BindItem.ContactType) %>' SelectMethod="GetContactTypes"
DataValueField="Value" DataTextField="Text">
</asp:DropDownList>
</EditItemTemplate>
1
2
3
4
5
6
<EditItemTemplate>
<asp:DropDownList runat="server" ID="ContactType"
SelectedValue='<%# (int)(Item.ContactType) %>'
SelectMethod="GetContactTypes" DataValueField="Value"
DataTextField="Text"></asp:DropDownList>
</EditItemTemplate>
1
2
3
4
5
6
protected void EmailAddressesGrid_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
var typeDropDown = (DropDownList)
EmailAddressesGrid.Rows[e.RowIndex].FindControl("ContactType");
e.NewValues["ContactType"] = (ContactType)Convert.ToInt32(typeDropDown.SelectedValue);
}
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 131 of 162
This concludes the implementation of the ASP.NET 4.5 Web Forms site. Theres more code in the project than I have
discussed here, but its either standard ASP.NET code that falls outside the scope of this article, or it is code similar to
the code that I did present in this article.
Summary
In this article you saw how to implement a data-driven web site built with ASP.NET 4.5 Web Forms with model binding
(new to ASP.NET 4.5) and using the repository and model projects created in earlier articles in this series. As you saw,
its pretty straight forward to implement these pages using either the built-in data-bound controls, or using a more
custom code oriented approach where you have more control over the code as shown in the AddEditPerson.aspx
page.
Most of the code needed is standard ASP.NET code. In the Code Behind of the pages the various model binding
methods make use of the repository and unit of work classes. To avoid instantiating these classes over and over again
in all your ASPX pages, the RepositoryHelper class contains two factory methods that return an instance of the
requested type. Should you ever want to use another repository (with a NHibernate or other ORM implementation for
example), then all you need to update are these two helper methods.
Although MVC and Web Forms are probably the two most likely frontend implementations for an ASP.NET framework
like this, there are other options as well. In the next article, you see how to expose (some of) your repository methods
over a WCF service. In the article that follows you see how to build a command line tool to import existing data from
other sources, such as a CSV file.
Stuff I Like to Do
I only use the data-bound controls for simple data structures. E-mail addresses and phone numbers are good
candidates as they only have a few properties. And even then you saw how difficult it can be to do relatively
simple stuff as using an enum for one of the objects properties. I use the hand coded data access pages for
complex types such as Person. You recover the time spent on creating the initial UI controls by having to spend
less time on duplicating the code in the Item and EditItem templates and on handling the many events of the
data-bound and data source controls.
Although not shown in this project, its easy (and recommended) to use UI frameworks like jQuery UI or Kendo
UI. These frameworks make it easy to create good-looking UIs with little effort.
I like to group files in the project by their type. In the sample project, I stored the two helper classes in the
Helpers folder and all the Web Forms for managing contact people in the People folder. Although you could
theoretically store everything in the root or pretty much anywhere else, I find that a structure like this makes it
easier to find your files and maintain the application.
Links in this Document
(1) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part07/Figure7-1_WebForms_People_list.png
(2) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part07/Figure7-2_WebForms_Edit_Person.png
(3) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part07/Figure7-3_WebForms_Edit_Address.png
(4) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part07/Figure7-4_WebForms_EmailAddresses.png
(5) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part07/Figure7-5_Model_Architecture.png
ASP.NET N-Layered Applications - Implementing a Web Forms 4.5 Frontend (Part 7)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 132 of 162
ASP.NET N-Layered Applications - Implementing a WCF 4.5
Frontend (Part 8)
This is Part 8 in a series of 10 that show you how to build N-Layered applications using ASP.NET 4.5 and Entity
Framework 5 Code First. In this part youll see how to build a WCF service that makes use of the model and repository
projects I have shown in the first five articles in this series. The WCF service can be used by different types of
applications to access contact people and their related data.
Introduction
In this article, Ill show you how to build a WCF service that can expose contact data to calling applications over the
internet or local network. WCF is widely accessible from other application types so its a great platform to share data
with other applications, whether they are based on .NET or not.
In this article, Ill cover the following topics:
How to use StructureMap to inject repositories and units of work in your WCF service.
How to design your service API.
How to Unit Test your service methods.
How to return and access data.
This wont be a very long article as a lot of it has already been discussed, or is based on common WCF functionality.
However, I decided to include this part in the series to give you a complete picture of using the repositories and
models in a variety of applications.
Under the hood, this WCF service application uses the PeopleRepository, targeting the Entity Framework for all data
access. To see how it all fits together, heres the architecture diagram showing the WCF frontend application and how
its related to the other components in the system:
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 133 of 162
(1) See Links in this Document at the end for the full URL of this image.
Figure 8-1 The N-Layer Architecture Diagram
Youll see another view on the diagram a little later in this article when View Models are discussed.
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 134 of 162
Using StructureMap for Dependency Injection (DI)
Just like in an ASP.NET MVC application, it could be desirable to use Dependency Injection in your WCF services. The
biggest benefit is that it makes your services testable. Since a WCF Service is a normal class, you can instantiate it
and call its methods, just like any other class. For example, the following WCF Service (found in the
Spaanjaars.ContactManager.Web.Wcf project):
can be tested like this:
The first test ensures that the constructor of the service throws an exception if you dont supply a valid
IPeopleRepository. The sample project contains two more tests; one to test the IUnitOfWorkRepository, the other
to ensure the constructor does not throw an exception if both objects are supplied correctly. Although your code is
likely to crash elsewhere when you dont supply the correct objects in the constructor, I like these kinds of tests as
they clearly enforce code by contract and fail fast to be implemented in the service, which will avoid problems
further down the road.
Note: for more information on testing for exceptions using FluentAssertions, check out the documentation at:
http://fluentassertions.codeplex.com/documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ContactManagerService : IContactManagerService
{
private readonly IPeopleRepository _peopleRepository;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public ContactManagerService(IPeopleRepository peopleRepository,
IUnitOfWorkFactory unitOfWorkFactory)
{
if (peopleRepository == null)
{
throw new ArgumentNullException("peopleRepository", "peopleRepository is null.");
}
if (unitOfWorkFactory == null)
{
throw new ArgumentNullException("unitOfWorkFactory", "unitOfWorkFactory is null.");
}
_peopleRepository = peopleRepository;
_unitOfWorkFactory = unitOfWorkFactory;
}
public PersonModel GetPerson(int id)
{
var person = _peopleRepository.FindById(id);
return new PersonModel {FirstName = person.FirstName, Id = person.Id};
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[TestClass]
public class ContactManagerServiceTests : ServiceTestBase
{
[TestMethod]
public void ContactManagerServiceRequiresRepository()
{
Action act = () => new ContactManagerService(null, new FakeUnitOfWorkFactory());
act.ShouldThrow<ArgumentNullException>().WithMessage("peopleRepository is null",
ComparisonMode.Substring);
}
[TestMethod]
public void GetPersonByIdReturnsCorrectPerson()
{
var service = new ContactManagerService(new FakePeopleRepository(),
new FakeUnitOfWorkFactory());
var person = service.GetPerson(24);
person.LastName.Should().Be("Youngest Lastname");
person.Id.Should().Be(24);
}
}
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 135 of 162
The second test, GetPersonByIdReturnsCorrectPerson, checks whether the service method calls the FindById
method on the IPeopleRepository that is passed in and then constructs a correct PersonModel based on the return
value of FindById. For this to work, I implemented FindById in the fake repository and had it return one of the
available persons in the list. Youll see more of the GetPerson method after I have showed you how to set up
Dependency Injection in a WCF project.
To enable DI in a WCF project using StructureMap, follow these steps:
Install the package StructureMap into your WCF project.
Add a few code files to the project. Most of these are boiler plate files that you can add to any WCF project
without modification. You could even add them to the Infrastructure project or other shared class library if
youre willing to reference WCF related libraries such as System.ServiceModel.Activation.
Write a method that initializes StructureMap, similar to how I did it in the ASP.NET MVC project.
Modify the markup of your service method so it uses a custom ServiceHostFactory.
You see these steps in detail next.
Install StructureMap
This is easy. Just execute Install-Package StructureMap from the Package Manager Console window. Make sure
that the WCF project is selected in the Default project drop-down list.
Add Code Files to the Project to Define a Custom ServiceHostFactory and other Custom Types
In the root of the WCF project in the sample application you find a folder called StructureMap. It has five files, four of
which start with StructureMap:
Figure 8-2 Solution Explorer Showing StructureMap files for the WCF project
These classes are based on the work by Jimmy Bogard (who came up with the initial solution) and Scott Griffin (who
refined the code and fixed some issues.) You can find more background information here:
http://lostechies.com/jimmybogard/2008/07/30/integrating-structuremap-with-wcf
http://www.sgriffinusa.com/2011/02/setting-up-wcf-to-use-structuremap.html
You find the slightly modified classes in my sample project or you can refer to the online articles for the original
implementation.
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 136 of 162
Using Jimmys and Scotts work, all I needed to do was add the files to my project, and write a single class that sets up
StructureMap.
Write a method that Initializes StructureMap
The configuration code for StructureMap can be found in the Ioc.cs file. It looks like this:
This is almost identical to the code in the ASP.NET MVC project: it tells StructureMap to resolve types using the default
conventions (when requested to return an ISomething, StructureMap will try to use the Something type) and to scan
all assemblies in the Bin folder. In addition, it sets up EFUnitOfWorkFactory as the type to return when an
IUnitOfWorkFactory is requested.
Modify the Markup of the Service
To tell WCF to use another ServiceHostFactory that returns services whose constructors can receive injected types,
you need to modify the Markup of the service. You see how to do this in the next section when the actual service is
built, but if you want to see the code, heres how it looks:
The Factory attribute points to my custom StructureMapServiceHostFactory that in turn is able to return concrete
instances of my service(s) with the proper dependencies injected in the services constructor.
Adding the Service to the WCF Project
To add the service to your WCF project, right-click the project in the Solution Explorer, choose Add | New Item and
then choose WCF Service. In my project, I named the service ContactManagerService, giving me the following files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class Ioc
{
public static void Initialize()
{
ObjectFactory.Initialize(scanner =>
{
scanner.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.WithDefaultConventions();
});
scanner.For<IUnitOfWorkFactory>().Use<EFUnitOfWorkFactory>();
});
}
}
1
2
3
4
<%@ ServiceHost Language="C#" Debug="true"
Service="Spaanjaars.ContactManager45.Web.Wcf.ContactManagerService"
CodeBehind="ContactManagerService.svc.cs"
Factory="Spaanjaars.ContactManager45.Web.Wcf.StructureMap.StructureMapServiceHostFactory" %>
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 137 of 162
Figure 8-3 Solution Explorer Showing the Service Files
The .svc file is the actual service file that will be called by remote systems. Its code behind contains the actual
implementation, The IContactManagerService file contains an interface that your service must implement, and where
you define the methods you want to expose from your service.
With the service added, I then had to update the Factory attribute in the markup of the .svc file. To do that, I right-
clicked ContactManagerService.svc and chose View Markup. Double-clicking the svc file itself wont work as it will
open its code behind file instead. I then added the following Factory attribute to the code after the Code Behind
attribute that was already there:
Now that the service is added and configured for DI, the next logical step is defining its public API.
Designing your Service API
A WCF service is typically based on an interface that defines the members that you want to expose publicly. But which
members do you want to expose? That all depends on your business requirements. In the sample application I decided
to keep things relatively simple, so its easier to focus on the design principles rather than on the actual
implementation. My service will support the following features:
Name Description
GetPerson (int id) Returns a PersonModel based on the incoming ID.
Contains the two addresses, but no phone numbers or
e-mail addresses.
InsertPerson(PersonModel personModel) Enables you to insert a new person. Does not support
inserting phone numbers or e-mail addresses.
UpdatePerson(PersonModel personModel) Enables you to update an existing person. Does not
support inserting phone numbers or e-mail addresses.
DeletePerson(int id) Enables you to delete an existing person.
This leads to the following proposed interface:
1 Factory="Spaanjaars.ContactManager45.Web.Wcf.StructureMap.StructureMapServiceHostFactory"
1
2
public interface IContactManagerService
{
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 138 of 162
In WCF, its recommended to put attributes on types and members you want to expose through the service, resulting
in the following interface:
In the sample application the actual return type of InsertPerson and UpdatePerson is different. Youll see why this is
next.
Returning and Accepting Data
In order to build a flexible solution that remains easy to maintain in the future, its important that your WCF services
dont return types from your model directly. First of all, you may be exposing too much data, leaking more details than
you may want to. But another important reason for not returning model types directly is flexibility. A WCF service
serves as a contract between the service and any consuming client application, which means that as long as the
service is publicly available, you cant change the members of the service or the types they return. For example,
renaming a property of the Person class would result in a breaking change for all applications consuming your service.
If you put a layer of View Models on top of your model types, you can freely change the underlying implementation
without breaking the service contract. Figure 8-4 shows how View Models are flowing between the WCF service project
and external applications that use the service:
3
4
5
6
7
PersonModel GetPerson(int id);
int InsertPerson(PersonModel personModel);
void UpdatePerson(PersonModel personModel);
void DeletePerson(int id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[ServiceContract]
public interface IContactManagerService
{
[OperationContract]
PersonModel GetPerson(int id);
[OperationContract]
int InsertPerson(PersonModel personModel);
[OperationContract]
int UpdatePerson(PersonModel personModel);
[OperationContract]
void DeletePerson(int id);
}
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 139 of 162
(2) See Links in this Document at the end for the full URL of this image.
Figure 8-4 View Models in the WCF Service Project
In the sample application I created two main View Model types that are returned by the service: PersonModel and
AddressModel that look as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[DataContract]
public class PersonModel
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public DateTime DateOfBirth { get; set; }
[DataMember]
public AddressModel HomeAddress { get; set; }
[DataMember]
public AddressModel WorkAddress { get; set; }
}
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 140 of 162
For more information about the DataContract and DataMember attributes, check out the following links:
http://msdn.microsoft.com/en-us/library/ms733127.aspx
http://stackoverflow.com/questions/4836683/wcf-datacontract
Note that the PersonModel has two properties of type AddressModel. Furthermore, PersonModel does not have
properties for the e-mail address and phone number collections. PersonModel is used as the type returned for
GetPerson as well as an input parameter for InsertPerson and UpdatePerson. This doesnt have to be the case and
you could easily create different View Models for different methods.
I use AutoMapper to map between the Person and Address on one side and the PersonModel and AddressModel on
the other side, with this configuration code that should look very familiar by now:
When mapping to a Person, I am ignoring five properties. Just as an example, I am ignoring the Type property. Inside
the service, Ill set this type to a hardcoded value. Obviously, you can include the Type property as well so external
code could explicitly set it. I am ignoring the date properties as they are set by the Entity Framework automatically.
Finally, I am ignoring the contact data as I decided not to implement them in this service to keep things simple. Note
though that it is straight forward to add them; you could implement them as a List<EmailAddressModel> for example
and then provide AutoMapper configuration for it.
I am also using IsSourceValueNull to avoid overwriting an existing address with a null value if the calling code has
not submitted an address. You see how this is used later in the article.
You see what the mapper code for the ValidationResult is for in the next section that talks about error handling.
Returning Validation Errors
Imagine that the InsertPerson has been implemented, and that youre calling it from an external client application. If
all works out as expected, the calling code receives the ID of the newly created person. So far so good. But what if the
validation of the Person instance fails? For example, because the calling code didnt supply a first name? To handle
that situation, your code could throw an exception. However, depending on how you configured your WCF services,
those exceptions may never end up at the client, leaving them guessing as to why the insert failed. As an alternative,
18
19
20
21
22
23
24
25
26
27
28
29
[DataContract]
public class AddressModel
{
[DataMember]
public string Street { get; set; }
[DataMember]
public string City { get; set; }
[DataMember]
public string ZipCode { get; set; }
[DataMember]
public string Country { get; set; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static class AutoMapperConfig
{
public static void Start()
{
Mapper.CreateMap<PersonModel, Person>()
.ForMember(x => x.Type, x => x.Ignore())
.ForMember(x => x.DateCreated, x => x.Ignore())
.ForMember(x => x.DateModified, x => x.Ignore())
.ForMember(x => x.EmailAddresses, x => x.Ignore())
.ForMember(x => x.PhoneNumbers, x => x.Ignore())
.ForMember(x => x.HomeAddress, y => y.Condition(src => !src.IsSourceValueNull))
.ForMember(x => x.WorkAddress, y => y.Condition(src => !src.IsSourceValueNull));
Mapper.CreateMap<Person, PersonModel>();
Mapper.CreateMap<AddressModel, Address>()
.ForMember(x => x.ContactType, x => x.Ignore());
Mapper.CreateMap<Address, AddressModel>();
Mapper.CreateMap<ValidationResult, ValidationResultModel>();
Mapper.AssertConfigurationIsValid();
}
}
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 141 of 162
your method could return a collection of ValidationResult instances. However, you cant have a method that returns
either an int or a collection of errors, unless you change the return type to an object which isnt really helpful.
The solution to this problem I typically use is to use a generic class that has two properties: the actual data you want
to return (as a generic T) and a collection of validation errors. Heres how the class could look:
The type of T is defined when instantiating the class as youll see in a moment. The Errors collection is of type
IEnumerable<ValidationResultModel>. I had to create the ValidationResultModel class because the
ValidationResult class from the DataAnnotations namespace is not marked as serializable and as such cannot be
sent back as a result from a WCF service. I am using AutoMapper once again to go from a ValidationResult to a
ValidationResultModel.
To return a valid int from a service method using this class, you can use code like this:
In this case, the Errors property is null. If you need to return errors instead, you can use code like this:
Here, the Data property is null, but now the Errors collection is filled. The code uses AutoMapper to convert the
collection of errors in the errors variable to a new List of ValidationResultModel.
For this code to work, the methods signature must be changed to this:
And the calling code can then use the return value as follows:
Implementing the Service
With all the hard work of creating interfaces, models, return types and mapping code done, implementing the service
is now pretty easy. For example, all you need for GetPerson is this:
1
2
3
4
5
6
7
8
9
[DataContract]
public class ServiceResult<T>
{
[DataMember]
public T Data { get; internal set; }
[DataMember]
public IEnumerable<ValidationResultModel> Errors { get; internal set; }
}
1 return new ServiceResult<int?> { Data = person.Id };
1 return new ServiceResult<int?> { Errors = Mapper.Map(errors, new List<ValidationResultModel>()) };
1
2
3
4
public ServiceResult<int?> InsertPerson(PersonModel personModel)
{
...
}
1
2
3
4
5
6
7
8
9
10
11
var personModel = new PersonModel { ... };
var result = wcfClient.InsertPerson(personModel);
if (result.Errors.Any())
{
// Deal with errors
}
else
{
int newId = result.Data.Value;
// Use the new ID of the person somehow
}
1
2
public PersonModel GetPerson(int id)
{
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 142 of 162
And InsertPerson can be implemented like this:
I configured AutoMapper to ignore the two address properties on PersonModel when they are null using
IsSourceValueNull. That means that existing addresses wont be overwritten by a null value.
Note that this WCF service will currently only save colleagues as that type is hardcoded in InsertPerson. Obviously,
you can change this behavior by making Type a member of PersonModel as well.
UpdatePerson is similar although it calls Undo on the Unit of work to undo any changes when any one of the objects is
in an invalid state. Finally, DeletePerson simply forwards the ID of the person to the Remove method on the repository
and then exits.
The WCF service now supports all CRUD operations on a contact person: Create, Read, Update, and Delete. Obviously,
you can expand this example any way you see fit. You could add support for e-mail addresses and phone numbers,
check whether the current user has access to view or modify a contact person (using WCF security or a custom
solution) and more, all using the same principles.
Stuff I Like to Do
I usually write unit tests for my AutoMapper code. Not for all of it but mostly for the exceptions. A test like
NullValueInSourceDoesNotOverwriteTarget that youll find in the code helps to ensure youre not
accidentally overwriting existing values with null values. This can prevent awkward bugs or data corruption
from happening.
I also write tests for my WCF services. By abstracting external dependencies as youve seen in this article, the
service methods become very easy to test. Since service methods are often used in machine-to-machine
scenarios its even more important they behave correctly as its less likely your end users will run into them and
report them to you.
Summary
In this article you saw how to use the contact manager applications repository and unit of work to expose application
data to the internet, and to accept requests to insert, edit and delete contact people. The article started with an
explanation of enabling Dependency Injection in a WCF. Although the code to implement this isnt very straightforward,
its pretty much the same code over and over again so it mostly comes down to copying a few existing files into your
project and then writing a bit of code to configure StructureMap.
You then saw how to design the API of the service, starting with the design of the interface. Heres where you define
the members of the service such as GetPerson, InsertPerson and so on. To decouple your service layer from your
3
4
5
var person = _peopleRepository.FindById(id);
return Mapper.Map(person, new PersonModel());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public ServiceResult<int?> InsertPerson(PersonModel personModel)
{
var person = new Person();
Mapper.Map(personModel, person);
// For demo purposes, let's assume we only accept colleagues.
// You could easily add Type to the PersonModel and let external code set it
person.Type = PersonType.Colleague;
List<ValidationResult> errors = person.Validate().ToList();
if (errors.Any())
{
var result = new ServiceResult<int?>
{ Errors = Mapper.Map(errors, new List<ValidationResultModel>()) };
return result;
}
using (_unitOfWorkFactory.Create())
{
_peopleRepository.Add(person);
}
return new ServiceResult<int?> { Data = person.Id };
}
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 143 of 162
applications model, the service doesnt expose your model types directly, but uses View Models instead. Once again,
AutoMapper is very helpful to minimize the code you need to write to map from one type to another.
At the end you saw the actual implementation of the service. As an example, I hard coded some of the incoming data
in the service; for example, I set the Type of a Person explicitly and fixed the addresses. If you want, you can also fix
issues like this in your model, or force calling code to only supply valid data.
Besides a WCF service theres another common way to get data into an application like the contact manager in an
automated way: using a command line tool to import data from an external resource. You see how to do this in the
next part in this article series.
Links in this Document
(1) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part08/Figure8-1_Model_Architecture.png
(2) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part08/Figure8-4_Architecture_Diagram.png
ASP.NET N-Layered Applications - Implementing a WCF 4.5 Frontend (Part 8)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 144 of 162
ASP.NET N-Layered Applications - Importing Data Using the
API (Part 9)
This is Part 9 in a series of 10 that show you how to build N-Layered applications using ASP.NET 4.5 and Entity
Framework 5 Code First. In this part youll see how to build a command line application to import data from an
external source (a CSV file in this case), using the applications repository and model classes. Although this article is
not related to ASP.NET directly, many ASP.NET developers may still benefit from it as importing data from an external
resource is a common activity for ASP.NET developers.
Introduction
Quite often when you build data-driven applications like the Spaanjaars ContactManager, you dont always start with a
green field project. Its quite likely you already have an existing system with its own database that contains data you
have to use in the new application. To get that data into your new application, you could import directly into SQL
Server, or you could use the applications API. The advantage of using the API is that you get all the type checking and
validation that has been built into the model classes. Using the by now familiar repositories and model classes, you can
quickly import data from any data source that .NET can communicate with and ensure only data that matches your
applications rules makes it into the system.
In this article, Ill show you how to import data from a CSV file using a third-party library called FileHelpers. You can
use the exact same techniques for other data sources such as databases (SQL Server, Oracle, Access, other OLEDB
and ODBC databases and so on), XML files, web services and more.
In this article, Ill cover the following topics:
How to read a CSV file line by line using the FileHelpers library.
How to deal with invalid or incomplete data in an import application.
How to ensure a great performance of the application.
Importing from a CSV File
In the sample application that comes with this article, you find a project called Spaanjaars.ContactManager.Import
in the Frontend folder. Its a C# Console Application designed to read a CSV file from disk, extract data from that file,
transfer that data into contact people and contact details and insert them in the database using the repository and
model classes youve seen in the first five articles in this series. Heres how the application looks in the Solution
Explorer:
Figure 9-1 Solution Explorer for the Spaanjaars.ContactManager.Import application
Ill discuss the three code files later in this article, and the CSV file called People.csv in the next section.
Introducing the CSV File
To make things as realistic as possible, I created a CSV file that is not error-free or optimal and contains a number of
issues that need to be resolved during the import. The following table lists the available columns in the file and the
possible problems the data in those columns can have.
ASP.NET N-Layered Applications - Importing Data Using the API (Part 9)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 145 of 162
Name Type Description
First name string The persons first name. Always present.
Last name string The persons first name. Always present.
Date of birth DateTime The persons date of birth in the format M/d/yyyy. The date is always present.
Type int The type of the person; matches the values in the PersonType enum.
Address 1 string These four columns contain the home address of the person. Not every field is
always filled. Some fields contain the text NULL to indicate a null value.
Zip 1 string
City 1 string
Country 1 string
Address 2 string These four columns contain the work address of the person. Not every field is
always filled. Some fields contain the text NULL to indicate a null value.
Zip 2 string
City 2 string
Country 2 string
Email string The persons e-mail address. Some e-mail addresses are in an invalid format.
This field can be null in which case it contains the text NULL.
Email type string The type of e-mail address. Can be PRIVATE or COMPANY or can be NULL when
the e-mail address is null. Note that these dont match the enum values directly
and thus need to be converted into the proper enum type.
Phone number string The persons phone number. Since no type is present in the file, the application
imports these as ContactType.Business.
Heres an example row:
The CSV file is copied to the applications Bin folder whenever it changes, and the application will use that file if youre
not explicitly specifying a full path to a file as an argument at the command line.
When the application is running, it will report the number of rows that have been imported successfully and the
number of rows that failed. It also reports the number of rows that are processed per second. Figure 9-2 shows the
application while running:
(1) See Links in this Document at the end for the full URL of this image.
Figure 9-2 The Command Line Application Importing Contact Data
If you wonder if I know that many people, the answer is no. I generated a huge amount of sample data in the SQL
Server database using Red Gates Data Generator
(2)
and then exported that to a CSV file. Part 10 has more details on
this tool.
1
2
Crystal,Palmer,7/6/1968,2,113 East Old Road,63867,30 New St.,Togo,55 Milton Road,
28027,93 West White Hague Road,Macao,[email protected],COMPANY,(845) 121-3652
ASP.NET N-Layered Applications - Importing Data Using the API (Part 9)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 146 of 162
Introducing the FileHelpers
You have a couple of different options when it comes to parsing a CSV file. You could write your own custom code to
do it, you can use the TextFieldParser that is part of the Microsoft.VisualBasic assembly, or you can use one of
the existing third party libraries such as CsvHelper (https://github.com/JoshClose/CsvHelper), CsvReader
(http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader) or FileHelpers (http://filehelpers.sourceforge.net/). I
have used FileHelpers a number of times in real-world projects in the past and it has served me well, so Ill use it in
this article again.
Importing a CSV file with FileHelpers is really easy and is typically a three-step process:
Install the FileHelpers library using NuGet.
Define a custom class that can hold the data for an individual row. Each field in the class maps to a column in
the source file. You must define the fields in the order they appear in the source file. You can use .NET types
like string, int and DateTime and the FileHelpersEngine will correctly populate them. You can use various
attributes to define the conversion from the source to the target to handle special cases.
Finally, you can loop over the individual rows in the source file using the FileHelperEngine. In my example, I
am using the FileHelperAsyncEngine class which lets you process rows as they are being read by the engine.
The alternative is the FileHelperEngine class which loads all rows at once which may consume more memory
when reading large files.
Ill show you each step in detail in the next sections.
Installing FileHelpers using NuGet
Once again, this is easy. Just execute Install-Package FileHelpers from the Package Manager Console window.
Make sure your import application is selected in the Default project drop-down list.
Define a Custom Class to Hold the Imported Data
My custom data class looks more or less like the CSV file I described earlier. For each column in the source file I create
a field. I also used .NET types for columns such as the DateOfBirth (a DateTime) and the Type (an int in this
example). Heres the full class (found in ImportPerson.cs):
As you can see, all you need to do is define a class, add a few fields, and then apply some attributes. The
DelimitedRecord attribute tells the engine that each column value is separated by a comma. The FieldConverter
attribute helps to convert source data to the target. In this example, I am also specifying the date format as it appears
in the source file. For more information on the attributes, take a look at the FileHelpers web site at:
http://filehelpers.sourceforge.net/.
Processing Contact Rows
Once the data class is setup, reading a CSV file and getting instances of ImportPerson back is really simple. Heres the
minimum amount of code you need to write:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[DelimitedRecord(",")]
public class ImportPerson
{
public string FirstName;
public string LastName;
[FieldConverter(ConverterKind.Date, "M/d/yyyy")]
public DateTime DateOfBirth;
public int Type;
public string Address;
public string Zip;
public string City;
public string Country;
public string Address2;
public string Zip2;
public string City2;
public string Country2;
public string Email;
public string EmailType;
public string PhoneNumber;
}
1
2
3
using (var importEngine = new FileHelperAsyncEngine<ImportPerson>())
{
importEngine.BeginReadFile(_fileName);
ASP.NET N-Layered Applications - Importing Data Using the API (Part 9)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 147 of 162
For the sample application, the foreach loop contains code like this (theres more, but most of it has to do with error
handling and writing the progress to the screen). You find this code in Program.cs inside the Main method.
This code creates a Person instance that is eventually added to the repository if its completely valid. I am using
AutoMapper again to go from an ImportPerson to a Person. Since the four properties of each address come from
separate columns in the source file, I needed some trickery to tell AutoMapper how to take those fields and turn them
into an Address. Heres the code for the AutoMapper configuration (inside AutoMapperConfig.cs):
ResolveUsing uses a lambda expression that receives an instance of the ImportPerson and returns an instance of
Address. Within the function body I can access the relevant fields to construct a new Address. I really like this as it
enables me to define all mapping in a central location so my core application is free from stuff like this.
I am taking a slightly different approach with the TryAddEmailAddress and TryAddPhoneNumber methods. Heres the
code for the first of these two methods:
When the column for the phone number does not contain an empty string or the word NULL, I add a phone number to
the Persons PhoneNumbers collection. As a contact type I am hardcoding ContactType.Business as the source file
contains no relevant information to determine the real type. This is just an implementation choice. How you handle
exceptions like these depends entirely on the source file and the business requirements of your application.
Note that earlier I said that the source file contains invalid data for the addresses and e-mail addresses. For example,
an address may not be complete and one or more columns may contain the value NULL. Once again, how you handle
that depends on your requirements. As an example, I wrote two methods that can set the address properties to null
4
5
6
7
8
9
foreach (ImportPerson importPerson in importEngine)
{
// Work with importPerson here.
}
importEngine.Close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var person = new Person();
Mapper.Map(importPerson, person); // Maps the ImportPerson to a person using AutoMapper.
TryAddEmailAddress(importPerson, person);
TryAddPhoneNumber(importPerson, person);
FixAddress(person.HomeAddress, changeMissingDataToNull: false);
FixAddress(person.WorkAddress, changeMissingDataToNull: false);
if (!person.Validate().Any())
{
PeopleRepository.Add(person);
// Todo: Commit changes
}
1
2
3
4
5
6
7
8
9
10
11
Mapper.CreateMap<ImportPerson, Person>()
.ForMember(x => x.Id, x => x.Ignore())
.ForMember(x => x.DateCreated, x => x.Ignore())
.ForMember(x => x.DateModified, x => x.Ignore())
.ForMember(x => x.EmailAddresses, x => x.Ignore())
.ForMember(x => x.PhoneNumbers, x => x.Ignore())
.ForMember(x => x.HomeAddress, x => x.ResolveUsing(ip => new Address(ip.Address,
ip.City, ip.Zip, ip.Country, ContactType.Personal)))
.ForMember(x => x.WorkAddress, x => x.ResolveUsing(ip => new Address(ip.Address2,
ip.City2, ip.Zip2, ip.Country2, ContactType.Business)));
Mapper.AssertConfigurationIsValid();
1
2
3
4
5
6
7
private static void TryAddPhoneNumber(ImportPerson importPerson, Person person)
{
if (!string.IsNullOrEmpty(importPerson.PhoneNumber) && importPerson.PhoneNumber != "NULL")
{
person.PhoneNumbers.Add(importPerson.PhoneNumber, ContactType.Business);
}
}
ASP.NET N-Layered Applications - Importing Data Using the API (Part 9)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 148 of 162
or to <Unknown> when they contain invalid data:
The value <Unknown> will eventually make it to the database, but null may be rejected if at least one of the other
three properties of Address does have a value (remember: its all or nothing. No value for all properties is considered
null, but at least one value means the Validate method of Address requires all values to be set).
Right before the code tries to add the Person instance to the repository, it calls Validate. When the Person, or any of
its properties contain invalid data, the person is not added to the repository. What you do in this case depends on your
business requirements. You can log the error and write the line number to a file for later processing or you could try to
automatically fix problems as I did with the Addresses.
Once the person is added to the repository, the final thing to do is save the changes to the database. For the import
application that deals with many entities at the same time, this works a bit differently as youll see in the next section.
Ensuring Great Performance
When saving the changes in the repository to the database, you have a couple of options. For example you could try to
save all changes at once, with code like this:
For every valid row in the CSV file, a new Person is added to the repository. As youve seen in Part 5 of this series, at
the end of the using block the UnitOfWork automatically calls SaveChanges which submits the changes to the
database. In testing I found this to be quite a slow method when dealing with many entities. The reason for this is that
inserts in an EF DbContext become slower when the context already contains other entities. You wont notice this with
a few rows, but it becomes quite obvious with hundreds or thousands of rows in the source file.
An alternative to solution to this problem looks like this:
In this example a new UnitOfWork is created for each individual person (inside the foreach loop) which is then saved
to the database. This also doesnt perform well, because of all the overhead of instantiating data contexts and SQL
Server connections. I found that saving the people entities every 30 rows or so gave me the best performance. For
that to work, I had to make two changes. First, I needed to keep track of the number of new people being added using
the success variable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static Address FixAddress(Address address, bool changeMissingDataToNull)
{
string street = SetValueToNullOrUnknown(address.Street, changeMissingDataToNull);
string zipCode = SetValueToNullOrUnknown(address.ZipCode, changeMissingDataToNull);
string city = SetValueToNullOrUnknown(address.City, changeMissingDataToNull);
string country = SetValueToNullOrUnknown(address.Country, changeMissingDataToNull);
return new Address(street, city, zipCode, country, address.ContactType);
}
private static string SetValueToNullOrUnknown(string value, bool changeMissingDataToNull)
{
string temp = changeMissingDataToNull ? null : "<Unknown>";
return string.IsNullOrEmpty(value) || value == "NULL" ? temp : value;
}
1
2
3
4
5
6
7
8
using (var uow = new EFUnitOfWorkFactory().Create())
{
foreach (ImportPerson importPerson in engine)
{
...
PeopleRepository.Add(person);
}
}
1
2
3
4
5
6
7
8
foreach (ImportPerson importPerson in engine)
{
using (var uow = new EFUnitOfWorkFactory().Create())
{
...
PeopleRepository.Add(person);
}
}
ASP.NET N-Layered Applications - Importing Data Using the API (Part 9)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 149 of 162
Secondly, I had to call Commit on the IUnitOfWork on every 30 rows and pass true to have it clear out the existing
data context. The final solution looks like this:
The Commit method saves the changes in the DbContext by calling SaveChanges:
The resetAfterCommit parameter clears the existing data context which gets rid of the previously saved Person
entities. On the next call to GetDataContext, a new instance is created and returned.
The remaining records that are not saved by calling Commit are saved automatically at the end of the using block.
This import implementation actually drove part of the design of the IUnitOfWork interface and concrete
implementations. Initially, I did not design a Commit method as committing would happen automatically. For most web
applications where you only have a few entities that need to be saved at the same time, that works really well.
However, for bulk inserts like this one, I really needed a better solution, and a Commit method to send the changes to
the database explicitly as well as a means to clear out the existing data context turned out to be the best solution.
I tested the performance of the various alternatives by inserting 30,000 rows from a CSV file for each option. The
table below shows the performance numbers for each alternative:
Strategy Number of items per second Time
1 Unit of work for the entire CSV file 10 ~ 40 minutes
1 Unit of work per line in the CSV file A few A few hours
Save every 10 rows 546 54 seconds
Save every 30 rows 671 44 seconds
Save every 100 rows 551 54 seconds
When using one unit of work per line in the CSV file, performance degraded up to a point where less than a row per
second was processed. I canceled the import after almost an hour at which time it had processed around 20 percent of
the rows.
Saving every 30 rows seemed to give the best performance on my machine and for my application. Its important to
test out settings like this yourself, as a lot of it depends on the specifics of your application. If raw speed is very
important you could also consider alternative solutions such as bulk copy, or disabling features like validation and
change tracking on the DbContext. More details can be found here:
http://elegantcode.com/2012/01/26/sqlbulkcopy-for-generic-listt-useful-for-entity-framework-nhibernate/
http://stackoverflow.com/questions/5940225/fastest-way-of-inserting-in-entity-framework/5942176#5942176
http://stackoverflow.com/questions/6107206/improving-bulk-insert-performance-in-entity-framework
When I disabled validation and change tracking I was able to insert around 960 items a second; a performance
improvement of over 40 percent compared to the best performing solution. If you decide to use this feature, be sure
to make it configurable so only tools like the Importer use it and other code still uses the default validation and
change tracking mechanisms. I havent implemented this in the sample application as I feel it may not be necessary in
the majority of the cases and should be treated as a one-off implementation.
1
2
3
4
5
PeopleRepository.Add(person);
if (success % 30 == 0)
{
uow.Commit(true);
}
1
2
3
4
5
6
7
8
public void Commit(bool resetAfterCommit)
{
DataContextFactory.GetDataContext().SaveChanges();
if (resetAfterCommit)
{
DataContextFactory.Clear();
}
}
ASP.NET N-Layered Applications - Importing Data Using the API (Part 9)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 150 of 162
Stuff I Like to Do
With an import tool like this one, I am not overly concerned with testing or dependency injection. Most of the
testing comes down to integration testing anyway (where you import real source files and validate them
directly in a real database) since the import code is pretty straightforward (and thus testing mainly comes
down to making sure the data lands where it should). This means I dont mind instantiating classes like
PeopleRepository directly in the Main method.
I do use AutoMapper for applications like this, as it makes it really easy to convert from one type to another. In
this example, I could have used methods like ResolveUsing to fix the problem with the non-matching contact
type column. I chose not to do this to show you another alternative. However, using AutoMapper for the
conversion would be similar to how the Address objects were filled with data from the ImportPerson class.
I do like to keep an eye on performance. Often these applications are used only once to do the final import and
then it doesnt really matter if it takes 20, 30 or 120 minutes to complete. However, by analyzing the
performance of the application you may find areas that can be improved for other scenarios as well, leading to
a better overall performance.
Summary
In this article you saw how to import contact data from a CSV file using the repository and model classes introduced in
earlier parts of this article series as well as an external library called FileHelpers. With FileHelpers, importing a CSV file
is really straightforward. All you need to do is define a data class that will hold the data coming from each row, and
then write some code to loop over all the rows in the source file using the FileHelperEngine and
FileHelperAsyncEngine classes. I use AutoMapper to convert the imported type into a Person model instance which I
can then feed into my repository.
At the end I discussed a few performance implications that come to light with an import application like this one.
Performance issues may go unnoticed when dealing with only a few entities, but they become more apparent when
dealing with thousands of them.
Links in this Document
(1) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part09/Figure9-2_CommandlineTool_Output.png
(2) http://www.red-gate.com/products/sql-development/sql-data-generator/
ASP.NET N-Layered Applications - Importing Data Using the API (Part 9)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 151 of 162
ASP.NET N-Layered Applications - Extensions, Tools and
Wrapping Up (Part 10)
This is Part 10 in a series of 10 that show you how to build N-Layered applications using ASP.NET 4.5 and Entity
Framework 5 Code First. In this part Ill discuss a few miscellaneous topics that I havent touched in previous articles
in the series.
Possible Extensions
In this section Ill discuss a few possible extensions to the sample application. I may update this section in the future if
I get questions from readers asking how to implement specific functionality.
Implementing Security
In my previous article series on building N-Layered applications with ASP.NET I spent an entire article on the topic of
security
(1)
. In the new series I havent touched on this subject at all. The reason for this is that not much has
changed and most of the concepts discussed in that article still apply.
Note that when youre using MVC as your frontend, you can also make use of the authorization attributes such as
Authorize and AllowAnonymous, to control access to the action methods in your controllers.
For more details, check out the following links:
http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-
new-allowanonymous-attribute.aspx
http://www.davidhayden.me/blog/asp.net-mvc-4-allowanonymous-attribute-and-authorize-attribute
Implementing Concurrency
Although not implemented in the sample code, implementing concurrency handling is relatively straightforward. Heres
a high-level description of the steps involved to implement concurrency handling in the MVC frontend:
Add a Version property to your entity of type byte[].
Configure EF to treat that property as the row version of your entity.
Modify the View Models and views to support the Version property. When youre editing an entity, you need to
store the row version in a hidden field. Then when the page is posted back, you get the version number from
the hidden field and assign it to the entity that is being edited. Then when the changes are saved, EF will use
that number and compare it against the version in the database and throw a concurrency exception when the
two values dont match. Theres a caveat you need to be aware of when assigning the row version as youll see
in the description of step 5.
When saving changes to an existing entity, make sure it uses the correct version number.
Handle the error in the UI.
Ill describe each of these steps in more detail next.
Add a Version Property to Your Entity
You can add the property to each individual entity, or to the base class DomainEntity. I usually add it to individual
entities as not all my entities need concurrency handling. I also create an IHandlesConcurrency interface so I can tell
whether an entity handles concurrency or not. This is useful in step 3 as youll see soon. For the Person entity, I could
end up with something like this:
1
2
3
4
5
6
7
8
9
10
11
12
public interface IHandlesConcurrency
{
byte[] Version { get; set; }
}
...
public class Person : DomainEntity<int>, IDateTracking, IHandlesConcurrency
{
// Other members go here
public byte[] Version { get; set; }
}
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 152 of 162
Configure EF to Treat that Property as the Row Version of Your Entity
Again, this is simple to do as the Fluent API has a handy IsRowVersion method. Inside my PersonConfiguration
class I can add the following code:
Modify the View Models and Views to Support the Version Property
First, you need to add a Version property to the CreateAndEditPerson class.
You then need to store the value of the Version property in the view. Heres an example from the Edit view:
When Saving Changes to an Existing Entity, Make Sure it Uses the Correct Version Number
This is a bit of a weird step. When you configure the Version property as the RowVersion, EF treats this field as store
generated. That means that when it needs to send its value to the database, it will use the original value, not the
value that has been retrieved from the hidden form field (even though that has been assigned as the new value for the
Version property when mapping from a CreateAndEditPerson to a Person instance in the Edit action method). To
work around this, you can tell EF to use the current value rather than the original value. Heres where the
IHandlesConcurrency interface comes in handy. In SaveChanges you can find all entities that implement this interface
and then reassign the Version number:
This way, the version retrieved from the form is used in the concurrency check.
Handle the Error in the UI
Finally, you need to handle the error in the UI. How you do this is up to you; you can intercept the problem by using a
catch block for a DbUpdateConcurrencyException. Then inside that block you can tell the user what went wrong and
offer them a chance to reload the changes from the database or force their new values to be submitted to the
database.
Using Red Gates SQL Data Generator to Generate Relevant
Sample Data and Find Performance Issues
When building an application like this, its useful to have good sample data. While you certainly can (and should) insert
data using unit tests, there are times where you need more data. For example, when doing performance testing or
profiling, it really helps to stuff your tables with tens of thousands of rows to reveal any issues with indexes etc. One
solution is to use the Seed method of your database initialization class. Writing a quick loop that adds a lot of entities
is easily done.
Another alternative that I often use is Red Gates SQL Data Generator
(2)
. Using this tool you can generate sample
data for your database. The price for the tool as a stand-alone version is 220 but it also comes bundled in the SQL
1
2
3
4
5
6
7
8
public class PersonConfiguration : EntityTypeConfiguration<Person>
{
public PersonConfiguration()
{
Property(x => x.Version).IsRowVersion();
// Other code here
}
}
1
2
3
4
5
6
@using (Html.BeginForm())
{
... Other code here
@Html.HiddenFor(model => model.Version)
... Other code here
}
1
2
3
4
5
6
7
8
if (item.State == EntityState.Modified)
{
var supportsConcurrency = item.Entity as IHandlesConcurrency;
if (supportsConcurrency != null)
{
item.Property("Version").OriginalValue = item.Property("Version").CurrentValue;
}
}
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 153 of 162
Developer Bundle and the SQL Toolbelt packages. What I like about this tool is that it can generate data that looks as
real as possible. The tool has smarts built-in to recognize columns by their name and type and then recommends the
appropriate sample data. For example, when using the tool to generate data for the People table, it recognizes the
FirstName and LastName column (and many others as well) and suggests to fill them with sample data contain names.
Figure 10-1 shows an example:
(3) See Links in this Document at the end for the full URL of this image.
Figure 10-1 Red Gates SQL Data Generator
Using this tool, inserting a million rows in the People table takes less than a minute. With this large amount of sample
data, I was able to find a few areas where I could improve performance. For example, if you sort the grid with all the
people based on their ID, youll notice this is really fast as the ID has been set up as a (clustered) index as its also the
primary key. However, when you try to sort on the full name of date of birth columns youll notice performance is not
great. In this example it was easy to guess what the solution is - (add indexes for the first name, last name and date
of birth columns to the database) but for more advanced scenarios having a good and large data set will help you find
issues in your code and database. To make sure these indexes get added to the database even when it is regenerated
after the model changes, you can execute the following code from a custom database initializers Seed method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyInitializer : DropCreateDatabaseIfModelChanges<ContactManagerContext>
{
protected override void Seed(ContactManagerContext context)
{
context.Database.ExecuteSqlCommand(@"
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20130505-160821] ON [dbo].[People]
(
[DateOfBirth] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)");
context.Database.ExecuteSqlCommand(@"
CREATE NONCLUSTERED INDEX [NonClusteredIndex-20130505-160727] ON [dbo].[People]
(
[FirstName] ASC,
[LastName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)");
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 154 of 162
Now whenever the database is recreated two indexes for the first name, last name and date of birth columns are
created automatically.
For more advanced scenarios you can also use SQL Servers query plan analyzer to figure out how to best optimize
your database.
For more information about setting up Red Gates Data Generator, check out these resources:
http://imar.spaanjaars.com/523/avoiding-the-works-on-my-machine-syndrome
http://documentation.red-gate.com/display/SDG20/SQL+Data+Generator+2.0+documentation
Using Entity Framework Profiler from Hibernating Rhinos
Another great tool to help you with performance problems is the Entity Framework Profiler from Hibernating
Rhinos
(4)
. This tool gives you valuable insight and perspective into [the] usage of Entity Framework by analyzing
your queries during run-time. It can help you find common problems like Select N+1
(5)
and it provides tips to help
you solve them. The tool costs $287.00 for a yearly subscription or $389.00 for a standard license. Entity Framework
Profiler is also available in the Uber Profiler
(6)
that contains profilers for other ORMs as well.
Using Entity Framework Profiler is straightforward and involves a few simple steps:
Download the Entity Framework Profiler.
Add a reference to one of the applications assemblies.
Write some startup code in your applications Gobal.asax file (or other startup file for non-web projects).
Start the profiler.
Analyze the results.
Ill describe each of these steps in the following sections.
Download the Entity Framework Profiler
You can download the Entity Framework Profiler from its products page at http://www.hibernatingrhinos.com/products
/EFProf. Once youve downloaded the application you can request a trial key from this page:
http://www.hibernatingrhinos.com/products/efprof/trial which will be sent to you by e-mail. You dont need to install
anything, all you need to do is extract the ZIP file to a convenient location such as C:\Tools\EFProf.
Add a Reference to one of the Applications Assemblies
In order to see profiling data in the tool, you need to add a reference to the
HibernatingRhinos.Profiler.Appender.dll assembly (from the EF Profiler directory) to the application youre
profiling. This can for example be your MVC or Web Forms project or your Integration tests project. See the file How to
use.txt in the applications main folder for more details about using the profiler in a production environment.
Write Code in your Applications Startup File
Next, you need a single line of code to start the profiling. For a web project you need to add the following line of code
to the Application_Start handler in Global.asax:
If youre profiling your integration tests, you could add this code to the constructor of the IntegrationTestBase class.
Start the Profiler and Your Application
Next, from the EF Profiler folder, startup EFProf.exe. Then start up your application (browse to a web project or run
the unit tests) to see the results in the profiler as shown in Figure 10-2:
1 HibernatingRhinos.Profiler.Appender.EntityFramework.EntityFrameworkProfiler.Initialize();
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 155 of 162
(7) See Links in this Document at the end for the full URL of this image.
Figure 10-2 Entity Framework Profiler
Analyze the results
In Figure 10-2 you can see the results from browsing to the main list of people. The Alerts column on the right is
empty which means that the EF Profiler hasnt found any problematic queries. However, to show you how this works,
lets introduce an issue in the FindById method in the base Repository<T> class. It currently looks like this:
In order to introduce a (major) issue, I changed the code to this:
Notice the inclusion of ToList, a common mistake made by developers. ToList causes an immediate execution of the
query which means that all rows are retrieved from the database into EF. The SingleOrDefault method is then
executed against the in-memory collection of objects, not on the database. You wont notice this with a few rows in the
database, but this surely breaks or causes bad performance with thousands or more rows. EF Profiler flags this issue
as follows:
1
2
3
4
public virtual T FindById(int id, params Expression<Func<T, object>>[] includeProperties)
{
return FindAll(includeProperties).SingleOrDefault(x => x.Id == id);
}
1
2
3
4
public virtual T FindById(int id, params Expression<Func<T, object>>[] includeProperties)
{
return FindAll(includeProperties).ToList().SingleOrDefault(x => x.Id == id);
}
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 156 of 162
(8) See Links in this Document at the end for the full URL of this image.
Figure 10-3 Entity Framework Profiler Highlighting Issues
The profiler is flagging two issues. It has noticed that your query uses an unbounded result set and that a large
number of rows are returned. The first issue is caused by the ToList method. This executes a query against the
database without a WHERE clause, which means all rows are returned from the database. In this case, the second issue
is a directly related to the first: since the table contains many rows and there is no restrictive WHERE clause, a large
number of rows are returned to the calling application. You may also see these alerts independent of each other: if you
query a large table with a WHERE clause thats not very restrictive (for example Id > 1) you would still see the first
alert. A small table that is being queried without a WHERE clause would cause the second alert to appear in the tools
Alerts column.
While these tools are great to help figure out problems in the code and setup of your application, you may also have
the need to find out issues in your application while its running in production. To gain more information about what
your site does at run-time you can implement a logging strategy, discussed next.
Using NLog for Extensive Logging
Its not uncommon that one of your applications in production suddenly doesnt behave as expected. With the many
layers in a layered application it may not always be easy to figure out whats going. Imagine you have an external
application that accesses your WCF service but receives null when asking for a specific contact person. On a
production server, it may be difficult to track down whats going on. Is the calling application making a mistake by
passing in an ID that doesnt represent an existing person? Or maybe the person is deleted from the database while it
shouldnt have been? Or maybe someone accidentally deployed a web.config file that targets the staging server
instead of the production server. Investigating an issue like this can be quite time consuming.
You can take out a lot of the guesswork if your application logs its activities into a log file. Then whenever something
unexpected occurs, you can check the log file to see whats going on. Logging is really easy to do using an existing
logging framework such Log4Net
(9)
, the logging framework thats part of the Microsoft Enterprise Library
(10)
,
NLog
(11)
or others. For a feature by feature comparison of some these frameworks, check out
http://www.dotnetlogging.com/comparison/. Note: the information you find here is biased a bit, as this site is owned
by Gurock Software, the company that makes the commercial SmartInspect logging framework. Take it with a grain of
salt.
The option I usually choose is NLog. Its free to use (its licensed under a BSD license), its easy to configure and yet
its powerful enough to fulfil all of your logging needs. I usually reference the NLog library directly in my application (I
set it up using NuGet) and write code directly against the Logger classes in the NLog namespace. Although this creates
a dependency between my application and NLog, I am usually OK with that. It doesnt interfere with Unit Testing (as
youll see soon) and I usually dont have the need to swap out NLog for another framework. If you do have that need,
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 157 of 162
you could create (or use an existing) interface for your logging methods and then inject a concrete log implementation
at runtime using your DI tool of choice. More information can be found here:
http://netcommon.sourceforge.net/
http://stackoverflow.com/questions/5646820/logger-wrapper-best-practice
Configuring and using NLog is easy as youll see in the next section. In addition, since configuration can be done in the
applications config file, its easy to use config transformations to have different settings for development, test and
production environments. This makes it easy to turn on and off logging completely for your unit tests for example. In
the next steps, you see how I can add logging to my WCF service project and then configure the web.config file to log
minimal information in production and lots of data during development. If you ever have the need to diagnose a
problem at run-time, just modify the configuration code and specify a different log level.
I started by installing NLog in my WCF service project using the following NuGet command:
I then added the following line of code at the top of the ContactManagerService class to declare and
instantiate a logger object:
Using the Logger instance I can now log with NLog using a single line of code. Note that NLog has multiple
methods for logging such as Warn, Trace, Error and more. Check out the documentation at https://github.com
/nlog/NLog/wiki for more information. The following code block shows some of the log messages you could add
to your service methods:
1 Install-Package NLog
1
2
3
4
5
6
7
8
namespace Spaanjaars.ContactManager45.Web.Wcf
{
public class ContactManagerService : IContactManagerService
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
// Other code here
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ContactManagerService : IContactManagerService
{
private readonly IPeopleRepository _peopleRepository;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public ContactManagerService(IPeopleRepository peopleRepository,
IUnitOfWorkFactory unitOfWorkFactory)
{
if (peopleRepository == null)
{
Logger.Error("peopleRepository is null.");
throw new ArgumentNullException("peopleRepository", "peopleRepository is null.");
}
if (unitOfWorkFactory == null)
{
Logger.Error("unitOfWorkFactory is null.");
throw new ArgumentNullException("unitOfWorkFactory", "unitOfWorkFactory is null.");
}
_peopleRepository = peopleRepository;
_unitOfWorkFactory = unitOfWorkFactory;
}
public PersonModel GetPerson(int id)
{
Logger.Trace("Getting person: {0}", id);
var person = _peopleRepository.FindById(id);
Logger.Trace("Person with ID: {0} is {1}null.", id, person == null ? "" : "not ");
return Mapper.Map(person, new PersonModel());
}
}
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 158 of 162
In this sample code I am using the Error method to log exception cases, and Trace to log additional information. This
way, an error like a missing repository can always be logged while the additional information is only logged when you
configure NLog to log trace message as well.
The next step is to add config information to the application to tell NLog what to log and where. NLog supports
three main ways to configure it: through its API (https://github.com/nlog/nlog/wiki/Configuration-API),
through an external config file and with configuration code embedded in the applications main file. For the
latter two solutions, check out https://github.com/nlog/nlog/wiki/Configuration-file. I prefer to embed the code
in my main configuration file as it makes it easier to use Web Config Transformations to generate different
versions of the file for different deployment targets. To enable logging, you can add something like the
following to the top of the config file
If you now call one of the service methods (for example, GetPerson) youll see something like this in the log file:
Since no exceptions are raised, you only see the Trace messages, If you disable that trace level by setting the
minLevel to Info, you wont see anything at all in the log file for normal operations, Exceptions are still logged
though; this is a great setting for a production server:
If you want to use the log file from your unit test project as well you need to add the NLog configuration code
to the unit test projects app.config file. In addition, because of the way unit tests are executed, you need to
supply an absolute path to the log file, like so:
The final tool I often use when building database-driven web applications is Red Gates SQL Compare, discussed next.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
</configSections>
<nlog xmlns=http://www.nlog-project.org/schemas/NLog.xsd
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="file" xsi:type="File" fileName="${basedir}/App_Data/Log.txt"
layout="${longdate} ${callsite} ${level} ${message} ${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
</nlog>
1
2
3
4
2013-05-06 17:18:02.3084 Spaanjaars.ContactManager45.Web.Wcf.
ContactManagerService.GetPerson Trace Getting person: 40
2013-05-06 17:18:02.3535 Spaanjaars.ContactManager45.Web.Wcf
ContactManagerService.GetPerson Trace Person with ID: 40 is not null.
1 <logger name="*" minlevel="Info" writeTo="file" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
</configSections>
<nlog xmlns=http://www.nlog-project.org/schemas/NLog.xsd
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="file" xsi:type="File"
fileName="C:\Projects\Spaanjaars.ContactManagerV45\Main\Logs\WcfTests.txt"
layout="${longdate} ${callsite} ${level} ${message} ${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
</nlog>
</configuration>
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 159 of 162
Using Red Gates SQL Compare to Update your Live Database
Earlier in this series I mentioned that I usually dont use EF migrations in a production environment. Often DBAs wont
grant permissions to your application to modify the database schema at run-time. Instead, when doing a deployment,
they typically want you to supply a SQL file with DDL (Data Definition Language) statements that update the database
to the latest schema. While you could use the command line options of EF Migrations to create those scripts, I usually
find it easier to compare the latest development database with the production environment using a tool like SQL
Compare from Red Gate
(12)
. Just like Data Generator, SQL Compare is a commercial tool with a price that starts
around 295. It also comes bundled in the SQL Developer Bundle and the SQL Toolbelt packages.
In order to compare the databases, you need access to both the source and target database from the machine that
runs SQL Compare. For a production environment that may not be feasible. In that case, its recommended to restore
a backup of production on your local or networked machine and use that for the compare operations.
To explain how to use SQL Compare, I added a new property called NickName to the Person class in the EF model:
I then ran my unit tests which in turn recreated the database so the Person table now also has a NickName column.
To perform a compare operation and script the changes, I started SQL Compare and then configured the source and
target databases. In Figure 10-4 you can see I am comparing two databases running on my local machine.
(13) See Links in this Document at the end for the full URL of this image.
Figure 10-4 Red Gate SQL Compare Configure Databases
By clicking Compare Now, SQL Compare will analyze both databases and then displays a window with the results,
shown in Figure 10-5:
1 public string NickName { get; set; }
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 160 of 162
(14) See Links in this Document at the end for the full URL of this image.
Figure 10-5 Red Gate SQL Compare Results
Notice how it picked up the new column, highlighted in the T-SQL section at the bottom. Next, I selected the People
table and then clicked Deployment Wizard on the main toolbar. This triggers a dialog that lets you choose between
generating a .sql script file (that can be used to update the target database at a later stage) or immediately update
the target database. The former case is useful if you need to run the SQL script on another database or on multiple
databases, or if you dont have direct access to the target database. The Deploy Using SQL Compare option will
directly modify the target database, bringing the two databases in sync.
This example is somewhat contrived as its a relatively simple change to the database. You wont be able to make
some complex changes such as adding a non-nullable column to an existing table as SQL Compare currently has no
way to specify a default value to use in the upgrade process. As a solution you could add a default value to the column
in the source database and then run the Deployment Wizard. Alternatively, you can let the tool generate the SQL file
for the update process and then manually update it to supply a default value. Hopefully Red Gate adds support for
scenarios like this to SQL Compare in a future release.
Summary
In this article youve seen a number of extensions you could add to the sample application as well as number of tools
and frameworks that I often use when building database-driven, N-layered web applications.
Although Ive tried to make the sample solution as rich as possible, there are a number of features I did not include. In
this article, I briefly mentioned a few and provided pointers on how to implement them yourself.
The article then discussed a few tools to create a great set of sample data (Data Generator), profile your Entity
Framework queries (Entity Framework Profiler) and generate scripts to update production databases (SQL Compare). I
also discussed the NLog framework that enables you to log information from your application during run-time in order
to find and diagnose issues with your application.
This article also includes the series Building N-Layered Applications with ASP.NET 4.5. Over the past 10 articles, youve
seen how to build an N-layered application using Visual Studio 2012 and (ASP).NET 4.5. In the first five articles youve
seen how to design and set up your solution, how to make your solution unit testable, how to design your applications
domain model and how to build an entity framework implementation of the IRepository interface to work with data
from a SQL Server database in the frontend application.
Article 6 to 9 discussed various ways to implement a frontend. These articles showed you how to build ASP.NET MVC 4
and ASP.NET Web Forms 4.5 frontends, as well as a WCF service application and a command line tool to import
existing data. Each of these frontends have their own requirements and challenges which results in different
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 161 of 162
implementations of using the same repository layer in the solution.
The series closed off with a quick look at some of the tools that come in handy when building database driven
applications.
I hope you enjoyed reading the series as much as I enjoyed writing it. During the writing process I learned a ton of
new things on all the different technologies I am describing in these articles which in turn enabled me to refine the
processes and techniques I am using in my clients applications. I do realize that this article and concepts are just a
point in time. The software landscape changes rapidly so concepts that make sense today might be outdated
tomorrow. I wont be updating this series (much) over the coming time but I do look forward to writing an updated
series in a year or two, using techniques and technologies that are current by then.
If you have any comments or questions about this article series or the sample solution, post them at the end of the
article that deals with your question or get in touch with me through the Contact
(15)
page.
Thanks for reading, and happy N-layering!
Links in this Document
(1) http://imar.spaanjaars.com/481/n-layered-web-applications-with-aspnet-35-part-6-security
(2) http://www.red-gate.com/products/sql-development/sql-data-generator/
(3) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part10
/Figure10-1_Red_Gate_SQL_Data_Generator.png
(4) http://www.hibernatingrhinos.com/products/EFProf
(5) http://www.hibernatingrhinos.com/products/efprof/learn/alert/selectnplusone
(6) http://www.hibernatingrhinos.com/products/UberProf
(7) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part10/Figure10-2_EF_Profiler.png
(8) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part10/Figure10-3_EF_Profiler_Showing_Issues.png
(9) http://logging.apache.org/log4net/
(10) http://msdn.microsoft.com/en-us/library/ff647183.aspx
(11) http://nlog-project.org/
(12) http://www.red-gate.com/products/sql-development/sql-compare/
(13) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part10/Figure10-4_Red_Gate_SQL_Compare.png
(14) Image: http://imar.spaanjaars.com/Images/Articles/NLayer45/Part10
/Figure10-5_Red_Gate_SQL_Compare_Results.png
(15) http://imar.spaanjaars.com/contact
ASP.NET N-Layered Applications - Extensions, Tools and Wrapping Up (Part 10)
Imar Spaanjaars - http://imar.spaanjaars.com - 2013 Page 162 of 162