Integrating a Legacy DAL with WCF RIA services

You’ve gotta admit that RIA services is pretty awesome, it’s the perfect companion to Silverlight. However it’s relatively cumbersome and messy to get all the bells and whistles working with a legacy DAL (paging, sorting, etc) and in many cases it’s just not feasible to throw out your investment in your existing infrastructure. So what should you do? Well, I have started a project on Codeplex to address this issue (http://riatodal.codeplex.com/). Rather than go through the code now, I’ll post on what the framework accomplishes and give some code samples that use it. In subsequent posts I’ll delve into bits and pieces of it’s underpinnings.

First, imagine this scenario. You have a DAL and DAL providers. Specifically, let’s take a look at a user manager/provider like the ones shown below:

    public class UserManager
    {
        List<User> _users = new List<User>();

        public List<User> GetUsersByNameAndSearch(string name, int userStatusId, string whereClause, string orderBy, int pageIndex, int pageSize)
        {
            // Paginate our fake data source
            return _users.Skip(pageIndex * pageSize).Take(pageSize).ToList();
        }
    }

    public class DalProvider
    {
        private DalProvider()
        {
        }

        private static DalProvider _instance = null;
        public static DalProvider ProviderInstance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new DalProvider();
                }

                return _instance;
            }
        }

        private UserManager _umInstance = null;
        public UserManager UserManagerInstance
        {
            get
            {
                if (_umInstance == null)
                {
                    _umInstance = new UserManager();
                }

                return _umInstance;
            }
        }
    }

Pretty straight forward. There’s a provider following the singleton pattern, so to get a list of users, we would write something like:

List<User> users 
= LegacyDal.DalProvider.ProviderInstance.UserManagerInstance.GetUsersByNameAndSearch(...);

and from there we can bind that list of users to a grid. But, that’s not very good for RIA services. Paging would become a nightmare (how do we get the skip/take parameters to our legacy DAL for paging?). And custom where clauses? eww! How do we link these two together and have them play nicely? The answer is to provide a framework similar to the one described below, which is composed of two primary things:

1) A LINQ provider which we can use to query our DAL
2) “Hints” on our RIA entities and domain service methods that will tell the LINQ provider how to resolve the appropriate DAL methods.

The best part is the solution will result in you do not having to modify anything in your DAL, it will just link up and work.

So how does it work? First, decorate your RIA entities that are exposed through your domain service with some custom attributes:

    [EntityMap(typeof(LegacyDal.Entities.User))]
    public class RIAUser
    {
        [Key]
        [FieldMap("UserName")]
        public string RiaUserName { get; set; }

        [FieldMap("FirstName")]
        public string RiaFirstName { get; set; }

        [FieldMap("UserStatus")]
        public string RiaUserStatus { get; set; }
    }

Pretty straightforward. Above we are mapping our RIA user to a user entity in our legacy DAL by mapping the entity type and it’s corresponding fields.

Then, we add some configuration:

  <riaToDalLinqSettings
     resolverType="Configuration"
     dalProviderInstance="LegacyDal.DalProvider.ProviderInstance, LegacyDal"
     entityManagerNameFormatString="{0}ManagerInstance"
    />

The above says we will resolve our DAL information using the current configuration by setting resolverType=”Configuration” (we can write our own and export it using MEF if we want, in which case we would set the resolverType=”Custom”). The dalProviderInstance string let’s the framework know the provider instance to use in the DAL. And for the entityManagerNameFormatString it’s just a format string which would evaluate to[EntityName]ManagerInstance, or “UserManagerInstance” if we were looking for a UserManager.

Then, our domain service looks like:

    [EnableClientAccess]
    public class GetDataService : LegacyDalDomainServiceBase
    {
        // Create the query context
        RiaToDalQueryContext<RIAUser> RIAUserQueryContext;

        public GetDataService()
        {
            // Instantiate the context with this domain service
            RIAUserQueryContext = new RiaToDalQueryContext<RIAUser>(this);
        }

        /// <summary>
        /// MethodMap will map our queryable GetUser() ria method 
        /// to the GetUsersByNameAndSearch() method  in our DAL.
        /// It will look for an overload which takes name and userStatusId parameter.
        /// The full method signature in the legacy DAL looks like:
        ///     GetUsersByNameAndSearch(string name, int userStatusId, 
        ///           string whereClause, string orderBy, int pageIndex, int pageSize)
        /// but by convention any parameter name of:
        ///       whereClause, orderBy, pageIndex, pageSize 
        /// will be excluded from the match and
        /// will be passed data based on the LINQ query that is composed.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [MethodMap("GetUsersByNameAndSearch")]
        public IQueryable<RIAUser> GetUser(
            string name,
            int statusId
            )
        {
            // Use some LINQ against our RIA entities. 
            // The Entities property should already filtered by the "name" 
            // and statusId parameters of the current query method,
            // but we can do some additional filtering, and it will be propagated 
            // through to the whereClause in the legacy DAL method.
            var query = from u in RIAUserQueryContext.Entities
                        where (u.RiaFirstName != "Jim" && u.RiaUserStatus == "Active") || u.RiaFirstName == "Ronnie"
                        orderby u.RiaFirstName descending
                        select u;

            // Return the query, let RIA services compose 
            // any additional LINQ sent from the client
            // (such as any paging information)
            return query;
        }
    }

Woah! What’s this doing? I’ll let you figure that out from the comments and the code, but that’s all that needs to be done to integrate RIA with our legacy DAL, the framework will handle the rest. Here’s a screenshot of what the Legacy DAL method receives after this LINQ is executed:

and here’s what the client that executed the query method looked like:

Notice how the where clause from our LINQ expression got passed to the where clause parameter in our DAL method? (it even resolved the correct names by translating RIAFirstName to FirstName). The paging parameters came from the DataPager in the Silverlight client, as did the order by clause. The userName and userStatusId were received from the parameters in the IQueryable GetUser() domain service. All of this was done without any additional code, we just needed to give the framework some “hints” on how to resolve our DAL.

So what’s this running on? Just MEF, Silverlight 4 and WCF RIA Services. This is a very preliminary design, but I’m planning on adding:

  • Update support. Call this.Update(entity) in your domain service and based on the mappings it will persist the entity back to the DAL
  • Transaction support via TransactionScope
  • Deep loading with the [Associate] attribute
  • [Ignore] attributes for the query parameters so parameters can be selectively excluded from matching when trying to resolve the appropriate DAL method
  • Re-factoring the code and making it more plugable
  • Anything I can think of or that’s suggested to me!

Be sure to take a look on Codeplex for the source (and a working example), and if you have any suggestions please let me know in the comments section!

  1. Muhammad Adeel

    Hello, Thanks. This is realy out of the box. It helpded me alot. Thanks a lot.

Leave a Comment


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