Azure + Silverlight 4 + RIA Services + MVC2 (Part 2)

3 minute read

by Peter Daukintis

Ok, with Azure out of the equation until .NET 4 is supported I will turn my attention first to work out how to achieve data storage in the cloud whilst considering the overall architecture.

Firstly, I added an empty Domain Service Class by selecting the web hosting project and selecting Add New Item…

AddDomainServiceClass

This results in the following dialog:

EmptyDomainServiceClassDlg

From which I elected to choose <empty domain service class> since I wanted my storage to be hosted on Azure Tables which is not supported out of the box.

This generates an empty domain service class, which I fleshed out a little with the following code:

    [EnableClientAccess()]
    public class TestDomainService : DomainService
    {
        private readonly ITestEntityRepository _testEntityRepository;

        public TestDomainService(ITestEntityRepository repository)
        {
            _testEntityRepository = repository;
        }

        public IQueryable<TestEntity> GetEntities()
        {
            return _testEntityRepository.GetTestEntities();
        }

        [Invoke]
        public void AddTestEntity(TestEntity testEntity)
        {
            _testEntityRepository.AddTestEntity(testEntity);
        }
    }
}

which is about as simple as I could make it whilst introducing a repository interface to support testability and adhere to a clear separation of concerns. Leaving the code like this will result in problems as the RIA services code generation layer requires a default constructor for the domain service class. the intention is to inject the concrete repository at runtime using an IOC container.  I used Unity as my IOC container of choice. the mechanism provided as a hook for creation of domain service classes is the IDomainServiceFactory interface (see http://msdn.microsoft.com/en-us/library/system.web.domainservices.idomainservicefactory(VS.91).aspx). You can set the DomainService.Factory property to a concrete implementation of your own IDomainServiceFactory interface like the following:

    public class DomainServiceFactory : IDomainServiceFactory
    {
        public DomainService CreateDomainService(Type domainServiceType, DomainServiceContext context)
        {
            var service = Global.IocContainer.Resolve(domainServiceType) as DomainService;
            if (service != null)
            {
                service.Initialize(context);
            }
            return service;
        }

        public void ReleaseDomainService(DomainService domainService)
        {
            domainService.Dispose();
        }
    }

Now, this ensures that Unity will resolve the dependencies on the domain service context. So we just need to wire this in and register our dependencies with Unity and we should be back in business.

        protected void Application_Start(object sender, EventArgs e)
        {
            if (IocContainer == null)
                IocContainer = new UnityContainer();
            DomainService.Factory = new DomainServiceFactory();

            IocContainer.RegisterType<TestDomainService>();
            IocContainer.RegisterType<ITestEntityRepository, TestEntityRepository>();
        }

Now that takes care of some of the plumbing, but what about the actual implementation for the Azure Table Storage?

Well, borrowing some code from here http://social.msdn.microsoft.com/Forums/en/windowsazure/thread/efdc0bcc-918c-4034-be1d-ecb13a601e7e gives us an idea of how to implement the repository;

    public class TestEntityRepository : ITestEntityRepository
    {
        private static readonly TestDomainContext Ctx;

        static TestEntityRepository()
        {
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
                                                                 configSetter(ConfigurationManager.AppSettings[configName]));

            CloudStorageAccount account = CloudStorageAccount.FromConfigurationSetting("<your name here>");

            var tableClient = new CloudTableClient(account.TableEndpoint.ToString(), account.Credentials);
            tableClient.CreateTableIfNotExist("TestEntities");
            Ctx = new TestDomainContext(account.TableEndpoint.ToString(), account.Credentials);
        }

        public IQueryable<TestEntity> GetTestEntities()
        {
            return Ctx.TestEntities;
        }

        public void AddTestEntity(TestEntity testEntity)
        {
            Ctx.AddTestEntity(testEntity.Name, testEntity.Age);
        }
   }

Where the TestEntity looks like this:

    public class TestEntity : TableServiceEntity
    {
        public TestEntity()
        { }

        public TestEntity(string partitionKey, string rowKey)
            : base(partitionKey, rowKey)
        { }

        [Key]
        public override string PartitionKey
        {
            get { return base.PartitionKey; }
            set { base.PartitionKey = value; }
        }

        [Key]
        public override string RowKey
        {
            get { return base.RowKey; }
            set { base.RowKey = value; }
        }

        public string Name { get; set; }
        public int Age { get; set; }
    }

And the TestDomainContext used by the Concrete repository looks like this:

public classTestDomainContext :TableServiceContext
   
{
       
publicTestDomainContext(string baseAddress, StorageCredentials credentials)
            :
base(baseAddress, credentials)
        {
        }

       
public IQueryable<TestEntity> TestEntities
       
{
           
get { return CreateQuery<TestEntity>("TestEntities"); }
        }

       
public void AddTestEntity(string name, int age)
        {
           
var c = new TestEntity((DateTime.MaxValue.Ticks - DateTime.Now.Ticks).ToString(), Guid.NewGuid().ToString())
            {
               
Name = name,
               
Age = age
           
};
           
AddObject("TestEntities", c);
   
        SaveChanges();
        }
    }

I just needed to set up the account info in my web.config file to give a working solution:

      <add key="your_key" value="DefaultEndpointsProtocol=https;AccountName=your_acct_name;AccountKey=”xxxxxxxxxx" />

That’s it for now – in the next post I will try to configure the application to use azure table storage for authentication….

Comments