Click here to Skip to main content
15,893,337 members
Articles / Web Development / CSS

Building a 3-Tier App with Silverlight 3, .NET RIA Services, and Azure Table Storage

Rate me:
Please Sign up or sign in to vote.
4.89/5 (28 votes)
11 Jul 2009CDDL19 min read 165.4K   1.7K   148  
This article presents the techniques and caveats of building a 3-tire Azure hosted application using Silverlight 3 (presentation tier), .NET RIA services (business logic and data access), and Windows Azure Table (data storage).
// DataContext.cs
//

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Data.Services.Client;
using Microsoft.Azure.StorageClient;

namespace Microsoft.Azure.Linq {

    public abstract class DataContext {

        private static readonly Dictionary<string, StorageAccountInfo> _accountInfoMap =
            new Dictionary<string, StorageAccountInfo>();
        private static readonly HashSet<Type> _initializedEntitySets = new HashSet<Type>();

        private string _connectionStringName;
        private StorageAccountInfo _accountInfo;
        private TableStorageDataServiceContext _dataServiceContext;

        private string _partitionKey;
        private int _retryCount;
        private TimeSpan _retryDelay;

        protected DataContext(string connectionStringName) {
            if (String.IsNullOrEmpty(connectionStringName)) {
                throw new ArgumentNullException("connectionStringName");
            }

            _connectionStringName = connectionStringName;
            _retryDelay = RetryPolicies.StandardMinBackoff;
        }

        internal TableStorageDataServiceContext DataServiceContext {
            get {
                if (_dataServiceContext == null) {
                    _accountInfo = GetAccountInfo(_connectionStringName);
                    _dataServiceContext = new TableStorageDataServiceContext(_accountInfo);
                    _dataServiceContext.SaveChangesDefaultOptions = SaveChangesOptions.ReplaceOnUpdate;

                    // Initialize the Azure storage, by creating entity sets based on the set
                    // of properties defined on this DataContext instance.
                    // Do it once per appdomain instance, as this incurs the overhead of HTTP
                    // requests sent to the store.
                    Type modelType = this.GetType();
                    if (_initializedEntitySets.Contains(modelType) == false) {
                        TableStorage.CreateTablesFromModel(modelType, _accountInfo);
                        _initializedEntitySets.Add(modelType);
                    }
                }
                return _dataServiceContext;
            }
        }

        public string PartitionKey {
            get {
                return _partitionKey;
            }
            set {
                _partitionKey = value;
            }
        }

        public int RetryCount {
            get {
                return _retryCount;
            }
            set {
                _retryCount = value;
            }
        }

        public TimeSpan RetryDelay {
            get {
                return _retryDelay;
            }
            set {
                _retryDelay = value;
            }
        }

        protected internal virtual IQueryable<TEntity> CreateEntityQuery<TEntity>(string name) where TEntity : Entity, new() {
            IQueryable<TEntity> query = DataServiceContext.CreateQuery<TEntity>(name);

            if (String.IsNullOrEmpty(PartitionKey) == false) {
                query = query.Where(entity => entity.PartitionKey == PartitionKey);
            }

            return query;
        }

        private static StorageAccountInfo GetAccountInfo(string connectionStringName) {
            if (String.IsNullOrEmpty(connectionStringName)) {
                throw new ArgumentNullException("connectionStringName");
            }

            StorageAccountInfo accountInfo;
            if (_accountInfoMap.TryGetValue(connectionStringName, out accountInfo)) {
                return accountInfo;
            }

            ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings[connectionStringName];
            if (connectionString == null) {
                throw new ArgumentOutOfRangeException("connectionStringName");
            }

            string name = null;
            string key = null;
            string uri = "http://table.core.windows.net";
            bool pathStyleUri = false;

            string[] parts = connectionString.ConnectionString.Split(new char[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string part in parts) {
                int index = part.IndexOf('=');
                if (index >= 0) {
                    string partName = part.Substring(0, index);
                    string partValue = part.Substring(index + 1);

                    if (String.CompareOrdinal(partName, "name") == 0) {
                        name = partValue;
                        continue;
                    }
                    else if (String.CompareOrdinal(partName, "key") == 0) {
                        key = partValue;
                        continue;
                    }
                    else if (String.CompareOrdinal(partName, "uri") == 0) {
                        uri = partValue;
                        continue;
                    }
                    else if (String.CompareOrdinal(partName, "pathStyleUri") == 0) {
                        pathStyleUri = Boolean.Parse(partValue);
                        continue;
                    }
                }

                throw new ArgumentException("Invalid connection string.");
            }

            accountInfo = new StorageAccountInfo(new Uri(uri, UriKind.Absolute), pathStyleUri, name, key);
            _accountInfoMap[connectionStringName] = accountInfo;

            return accountInfo;
        }

        public void SubmitChanges() {
            string partitionKey = PartitionKey;

            if (String.IsNullOrEmpty(partitionKey) == false) {
                foreach (EntityDescriptor entityDescriptor in DataServiceContext.Entities) {
                    Entity entity = (Entity)entityDescriptor.Entity;
                    if (String.IsNullOrEmpty(entity.PartitionKey)) {
                        entity.PartitionKey = partitionKey;
                    }
                }
            }

            if (_retryCount == 0) {
                DataServiceContext.SaveChanges();
            }
            else {
                DataServiceContext.RetryPolicy = RetryPolicies.RetryN(_retryCount, _retryDelay);
                DataServiceContext.SaveChangesWithRetries();
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Technical Lead
United States United States
https://github.com/modesty

https://www.linkedin.com/in/modesty-zhang-9a43771

https://twitter.com/modestyqz

Comments and Discussions