May 2010

Volume 25 Number 05

RIA Services - Enterprise Patterns with WCF RIA Services

By Michael D. Brown | May 2010

Two major announcements from PDC09 and Mix10 were the availability of the Silverlight 4 beta and RC, respectively. By the time you read this, the full release to Web of Silverlight 4 will be available for download. Along with extensive printing support, it includes support for elevated permissions, webcams, microphones, toast, clipboard access and more. With its new feature set, Silverlight 4 is poised to go toe-to-toe with Adobe AIR as a multiplatform rich UI framework.

Although all of that does excite me, I’m primarily a business-application developer, and one thing I would love is a simple way to get my business data and logic into a Silverlight application.

One concern with line-of-business Silverlight applications is connecting to data. Nothing prevents you from creating your own Windows Communication Foundation (WCF) service and connecting to it in Silverlight 3, but that leaves a lot to be desired, especially when you consider the myriad ways you can connect to data from ASP.NET or desktop applications. Whereas desktop and Web applications can connect directly to the database via NHibernate, Entity Framework (EF) or raw ADO.NET constructs, Silverlight apps are separated from my data by “the cloud.” I call this separation the data chasm.

Crossing this chasm may seem deceptively simple at first. Obviously, it has been done to some degree in a number of existing data-rich Silverlight applications. But what initially appears to be an easy task becomes increasingly complicated as you address more concerns. How do you track changes over the wire or encapsulate business logic in entities that live on both sides of the firewall?  How do you keep the details of transmission from leaking into your business concerns?

Third-party tools are emerging to address these concerns, but Microsoft also saw it needed to provide a solution, so it introduced WCF RIA Services (formerly .NET RIA Services), or for brevity, RIA Services. (You’ll find a full introduction to RIA Services in “Building a Data-Driven Expense App with Silverlight 3” in the May 2009 edition of MSDN Magazine (msdn.microsoft.com/magazine/dd695920). I’ve been following it since I was first invited into the beta program, providing suggestions to the development team and learning how to leverage the framework within my own applications.

A common question in the RIA Services forums is how RIA Services fit into best practices architecture. I was always impressed with the basic forms-over-data capabilities of RIA Services, but I definitely saw the opportunity to better architect my application so the framework concerns didn’t leak into the logic of my application.

Introducing KharaPOS

I’ve developed an exemplar application, KharaPOS, to provide a tangible example of the concepts I present in this article. It’s a point-of-sale (POS) application implemented in Silverlight 4 using RIA Services, Entity Framework and SQL Server 2008. The ultimate goal is to enable the application to be hosted in Azure and SQL Azure, but there’s the little problem of Microsoft .NET Framework 4 support (or lack thereof) with Azure. 

In the interim, KharaPOS provides a good example of using the .NET Framework 4 to create a real-world application. The project is hosted via CodePlex at KharaPOS.codeplex.com. You can visit the site to download the code, view documentation and join the discussion surrounding development of the application.

I should note that I borrowed from the book, “Object Models: Strategies, Patterns, and Applications, Second Edition” (Prentice Hall PTR, 1996), by Peter Coad with David North and Mark Mayfield, for the majority of the design and functionality of the KharaPOS application. I’ll focus on a single subsystem of the application, catalog management (see Figure 1).

Figure 1 The Entity Data Model for Catalog Management
Figure 1 The Entity Data Model for Catalog Management

Enterprise Patterns

A number of excellent books discuss design patterns for enterprise application development. One book I constantly use as a reference is “Patterns of Enterprise Application Architecture” (Addison-Wesley, 2003) by Martin Fowler. This book and its supplemental Web site (martinfowler.com/eaaCatalog/) provide an excellent summary of helpful software patterns for developing enterprise business applications.

A handful of patterns in Fowler’s catalog deal with the presentation and manipulation of data, and interestingly enough, they occupy the same space as RIA Services. Understanding these will give a clearer picture of how RIA Services can be adapted to meet the needs of the simplest to the most complex business applications. I’ll discuss the following patterns:

  • Forms and controls
  • Transaction script
  • Domain model
  • Application service layer

Let’s take a quick tour of these patterns. The first three concern different ways of dealing with the logic surrounding your data. As you progress through them, the logic moves from being scattered throughout the application and repeated as needed to being centralized and focused.

Forms and Controls

The forms-and-controls pattern (or as I refer to it, forms over data) places all of the logic within the UI. At first glance, this seems like a bad idea. But for simple data-entry and master-detail views, it’s the simplest and most direct approach to get from UI to database. Many frameworks have intrinsic support for this pattern (Ruby on Rails scaffolding, ASP.NET Dynamic Data and SubSonic are three prime examples), so there’s definitely a time and place for what some call an anti-pattern. While many developers relegate the forms-over-data approach to initial prototyping only, there are definite uses for it in final applications.

Regardless of your opinion on its utility, there’s no denying the simplicity or approachability of forms over data. It’s not called rapid application development (RAD) because it’s tedious. WCF RIA Services brings RAD to Silverlight. Leveraging Entity Framework, RIA Services and the Silverlight Designer, it’s possible to create a simple forms-over-data editor against a database table in five steps:

  1. Create a new Silverlight business application.
  2. Add a new Entity Data Model (EDM) to the created Web application (using the wizard to import the database).
  3. Add a domain service to the Web application (be sure to build it first so that the EDM is properly discovered) referencing the data model.
  4. Use the data sources panel to drag an entity exposed by RIA Services onto the surface of a page or user control in the Silverlight application (be sure to build again so it can see the new domain service).
  5. Add a button and code-behind to save changes on the form to the database with this simple line:
this.categoryDomainDataSource.SubmitChanges();

You now have a simple data grid that can be used to directly edit existing rows in your table. With a few more additions, you can create a form that lets you add new rows to the table.

Although this pattern has been demonstrated repeatedly, showing the advantage of RAD with WCF RIA Services, it’s still relevant here because it provides a baseline for development with the framework. Also, as mentioned, this is a valid pattern within RIA Services-based applications.

Recommendation As with ASP.NET dynamic data, the forms-over-data pattern should be used for simple administration UIs (such as the KharaPOS product category editor), where the logic is simple and straightforward: add, remove and edit rows within a lookup table. But Silverlight and RIA Services scale to much more complex applications, as we’ll see now.

Table Data Gateway The standard, out-of-the box approach to RIA Services applications I just discussed can also be viewed as an implementation of the table data gateway pattern as presented on pp. 144–151 of Fowler’s book. Through two levels of indirection (EF mapping over the database followed by domain service mapping over EF), I’ve created a simple gateway to the database tables using basic create, read, update and delete (CRUD) operations returning strongly typed data transfer objects (DTOs).

Technically, this doesn’t qualify as a pure table data gateway because of its dual layers of indirection. But if you squint, it closely resembles the table data gateway pattern. To be honest, it would have been a more logical progression to discuss the mapping between the RIA Services and the table data gateway pattern, because all the remaining patterns in the list are data interface patterns, but forms over data is mostly a UI pattern. However, I felt it more prudent to start with the basic scenario and focus on the UI moving back toward the database.

Model-View-ViewModel (MVVM) Even though it’s simple to create a functional form using forms over data, there’s still some friction involved. Figure 2, the XAML for category management, illustrates this.

Figure 2 XAML for Category Management

<Controls:TabItem Header="Categories">
  <Controls:TabItem.Resources>
    <DataSource:DomainDataSource
      x:Key="LookupSource"
      AutoLoad="True"
      LoadedData="DomainDataSourceLoaded"
      QueryName="GetCategoriesQuery"
      Width="0">
      <DataSource:DomainDataSource.DomainContext>
        <my:CatalogContext />
      </DataSource:DomainDataSource.DomainContext>
    </DataSource:DomainDataSource>
    <DataSource:DomainDataSource
      x:Name="CategoryDomainDataSource"
      AutoLoad="True"
      LoadedData="DomainDataSourceLoaded"
      QueryName="GetCategoriesQuery"
      Width="0">
      <DataSource:DomainDataSource.DomainContext>
        <my:CatalogContext />
      </DataSource:DomainDataSource.DomainContext>
      <DataSource:DomainDataSource.FilterDescriptors>
        <DataSource:FilterDescriptor 
          PropertyPath="Id" 
          Operator="IsNotEqualTo" Value="3"/>
      </DataSource:DomainDataSource.FilterDescriptors>
    </DataSource:DomainDataSource>
  </Controls:TabItem.Resources>
  <Grid>
    <DataControls:DataGrid
      AutoGenerateColumns="False" 
      ItemsSource="{Binding Path=Data,
        Source={StaticResource CategoryDomainDataSource}}" 
      x:Name="CategoryDataGrid">
      <DataControls:DataGrid.Columns>
        <DataControls:DataGridTextColumn 
          Binding="{Binding Name}" Header="Name" Width="100" />
        <DataControls:DataGridTemplateColumn 
          Header="Parent Category" Width="125">
            <DataControls:DataGridTemplateColumn.CellEditingTemplate>
              <DataTemplate>
                <ComboBox 
                  IsSynchronizedWithCurrentItem="False" 
                  ItemsSource="{Binding Source=
                    {StaticResource LookupSource}, Path=Data}"  
                  SelectedValue="{Binding ParentId}" 
                  SelectedValuePath="Id" 
                  DisplayMemberPath="Name"/>
              </DataTemplate>
            </DataControls:DataGridTemplateColumn.CellEditingTemplate>
            <DataControls:DataGridTemplateColumn.CellTemplate>
              <DataTemplate>
                <TextBlock Text="{Binding Path=Parent.Name}"/>
              </DataTemplate>
            </DataControls:DataGridTemplateColumn.CellTemplate>
          </DataControls:DataGridTemplateColumn>
          <DataControls:DataGridTextColumn
            Binding="{Binding ShortDescription}"
            Header="Short Description" Width="150" />
          <DataControls:DataGridTextColumn 
            Binding="{Binding LongDescription}" 
            Header="Long Description" Width="*" />
        </DataControls:DataGrid.Columns>
    </DataControls:DataGrid>
  </Grid>
</Controls:TabItem>

The column for the parent category in the data grid is a combobox that uses a list of the existing categories so users can select the parent category by name instead of memorizing the ID of the category. Unfortunately, Silverlight doesn’t like it when the same object is loaded twice within the visual tree. Therefore, I had to declare two domain data sources: one for the grid and one for the lookup combobox. Also, the code-behind for managing categories is rather convoluted (see Figure 3).

Figure 3 Code-Behind for Managing Categories

private void DomainDataSourceLoaded(object sender, LoadedDataEventArgs e)
{
  if (e.HasError)
  {
    MessageBox.Show(e.Error.ToString(), "Load Error", MessageBoxButton.OK);
    e.MarkErrorAsHandled();
  }
}
private void SaveButtonClick(object sender, RoutedEventArgs e)
{
  CategoryDomainDataSource.SubmitChanges();
}
private void CancelButtonClick(object sender, RoutedEventArgs e)
{
  CategoryDomainDataSource.Load();
}
void ReloadChanges(object sender, SubmittedChangesEventArgs e)
{
  CategoryDomainDataSource.Load();
}

I’m not going to give a full tutorial on MVVM here—see the article, “WPF Apps with the Model-View-ViewModel Design Pattern” in the February 2009 issue (msdn.microsoft.com/magazine/dd419663) for an excellent treatise on the topic. Figure 4 shows one way to leverage MVVM within an RIA Services application.

Figure 4 Category Management Through a View Model

public CategoryManagementViewModel()
{
  _dataContext = new CatalogContext();
  LoadCategories();
}
private void LoadCategories()
{
  IsLoading = true;
  var loadOperation= _dataContext.Load(_dataContext.
    GetCategoriesQuery());
  loadOperation.Completed += FinishedLoading;
}
protected bool IsLoading
{
  get { return _IsLoading; }
  set
  {
    _IsLoading = value;
    NotifyPropertyChanged("IsLoading");
  }
}
private void NotifyPropertyChanged(string propertyName)
{
  if (PropertyChanged!=null)
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
void FinishedLoading(object sender, EventArgs e)
{
  IsLoading = false;
  AvailableCategories=
    new ObservableCollection<Category>(_dataContext.Categories);
}
public ObservableCollection<Category>AvailableCategories
{
  get
  {
    return _AvailableCategories;
  }
  set
  {
    _AvailableCategories = value;
    NotifyPropertyChanged("AvailableCategories");
  }
}

As you see, the ViewModel is responsible for initializing the domain context and informing the UI when a load is occurring, along with handling the requests from the UI to create new categories, save changes to existing categories and reload the data from the domain service. This leaves a clean separation between the UI and the logic that drives it. The MVVM pattern may appear to require more work, but its beauty reveals itself the first time you have to change the logic for getting data into the UI. Also, moving the process of loading the categories into the ViewModel allows us to clean up the view significantly (XAML and code-behind alike).

Recommendation Use MVVM to prevent complex UI logic from cluttering your UI—or worse, from cluttering your business object model.

Transaction Script

As you begin adding logic to your application, the forms-over-data pattern becomes cumbersome. Because the logic regarding what can be done with the data is embedded in the UI (or in the ViewModel, if you’ve taken that step), it will be scattered across the application. Another side effect of decentralized logic is that developers may not be aware that specific functionality already exists in the application, which can lead to duplication. This creates nightmares when the logic changes, because it needs to be updated in all locations (assuming that all locations implementing the logic have been properly cataloged).

The transaction script pattern (pp. 110–115 in Fowler’s book) provides some relief. It allows you to separate the business logic that manages the data from the UI.

As defined by Fowler, the transaction script “organizes business logic by procedures where each procedure handles a single request from the presentation.” Transaction scripts are much more than simple CRUD operations. In fact, they sit in front of the table data gateway to handle CRUD operations. Taken to the extreme, a separate transaction script would handle every retrieval and submission to the database. But, being logical people, we know there’s a time and place for everything.

A transaction script is useful when your application has to coordinate an interaction between two entities, such as when you create an association between two instances of different entity classes. For example, in the catalog management system, I signify that a product is available for a business unit to order for its inventory by creating a catalog entry. The entry identifies the product, the business unit, a product SKU and the time during which it can be ordered both internally and externally. To simplify the creation of catalog entries, I created a method on the domain service (see the following code snippet) that provides a transaction script for modifying product availability for a business unit without the UI having to manipulate catalog entries directly.

In fact, catalog entries aren’t even exposed through the domain service, as shown here:

public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
  var entry = ObjectContext.CreateObject<CatalogEntry>();
  entry.BusinessUnitId = businessUnitId;
  entry.ProductId = product.Id;
  entry.DateAdded = DateTime.Now;
  ObjectContext.CatalogEntries.AddObject(entry);
  ObjectContext.SaveChanges();
}

Rather than being exposed as a function on the client-side domain context, RIA Services generates a function on the entity in question (in this case Product) that when called, places a change notification on the object that on the server side gets interpreted as a call to the method on the domain service.

Fowler recommends two approaches for implementing the transaction script:

  1. With commands that encapsulate the operations and can be passed around
  2. With a single class that holds a collection of transaction scripts

I took the second approach here, but there’s nothing preventing you from using commands. The benefit of not exposing the catalog entry to the UI layer is that the transaction script becomes the only means of creating catalog entries. If you use the command pattern, the rule is enforced by convention. If a developer forgets a command exists, you’ll end up right back where you started with logic fragmentation and duplication.

Another benefit of placing the transaction script on the domain service is that the logic executes server side (as I mentioned earlier). If you have proprietary algorithms or want to be certain the user hasn’t manipulated your data maliciously, placing the transaction script on the domain service is the way to go.

Recommendation Use the transaction script when your business logic becomes too complex for forms over data, you want to execute the logic for an operation on the server side, or both.

Business Logic vs. UI Logic I make several references to UI logic versus business logic, and though the difference may seem subtle at first, it’s important. UI logic is the logic concerned with the presentation—what’s shown on screen and how (for example, the items used to populate a combobox). Business logic, on the other hand, is what drives the application itself (for example, the discount applied to an online purchase). Both are important facets of an application, and another pattern emerges when they’re allowed to mix—see the article, “Big Ball of Mud,” by Brian Foote and Joseph Yoder (laputan.org/mud).

Passing Multiple Entities to the Domain Service By default, you can pass only one entity to a custom domain service method. For example, the method

public void CatalogProductForBusinessUnit(Product product, int businessUnitId)

will not work if you attempt to use this signature instead of an integer:

public void CatalogProductForBusinessUnit(Product product, BusinessUnit bu)

RIA Services would not generate a client proxy for this function because … well, those are the rules. You can have only one entity in a custom service method. This shouldn’t pose a problem in most situations, because if you have an entity, you have its key and can retrieve it on the back end again.

Let’s just say, for the sake of demonstration, that it’s an expensive operation to retrieve an entity (perhaps it’s on the other side of a Web service). It’s possible to tell the domain service that you want it to hold a copy of a given entity, as shown here:

public void StoreBusinessUnit(BusinessUnit bu)
{
  HttpContext.Current.Session[bu.GetType().FullName+bu.Id] = bu;
}
public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
  var currentBu = (BusinessUnit)HttpContext.Current.
    Session[typeof(BusinessUnit).FullName + businessUnitId];
  // Use the retrieved BusinessUnit Here.
}

Because the domain service is running under ASP.NET, it has full access to the ASP.NET session and the cache, as well, in case you want to automatically remove the object from memory after a certain period of time. I’m actually using this technique on a project where I have to retrieve customer relationship management (CRM) data from multiple remote Web services and present it to the user under a unified UI. I use an explicit method because some data is worth caching and some isn’t.

Domain Model

Sometimes business logic becomes so complex that even transaction scripts can’t properly manage it. Frequently, this shows up as complex branching logic within a transaction script or multiple transaction scripts to account for nuances in the logic. Another sign that an application has outgrown the utility of transaction scripts is  the need for frequent updates to address rapidly changing business requirements.

If you’ve noticed any of those symptoms, it’s time to consider a rich domain model (pp. 116–124 in the Fowler book). The patterns covered so far have one thing in common: The entities are little more than DTOs—they contain no logic (this is considered by some to be an anti-pattern referred to as Anemic Domain Model). One of the major benefits of object-oriented development is the ability to encapsulate data and the logic associated with it. A rich domain model takes advantage of that benefit by putting the logic back into the entity where it belongs.

The details of designing a domain model are beyond the scope of this article. See the book, “Domain-Driven Design: Tackling Complexity in the Heart of Software” (Addison-Wesley, 2004), by Eric Evans, or the previously mentioned Coad book on object models for great coverage on this topic. I can, though, provide a scenario that helps illustrate how a domain model can manage some of this stress.

Some KharaPOS customers want to look at historical sales of certain lines and decide, on a market-by-market basis, whether the lines will be 
expanded (making more products from it available), reduced, cut all together or remain flat for a given season.

I already have the sales data in another subsystem of KharaPOS, and everything else I need is here in the catalog system. I’ll just bring a read-only view of product sales into our entity data model as shown in Figure 5.

Figure 5 Entity Data Model Updated with Sales Data
Figure 5 Entity Data Model Updated with Sales Data

Now all I have to do is add the product-selection logic to the domain model. Because I’m selecting products for a market, I’ll put the logic on the BusinessUnit class (use a partial class with a shared.cs or shared.vb extension to inform RIA Services you want this function to be shuttled to the client). Figure 6 shows the code.

Figure 6 Domain Logic for Selecting Products for a Business Unit

public partial class BusinessUnit
{
  public void SelectSeasonalProductsForBusinessUnit(
    DateTime seasonStart, DateTime seasonEnd)
  {
    // Get the total sales for the season
    var totalSales = (from sale in Sales
                     where sale.DateOfSale > seasonStart
                     && sale.DateOfSale < seasonEnd
                     select sale.LineItems.Sum(line => line.Cost)).
                     Sum(total=>total);
    // Get the manufacturers for the business unit
    var manufacturers =
      Catalogs.Select(c =>c.Product.ManuFacturer).
        Distinct(new Equality<ManuFacturer>(i => i.Id));
    // Group the sales by manufacturer
    var salesByManufacturer = 
      (from sale in Sales
      where sale.DateOfSale > seasonStart
      && sale.DateOfSale < seasonEnd
      from lineitem in sale.LineItems
      join manufacturer in manufacturers on
      lineitem.Product.ManufacturerId equals manuFacturer.Id
      select new
      {
        Manfacturer = manuFacturer,
          Amount = lineitem.Cost
      }).GroupBy(i => i.Manfacturer);
    foreach (var group in salesByManufacturer)
    {
      var manufacturer = group.Key;
      var pct = group.Sum(t => t.Amount)/totalSales;
      SelectCatalogItemsBasedOnPercentage(manufacturer, pct);
    }
  }
  private void SelectCatalogItemsBasedOnPercentage(
    ManuFacturer manufacturer, decimal pct)
  {
     // Rest of logic here.
  }
}

Performing an auto-select for products to carry over a season is as simple as calling the new function on BusinessUnit and following that by a call to the SubmitChanges function on DomainContext. In the future, if a bug is found in the logic or the logic needs to be updated, I know exactly where to look. Not only have I centralized the logic, I’ve also made the object model more expressive of the intent. On p. 246 of his “Domain-Driven Design” book, Evans explains why this is a good thing:

If a developer must consider the implementation of a component in order to use it, the value of encapsulation is lost. If someone other than the original developer must infer the purpose of an object or operation based on its implementation, that new developer may infer a purpose that the operation or class fulfills only by chance. If that was not the intent, the code may work for the moment, but the conceptual basis of the design will have been corrupted, and the two developers will be working at cross-purposes.

To paraphrase, by explicitly naming the function for its purpose and encapsulating the logic (along with a few comments to make it clear what’s happening), I’ve made it easy for the next guy (even if the next guy is me five months from now) to determine what’s happening before I even go to the implementation. Putting this logic with the data to which it’s naturally associated takes advantage of the expressive nature of object-oriented languages.

Recommendation Use a domain model when your logic is complex and gnarly and may involve several entities at once. Bundle the logic with the object to which it has the most affinity and provide a meaningful, intentional name for the operation.

The Difference Between Domain Model and Transaction Script in RIA Services You may have noticed that for both the transaction script and the domain model, the call was made directly on the entity. Note, though, the logic for the two patterns lies in two separate places. In the case of the transaction script, calling the function on the entity just serves to indicate to the domain context/service that the corresponding function should be called on the domain service the next time submit changes is called. In the case of the domain model, the logic is executed client side and then committed when submit changes is called.

The Repository and Query Objects The domain service naturally implements the repository pattern (see Fowler, p. 322). In the WCF RIA Services Code Gallery (code.msdn.microsoft.com/RiaServices), the RIA Services team provides an excellent example of creating an explicit implementation of the pattern over the DomainContext. This allows for increased testability of your application without the need to actually hit the service layer and the database.

Application Service Layer

Quick question: What do you do when you want to leverage a rich domain model but don’t want to expose its logic to the UI layer? That’s where the application service layer pattern (Fowler, p. 133) comes in handy. Once you have the domain model, this pattern is simple to implement by moving the domain logic out of shared.cs into a separate partial class and placing a function on the domain service that invokes the function on the entity.

The application service layer acts as a simplified facade over your domain model, exposing operations but not their details. Another benefit is that your domain objects will be able to take internal dependencies without requiring the service layer clients to take them as well. In some cases (see the seasonal product-selection example shown in Figure 6), the domain service makes one simple call on your domain. Sometimes it might orchestrate a few entities, but be careful—too much orchestration turns it back into a transaction script, and the benefits of encapsulating the logic within the domain are lost.

Recommendation Use the application service layer to provide a simple facade over the domain model and remove the requirement the UI layer needs to take dependencies your entities might take.

Extra Credit: The Bounded Context

In the RIA forums, participants often ask, “How do I break up my very large database across domain services so that it’s more manageable?” A follow-up question is, “How do I handle entities that need to exist in multiple domain services?” Initially I thought there shouldn’t be a need to do these things; the domain service should act as a service layer over your domain model and a single domain service should serve as a facade over your entire domain.

During my research for this article, though, I came across the bounded-context pattern (Evans, p. 336), which I’d read about before but hadn’t remembered when I originally responded to those questions. The basic premise of the pattern is that on any large project there will be multiple sub-domains in play. Take for example KharaPOS, where I have a domain for catalogs and a separate one for sales.

The bounded context allows those domains to coexist peacefully even though there are elements shared between them (such as Sale, Business Unit, Product and LineItem, which are in both the sales and catalog domains). Different rules apply to the entities based on which domain is interacting with them (Sale and LineItem are read-only in the catalog domain). A final rule is that operations never cross over contexts. This is makes life convenient, because Silverlight doesn’t support transactions over multiple domain services.

Recommendation Use bounded contexts to divide a large system into logical subsystems.

The Pit of Success

In this article, we’ve seen how RIA Services support the major enterprise patterns with very little effort. Rarely are frameworks so approachable while also being flexible enough to support everything from the simplest spreadsheet data-entry applications to the most complex business applications without requiring major effort to make the transition. This is the “Pit of Success”  Brad Abrams mentioned in his blog posting,of the same name (blogs.msdn.com/brada/archive/2003/10/02/50420.aspx).                 


Mike Brown  is the president and cofounder of KharaSoft Inc. (http://kharasoft.wordpress.com/), a technology firm specializing in training, custom software development and Software as a Service. He is an accomplished technology specialist with more than 14 years of industry experience, a back-to-back MVP Award recipient, cofounder of the Indy Alt.NET user group (indyalt.net) and die-hard Bears fan!

Thanks to the following technical expert for reviewing this article:  Brad Abrams