Home > Uncategorized > Part 2: Building The Basics: Silverlight 3, .NET RIA Services, & NHibernate (Fluent)

Part 2: Building The Basics: Silverlight 3, .NET RIA Services, & NHibernate (Fluent)

October 4th, 2009

UPDATE (February 22, 2010) : a newer release of Silverlight and RIA Services broke my code. Shaun was kind enough to make the necessary changes and let me know and you can find his post on the fixes and the code here.

Download the (original) source here.

Silverlight is a web application framework that provides a very solid foundation for building Rich Internet Applications (RIA) that are cross-browser compatible, and can run on Mac OS, Windows, and Linux. In general, any Line of Business (LoB) type application should have multiple tiers to separate the different business and development aspects of the project.  “Microsoft .NET RIA Services simplifies the traditional n-tier application pattern by bringing together the ASP.NET and Silverlight platforms. The RIA Services provides a pattern to write application logic that runs on the mid-tier and controls access to data for queries, changes and custom operations. It also provides end-to-end support for common tasks such as data validation, authentication and roles by integrating with Silverlight components on the client and ASP.NET on the mid-tier.” – Microsoft Marketing Blurb. So with Silverlight and .NET RIA Services we have set up a very solid foundation for creating a LoB Web Application. However, what about the database? Any large or even medium size application (web or standalone) more then likely needs a database. The problem is that relational databases and object-oriented programming (OOP) have very different views on data. Data in OOP is represented by objects, and those objects can contain scalar values (such as integers and strings) as well as other objects. Databases, however, typically only store scalar values. There is no direct way to map objects to databases. The solution: we use a Object-Relational Mapping (ORM) framework (in my case NHibernate) to bridge this gap and allow the database schema to be defined based off the software’s business entities. NHibernate lets you build up the database as you go, because, in my opinion, in a large-scale application, it is not possible to have a solid database implementation based on the requirements alone. Requirements change, design ideas change, perspectives change. Why would you spend so much time up front on the database when it is at the beginning of development that requirements are more likely to change. Also, to me, it doesn’t make sense to have to wait for the database to start development. Start developing, and let the database get built up based on your development. To make minor changes you make them once in code and your DB schema automatically (in a sense) adapts to what you are doing in code. So this way your not working around your database, you are making your database work around you. Makes sense to me. But what do i know? So bring Silverlight, .NET RIA Services and NHibernate together we have set up an environment that i believe will help the application become very stable, maintainable, and a joy to work in. So lets see what i have done so far. I have attempted to put together a “skeleton” of what I think a Silverlight project should look like. Keep in mind that I’m new to Silverlight, .NET RIA Services, and NHibernate so my views might not be the best ways of doing things. But i definitely appreciate feedback and any ideas on improvement.

LoB applications have data that needs to flow from the database (data layer) to the user interface (UI layer) and back. Ideally, data validation should be done in two places, at both the UI layer and at the data layer. The UI should warn users if they are entering invalid data (but should not have to go to the database to know what constitutes valid) and the database should not trust that the UI is giving it valid data. So a (terribly) common practice is to write your validation in two different places, two different ways. What RIA does is it allows the user to write validation and “server” logic in ONE place, and exposes it to both the client and server components in a way that all components can take advantage of it. This makes the application more stable and maintainable then an application that has code that does the same thing written twice. It also keeps your data access logic out of your client, which is the way it should be.

NHibernate

Originally, when setting up NHibernate the user would create the entity classes and then configure everything (mapping fields, relationships, etc) via XML files. Fluent NHibernate provides a framework for writing the mapping information in strongly typed C# code using the fluent interface. Lets look at the Contact entity and its corresponding mapping class.

ContactEntity

Figure 1- Contact Class

The first thing you will probably notice is that Contact derives from Entity. Entity is just an object that has an EntityId property as well as overridden Equals and GetHashCode methods. Contact is a simple class that has various properties. No big deal. You will notice, however, that there is one or more attributes with all of the properties. This is not a requirement, but one of the powerful tools to take advantage of.  This automatically applies client side and server side validation as we look at later.

The corresponding Fluent NHibernate class map is shown in Figure 2.  ContactMap maps each property and tells NHibernate that the corresponding property is to be mapped to a certain property in the database (which NHibernate will build for you). Mapping is done by passing in lambda expressions to the Fluent NHibernate’s mapping methods.                                        Map(System.Linq.Expressions.Expression<System.Func<T,object>>) maps a simple property. There are different methods used for different mapping situations. For example, Contact has a list of Addresses. In a database we would have a table that represents all Contact properties/fields, and a table that represents an Address. In the object-oriented world we say that a Contact “has many” Addresses and we represent that by making Contact keep a list of all of its own Addresses. However, on the other side of the fence, in the relational database world, we represent a Contact relationship by having a foreign key in the Address table. So each address points back to the contact that is associated with that address. So in the database it is the address’s responsibility to keep track of which contact it belongs to. Quite a difference between the two data models (which is why, obviously, one reason why we need an Object-Relational-Mapping Framework). In order to transparently handle this difference, we use the HasMany method. This allows NHibernate to automatically set up a foreign key column in the Address table for the Contact it is associated with. We are also telling NHibernate to not use lazy loading, and to set cascading to all. Lazy Loading is when the object, or its members are not initialized/retrieved (via hitting the database) until the point of which it is necessary. This is useful for large structures that might take a significant amount of time to load, but whose class members aren’t necessarily needed right away. It provides a quicker way to get necessary data without loading EVERYTHING at once. Cascading refers to the idea that every object, or entity, has associations with other entities, (one to many, many to one, etc) and when updating [to the database] one object, for example, Contact, you may or may not want to update all the associated entities. Cascade.All() tells NHibernate that when an operation is done against an object (save/update/delete), check the associations and save/update/delete all the objects found that belong to or are encapsulated within Contact. So in this case, deleting a Contact object will result in deleting all the Addresses associated with that object, which makes sense because an Address has a Contact as a foreign key, and does not make sense to keep if it has no associated Contact.

ContactMappings

Figure 2- ContactMap Class

DomainMap

Figure 3- DomainMap Class

Conventions

A “convention” in the way that I am about to talk about it is NHibernate’s way of setting options on database fields (unique, not null, length requirements, etc).  NHibernate provides the means to define conventions via a series of interfaces of varying degrees of granularity; any classes implementing any of the interfaces will be hooked into the mapping generation cycle via a method call. Because each convention is an interface, it means you can implement multiples of them in a single class, which allows the grouping of common conventions into a single class if desired. Lets look at a simple example. UniquePropertyConvention inherits from the AttributePropertyConvention class which is the base class for attribute based conventions. As you can see you simply override the Apply method. The first parameter is the attribute class that was passed in as a generic parameter and the second is a reference to the current instance that the convention is being applied to. It exposes many methods and properties that one can set to configure any property (or field in the database) that is marked with the corresponding attribute. So in our example, in Figure 4 below, UniquePropertyConvention is linked with the UniqueAttribute, so any property that contains this attribute is marked as “Unique” by NHibernate and enforced in the database schema.

conventions

Figure – 4

If we look at Figure – 1, we see that there is a “Required” attribute applied to all properties, as well as a “StringLength” property applied to FirstName and LastName. Here is the generated table definition.

create table [Contact] (
Id  integer,
Gender TEXT not null,
FirstName TEXT not null,
LastName TEXT not null,
BirthDate DATETIME not null,
primary key (Id)
)

As you can see there is a “not null” option applied to each field. This comes from one of the custom NHibernate’s conventions defined in the project. You will also notice that there is no string length option for FirstName or LastName. This is because the TEXT data type in SQLite has no length option.  If we were to use SQL Server we would have seen this applied. However, we still want to apply this attribute because it will still be recognized and enforced on the client side, as you will see.

NHibernate Sessions

ISession is the primary interface used by NHibernate applications for database operations. It exposes methods for finding, saving, updating, and deleting entities. A session can be thought of as something between a connection and a transaction. Think of a session as a cache or collection of loaded objects related to a single unit of work. It is very inexpensive to create or destroy and is usually created and then discarded when needed.

An ISessionFactory, on the other hand, is not lightweight, and is intended to be shared among the entire app as a single (typically static) instance. It caches generated SQL statements and other mapping metadata for NHibernate and is what creates sessions via the OpenSession() method. Figure 5 shows the implementation of the NHibernateSessionFactory, which encapsulates the ISessionFactory.

NHibernateSessionFactory

Figure 5 – NHibernateSessionFactory Outline

As you can see, there is a static (readonly) property that contains the ISessionFactory object. This property checks to see if the _sessionFactory member is null, and if it is, it creates a new one using the CreateSessionFactory method shown in Figure 6.

CreateSessionFactory

Figure 6 – CreateSessionFactory Method

This method takes a single Boolean parameter, needToBuildDatabase, which, as the name suggests, specifies whether or not we need to build the database (for testing). So my solution calls this in two places, once in a test, where I build up the database and populate it with default values (and therefore, pass in true to this method), and the other is described above in the SessionFactory property where it is passed a value of false. Both BuildDatabase() and the last return statement in CreateSessionFactory() use the Fluently.Configure() method to start fluently configuring NHibernate and eventually build the ISessionFactory using … can anyone guess… that’s right.. the BuildSessionFactory() method. From Figure 7, you can see the implementation of the BuildDatabase() method.

builddatabase

Figure 7 – BuildDatabase Method

As stated before, Fluently.Configure() fluently starts the configuring of NHibernate and this is where the user specifies the mappings, conventions, etc. This is also how we export the end result NHibernate configuration xml files (using ExportTo(string pathToExportTo)) and the building up of the database (using Export() method that is apart of the NHibernateSessionFactory class). So basically how I have set this up to work is when you are running tests, it wipes out the database and builds it up from scratch, inserts dummy data, and then I test to verify that the data i expect to be there is, in fact, there. If you are simply running the application, it will only build the ISessionFactory, it will not rebuild the database.

Domain Service

A DomainService is basically a class that encapsulates server side logic and database services that is eventually projected to the client side, allowing the user to centralize server (or service) logic and keep that logic out of the client side code. Lets look at my custom DomainService class in Figure 8.

BusinessAppDomainService

Figure 8 – BusinessAppDomainService

This domain service contains all the logic necessary to communicate with the database and perform the CRUD (Create, Read, Update, Delete) operations. There are naming conventions and requirements but that is out of the scope of this blog and can be read in the RIA documentation (RIA Services Overview). This class uses the current NHibernate session object (which is created by NHbernateSessionFactory) for these operations. As you can see NHibernate provides a simple interface for reading/updating from the database. You can perform Linq operations on the data as well.

As I described in the previous post, when the project is built, a DomainContext class is created, which is basically a client side proxy to the DomainService class. Please examine this file (\Generated_Code\BusinessApp.Web.g.cs) in the BusinessApp project to see the code generated. It is pretty self explanatory. RIA services also adds quite a bit of code for you. Check it out.

DataGrid

datagriddddddd

Figure 9 – DataGrid Example

This is pretty straightforward. We define a DataGrid and set some of its properties. We explicitly define each column (this isn’t a requirement but the column automatically takes the database field name if not, so instead of displaying “FirstName” we want it to display “First Name”, etc). We bind each column to a field in the database table we are binding to. At the last minute i stuck in another field, BirthDate because i wanted to demonstrate how to define and use “Converters”, which allows the programmer to define how to represent the data. So for example, the DataGrid displays a DateTime object by calling the ToString() method on it, which is probably not what we want. So how do we take any object and tell the DataGrid how to display it? We use an IValueConverter, seen in Figure 10.

ivalueconverter

Figure 10 – DateConverter

The IValueConverter interface defines two methods: Convert and ConvertBack. So we are basically creating an object that defines a way to convert an object from one specific type (DateTime) to another that the control is expecting (String) and back. So to convert it to a String I simply build up the format that i want the user to see in the DataGrid. To convert it back to the a DateTime object i use a regular expression to extract the data from the format that I defined and am expecting and then use that data to build the original DateTime object. I’m sure there is a better implementation (and feel free to share), this was just thrown together.

So going back to Figure 9 we see that I used DataGridTemplateColumn.CellTemplate object to bind the column to the BirthDate field (which is a DateTime object) and use the custom converter to display the DateTime string how I wanted.

You will also see from Figure 9 that I defined a simple DataPager and hooked it into our DataGrid. This does all the work for us and if you run the app you will see that we get a DataGrid that displays ten Contacts at a time. In the DomainDataSource, whose definition is not shown, but can be viewed in the Contacts.xaml file, you can see that I specify the LoadSize property to be 20, which tells RIA to only pull 20 Contacts at a time from the database. This improves performance significantly, and is a must when working with large databases. So if you run the app, go to the “Contacts” page, let the data load (we are hitting the database here), and then go to the next page, it will not hit the database again, but will instantly load the next ten Contacts. However, going to the next page (3) forces the service to go back out and grab the next 20 contacts.

.NET RIA Services also provide the hookups for automatic client side validation via the attributes that we set in our Entity classes. So applying the RequiredAttribute on a property not only applied the validation against the database, but also on the UI. See Figure 11.

watever

Figure 11 – Client Side Validation

This is extremely awesome.  I didn’t write anything else to make this message pop up. You can change the error message by specifying within the attribute you are using with the ErrorMessage property. For example, (see Figure 12)  if i wanted to use a regular expression for error validation, I could define my regular expression and then use the ErrorMessage property to explicitly define what the DataGrid will display if that particular validation fails. The same thing applies for the RequiredAttribute and many others.

regex

Figure 12 – Using Regex for validation

How is all of this done? Lets turn on all exceptions in Visual Studio (Debug –> Exceptions) and then type more then 30 characters into one of the FirstName fields in the DataGrid and then try to click away or press enter. We can see that the FirstName property (seen in Figure 13) of the Contact class DEFINED IN THE BusinessApp.Web.g.cs file (via projection from server) has an exception thrown when validating the property. The exception says “The field FirstName must be a string with a maximum length of 30”. So when I hit enter or try to click away from the DataGrid, the binding of the FirstName property kicks in and it tries to set this property. The first thing the setter does is validate that the value passed in doesn’t break any rules that I have applied (Required,StringLength).  This is all RIA generated code that we get for free.

validationexception

Figure 13 – ValidationException within FirstName

Conclusion

I believe we know have a solid foundation to start with in our Silverlight, .NET RIA, and NHibernate application. I started with the with the backend and showed how to build up the SQLite database from within the code, letting your database work around you, instead of you working around your database. I then showed how all this was configured and how it tied into the RIA Services, and finally, how it was all automagically projected to the client side.

Thanks to Jason Morse who helped me get going on a lot of this and whose code some of my code is based on :)   Also thanks to Brad Abrams and his post whose code was also the basis of some of mine as well.

Author: Shane Kercheval Categories: Uncategorized Tags:
  1. MylesRip
    January 21st, 2010 at 11:08 | #1

    Thanks for the great post, Shane! I’m looking at basically the same combination of technologies as you settled on, so it’s nice to see a detailed description, along with source code, of how they can be used together! I also appreciated the hyperlinks to additional information. Well done!

    I downloaded the source code and tried building it. That’s when I discovered all of the breaking changes to WCF RIA Services that were introduced with the November 2009 release. After spending a fair bit of time tracking down and resolving these issues, I discovered the following, interesting problem and I’m wondering if you ran into this as well.

    The file AddressValidationAttribute.shared.cs exists in two places: (1) inside the BusinessApp.Entities project where it is originally defined, and (2) in the BusinessApp project where it gets projected (generated code). In the first case it compiles fine, but in the projected code the Region and Country properties don’t exist so it doesn’t compile. The projected code uses a generated class definition that can be viewed in BusinessApp.Web.g.cs. Sure enough, there’s no mention of the Region and Country properties. I noticed that the original definition of these properties (in non-projected code) uses the same name for the property and the type, i.e.
    public virtual Region Region { get; set; }
    public virtual Country Country { get; set; }

    I suspect that this confused the projection process and it skipped these fields when it projected the class. Did you run into this?

    Myles

  2. Shane Kercheval
    January 26th, 2010 at 14:21 | #2

    @MylesRip

    Hmm.. That is interesting. I have installed all of the latest releases and it still compiles for me. Sorry about that. I hate when i download someone’s code and it doesn’t compile. I see what you mean with the Region Region and Country Country. I am looking in the generated code and Address does not contain those fields for me either. RIA uses metadata files and i think we need to use the RIA [Includes] attribute in the Address metadata file to make it work. I will look more into this and let you know what i find. Please update me if you find anything as well.

  3. Shaun
    February 19th, 2010 at 06:02 | #3

    Hi Shane, I also spent some time getting things working for the updated RIA drop in VS 2008. I’d been looking for a good example of RIA with NH and had no luck until now.
    I’ve updated the code and made it available for all – http://sunevnuahs.blogspot.com/2010/02/silverlight-3-ria-and-fluent-nhibernate.html

  4. Shane Kercheval
    February 23rd, 2010 at 01:34 | #4

    Thanks Shaun! I will update my post to reference your website and code. Thank you for letting me know!

  1. No trackbacks yet.