Archive for the 'Silverlight' Category

24
Feb
10

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

by Peter Daukintis

To illustrate how the final architecture hangs together I will take the AudioModule PRISM module from the previous post and link it up with a RIA services class library running on the server. So, the first step is to add a RIA Services class library to the solution:

RIAServicesClassLibrary

This step will create two new sub-projects – one for the client – and one for the server. I won’t be using the client library so will delete it and use the AudioModule in its place. So I have removed the project without the .Web suffix. Also, I will delete the default Class.cs that comes with the server-side library project. Then the next step is to add a Domain Service Class to the project using the right-click Add New Item menu. I called this AudioDomainContext to match up with the client-side PRISM module.

    [EnableClientAccess()]
    public class AudioDomainService : DomainService
    {
    }

This is the empty class that the wizard creates for you – since I don’t want to use any of the pre-built domain context’s such as the ones provided for Linq2Sql or Entity framework. Ultimately, I want to use Azure Table storage for this walkthrough but it’s nice to know that I have the flexibility to mix and match these. For this part in the series I am going to make a dummy data layer but for my overall architecture just imagine that I could use the TestEntityRepository behind the ITestEntityRepository from the second post in this series. So, here’s the code for the AudioDomainService:

    [EnableClientAccess()]
    public class AudioDomainService : DomainService
    {
        public IQueryable<AudioEntity> GetAudio()
        {
            var audioEntities = new List<AudioEntity> {new AudioEntity { Id = 1, Title = "Title1", Artist = "Artist1"}};
            return audioEntities.AsQueryable();
        }
    }

    [MetadataType(typeof(AudioEntityMetadata))]
    public class AudioEntity
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Artist { get; set; }
    }

    public class AudioEntityMetadata
    {
        [Key]
        public int Id { get; set; }
        public string Title { get; set; }
        public string Artist { get; set; }
    }

First notice I have just new’ed up a list of AudioEntity instances to act as my dummy data layer in this case (for the purpose of this demonstration it doesn’t really matter). The EnableClientAccess attribute on the class marks it as a class to use to generate the client-side code from. The RA Services code generation layer knows from inspecting the methods here that return IQueryable what are the entities and methods to transfer over to the client. You can add any methods you like in here and those returning IQueryable will later magically appear on your client.

Also the entity has a ‘buddy class’ used to define it’s metadata. This is achieved by marking up the properties with attributes. The buddy class is only necessary if the entity is actually generated externally by, for example, Linq2Sql. This method is used as you can’t mark up the generated code as you will lose your attributes each time you regenerate. So, in my case I could have just marked the AudioEntity class with attributes but I have used this to illustrate the difference.

So, this leaves one more step to link client and server.

In the Project settings for the AudioModule PRISM module:

rialink

At the bottom, there is a dropdown box called .NET RIA Services link and strangely this will have no options in it. In order to edit the link it is currently necessary to edit the project file by hand and find the following tag and type in it’s value:

    <LinkedServerProject>..RIAAudioService.WebRIAAudioService.Web.csproj</LinkedServerProject>

Now, the next time you open the project properties it will be set up correctly to point to the server-side RIA services class library project. Now, when you build the solution the client-side code part of RIA services will get generated.

RIAClientSide

In order to see the code you need to select ‘Show All Files’ at the top of the Solution Explorer and you will see a Generated_Code folder and the generated code will be in there with a suffux .Web.g.cs.

When you try to build this you will find some missing references – here’s the list of references you will need to add:

  • System.Windows.Ria
  • System.Runtime.Serialization
  • System.ServiceModel
  • System.ComponentModel.DataAnnotations

Now of course, you are enabled to use the client-side parts of RIA services.

Client-side

 

The first thing I can do is write some code like the following:

using System;
using System.Windows.Ria;
using RIAAudioService.Web;

namespace AudioModule
{
    public class AudioViewModel
    {
        public AudioViewModel()
        {
            var audioDomainContext = new AudioDomainContext();
            EntityQuery<AudioEntity> entityQuery = audioDomainContext.GetAudioQuery();

            audioDomainContext.Load(entityQuery, new Action<LoadOperation<AudioEntity>>(LoadComplete), null);
        }

        private void LoadComplete(LoadOperation<AudioEntity> obj)
        {
            
        }
    }
}

 

If you are not familiar with MVVM I would recommend you watch this video http://blog.lab49.com/archives/2650 by Jason Dolinger which is a very good introduction to the subject. In brief, the class is a ViewModel which provides properties which the xaml view will bind directly to. So, notice that we can use our entity that was defined on the server and we can asynchronously load our data.

There are a few other aspects to cover here; tooling support in VS2010 and server-defined validation – I will give a quick lowdown here but won’t go into too much detail.

The server-defined validation uses Data Annotations and you simply mark up the entity classes on the server and this metadata flows across to the client and can be used in client-side validation. See http://babaandthepigman.spaces.live.com/blog/cns!4F1B7368284539E5!167.entry?&_c02_vws=1&wa=wsignin1.0&sa=613584902 or http://msdn.microsoft.com/en-us/magazine/dd695920.aspx for further details about Data Annotations.

In visual studio 2010 go to Data > Show Data Sources from the main menu. This will open up a panel with your data sources in like the following:

RIADataSources 

These items can be manipulated and dragged out onto the designer surface to automatically create user interface elements. This doesn’t really fit with the architecture I have been trying to create as I want everything to be separate and this move will somewhat shackle the view to the data layer. However, may be useful in smaller apps and for rapid development scenarios. See here for a good video introducing vs2010 tooling support http://www.silverlight.net/learn/videos/silverlight-4-beta-videos/ria-services-support-visual-studio-2010/.

And that’s the end; I hope you enjoyed these posts! I’m pleasantly satisfied with the end result and did not expect it to be so straightforward and flexible.

Technorati Tags: ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

Windows Live Tags: Azure,AudioModule,PRISM,module,library,server,Class,Domain,Service,menu,AudioDomainContext,AudioDomainService,DomainService,context,framework,storage,series,data,TestEntityRepository,ITestEntityRepository,code,IQueryable,List,Title,Artist,AsQueryable,MetadataType,AudioEntityMetadata,purpose,demonstration,marks,generation,buddy,example,method,difference,Project,bottom,LinkedServerProject,RIAAudioService,Files,Explorer,Generated_Code,folder,System,Runtime,Serialization,ServiceModel,ComponentModel,DataAnnotations,AudioViewModel,EntityQuery,GetAudioQuery,Load,Action,LoadOperation,LoadComplete,MVVM,Jason,Dolinger,introduction,subject,ViewModel,validation,Annotations,classes,spaces,magazine,studio,panel,items,user,interface,development,result,instances,methods,options,references,aspects,metadata,blog

21
Feb
10

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

by Peter Daukintis

So, now I know how to set up a basic project, store and retrieve my data in Azure Table storage and have incorporated the asp.net providers that use Table Storage it’s time to turn my attention towards finding the right architecture to make the most out of RIA Services. I would like to use the PRISM libraries with an MVVM approach but I’m not sure how well these play with RIA Services in a Silverlight Business Application.

The first step was to get a version of PRISM that works with vs2010 – this I found here http://blogs.southworks.net/dschenkelman/2009/11/09/prism-2-composite-application-guidance-for-wpf-silverlight-migrated-to-visual-studio-2010-beta-2/

Then I imagined that the solution would consist of using PRISM’s module support to load each tab of the Silverlight Business Application on demand. This should support only downloading xap’s that the user has elected to view. I wasn’t searching for a fully dynamic solution – just a middle ground where the application is split up into separate modules – and the right environment in which to improve the design later.

The solution I went for was to create hyperlinks and related xaml files which fit with the Silverlight 4 navigation scenario but to define a PRISM ‘region’ inside each navigation page. If you are not familiar with PRISM regions or in general I would suggest that you take a look at Mike Taulty’s series here http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2009/10/27/prism-and-silverlight-screencasts-on-channel-9.aspx. The regions will act as a placeholder for the user interface which will later be supplied by the modules. So, effectively each tab is hard-coded and it’s view defines a region to be filled in later.

Here’s an example of one of the xaml pages:

<navigation:Page x:Class="PrismNav.Views.AudioModule"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           mc:Ignorable="d"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:Regions="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation" 
           d:DesignWidth="640" d:DesignHeight="480"
           Title="Audio Module">
  <Grid x:Name="LayoutRoot">
    <ItemsControl x:Name="Module2Region" Regions:RegionManager.RegionName="Module2Region"/>
  </Grid>
</navigation:Page>

and here’s an example of the link in the main xaml file:

                        <Rectangle x:Name="Divider3" Style="{StaticResource DividerStyle}"/>

                        <HyperlinkButton x:Name="AudioModuleLink" Style="{StaticResource LinkStyle}" 
                                     NavigateUri="/AudioModule" TargetName="ContentFrame" Content="Audio Module"/>

One of these are added for each required tab.

Some extra steps are required to initialise the PRISM environment:

Bootstrapper – add a bootstrapper class to the project to let PRISM know about the main window and also to intialise a module catalog.

    public class BootStrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            Shell shell = Container.Resolve<Shell>(); 
            Application.Current.RootVisual = shell; 
            return shell;
        }

        protected override IModuleCatalog GetModuleCatalog()
        {
            ModuleCatalog moduleCatalog = ModuleCatalog.CreateFromXaml(new Uri("PrismNav;component/ModuleCatalog.xaml", 
             UriKind.Relative));
            return moduleCatalog;
        }
    }
}

Where the Shell is the main page of the Silverlight app renamed to Shell.xaml (to adhere to convention). It is also necessary to kick off a call to run the bootstrapper code – this can be done like this

            var bootStrapper = new BootStrapper();
            bootStrapper.Run();
            _unityContainer = bootStrapper.Container;

inserted into the Application_Startup method in App.xaml.cs (Note that I take a reference to the bootstrapper’s unity container here – this I use to resolve creation of the Shell view class in InitializeRootVisual() as in the generated code it uses new Shell() with no ctor parameters but I have modified my Shell class to be constructor injected with the IModuleManager interface).

The module catalog you can add to the app and it’s contents are like this:

m:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 xmlns:m="clr-namespace:Microsoft.Practices.Composite.Modularity;assembly=Microsoft.Practices.Composite">
    <m:ModuleInfoGroup Ref="HomeModule.xap" InitializationMode="OnDemand">
        <m:ModuleInfo ModuleName="HomeModule.InitModule"
                      ModuleType="HomeModule.InitModule, HomeModule, Version=1.0.0.0"/>
    </m:ModuleInfoGroup>
    <m:ModuleInfoGroup Ref="AudioModule.xap" InitializationMode="OnDemand">
        <m:ModuleInfo ModuleName="AudioModule.InitModule"
                      ModuleType="AudioModule.InitModule, AudioModule, Version=1.0.0.0"/>
    </m:ModuleInfoGroup>
</m:ModuleCatalog>

This describes where the code for the modules are and some extra settings describing how the module should be loaded, etc..Now, onto creating a module:

using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;

namespace AudioModule
{
    public class InitModule : IModule
    {
        private readonly IRegionManager _regionManager;

        public InitModule(IRegionManager regionManager)
        {
            _regionManager = regionManager;
        }

        #region IModule Members

        public void Initialize()
        {
            _regionManager.RegisterViewWithRegion("Module2Region", typeof(Audio));
        }

        #endregion
    }
}

Add a new Silverlight application to the solution and empty out it’s contents and add in the code above which provides the PRISM module definition. Add a xaml file which will be the ui you wish to be injected into the region and the Initialise call above registers this modules interest in the ‘module2Region’.

So, now we have two ends of the story – these need to be connected together and here’s how to do it.

When the tabbed hyperlinks are clicked on a Silverlight Business Application there is some code in the code behind for the main shell file which is triggered – see the ContentFrame_Navigated method. This is just used to set the states of the hyperlink controls but is a handy place to load the modules. This can be done as below:

        private void ContentFrame_Navigated(object sender, NavigationEventArgs e)
        {
            string blah = e.Uri.ToString();
            if (blah.EndsWith("Module"))
            {
                blah = blah.Remove(0, 1);
                _moduleManager.LoadModule(blah + ".InitModule");
            }

            foreach (UIElement child in LinksStackPanel.Children)
            {
                HyperlinkButton hb = child as HyperlinkButton;
                if (hb != null && hb.NavigateUri != null)
                {
                    if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
                    {
                        VisualStateManager.GoToState(hb, "ActiveLink", true);
                    }
                    else
                    {
                        VisualStateManager.GoToState(hb, "InactiveLink", true);
                    }
                }
            }
        }

Please note that I have just used a ‘quick and dirty’ naming convention to get the module name from the Uri – this obviously doesn’t scale too well – but will do for now!

And that’s it – although it’s not perfect it is the start of a modular architecture. In my next post I will investigate how to have the RIA Services code play nicely with this modular architecture and allow it to generate my client side service calls + entities. 

21
Feb
10

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

by Peter Daukintis

Next up is getting the authentication service to use azure table storage.

To do this you need to get the additional azure code samples from here http://code.msdn.microsoft.com/windowsazuresamples and include the asp providers project into the solution (this will need to be up converted as the project is for vs2008). Then include a reference to this in the host website project.

For my scenario, where I am not running my app as a cloud service, I need to add settings to my web.config file to provide settings for the various providers. It was quite challenging to work out what exactly was required here but I got things working with the following in my web.config:

    <membership defaultProvider="TableStorageMembershipProvider" userIsOnlineTimeWindow = "20">
      <providers>
        <clear/>

        <add name="TableStorageMembershipProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageMembershipProvider"
             description="Membership provider using table storage"
             enablePasswordRetrieval="false"
             enablePasswordReset="true"
             requiresQuestionAndAnswer="false"
             minRequiredPasswordLength="1"
             minRequiredNonalphanumericCharacters="0"
             requiresUniqueEmail="true"
             passwordFormat="Clear"
             applicationName="YourAppName"
             accountName="youracctname"
             sharedKey="xxxxxx"
             allowInsecureRemoteEndpoints="true"
             tableServiceBaseUri="http://youracctname.table.core.windows.net/"
                />

      </providers>
    </membership>
    <roleManager enabled="true" defaultProvider="TableStorageRoleProvider" cacheRolesInCookie="true" cookieName=".ASPXROLES" cookieTimeout="30"
                     cookiePath="/" cookieRequireSSL="false" cookieSlidingExpiration = "true"
                     cookieProtection="All" >
      <providers>
        <clear/>
        <add name="TableStorageRoleProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageRoleProvider"
             description="Role provider using table storage"
             allowInsecureRemoteEndpoints="true"
             applicationName="YourAppName"
             accountName="youracctname"
             sharedKey="xxxxxx"
             tableServiceBaseUri="http://youracctname.table.core.windows.net/"
                />
      </providers>
    </roleManager>
    <sessionState mode="Custom" customProvider="TableStorageSessionStateProvider">
      <providers>
        <clear />
        <add name="TableStorageSessionStateProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageSessionStateProvider"
             allowInsecureRemoteEndpoints="true"
             applicationName="YourAppName"
             accountName="youracctname"
             sharedKey="xxxxxx"
             tableServiceBaseUri="http://youracctname.table.core.windows.net/"
             blobServiceBaseUri="http://youracctname.blob.core.windows.net/"
             />
      </providers>
    </sessionState>

I struggled a bit to get this to work but changing the passwordFormat to Clear made everything work so I have assumed that there’s a bug with the Hashed passwords currently.

By the way, I used this tool http://azurestorageexplorer.codeplex.com/ with great success for confirming whether data was being written/read correctly and whether the tables were being created successfully.

21
Feb
10

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

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….

19
Feb
10

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

by Peter Daukintis

The original idea for this post was to see whether this combination of technologies played well together. Whilst the development tools seem to support this scenario I believe that there is, as yet, no support for .NET 4 on Azure (see When .NET 4.0 will be available in Azure ?), so some of this discussion is hypothetical. The first post outlines setting up the development environment.

Anyway, first I began by doing what seemed obvious:

Having installed the required SDKs and tools for SL4, RIA services and Azure I started VS2010 (Beta 2) and created a new Windows Azure Cloud Service project, chose to add an MVC 2 Web Role by right-clicking on the Web Role folder of the newly created cloud project, added default unit test project and tried running this and it all ran fine.

Then, I added a Silverlight 4 project to be hosted in the MVC app. I did this by adding a new project to the solution, choosing Silverlight Navigation Application Project. From the options presented I chose to host the new app in the existing MVC app and I then set the aspx page which was added to the MVC project as the start-up page. Hit F5 and the build system produces this error

Error    2    Cloud Service projects currently support Roles that run on .NET Framework version 3.5.  Please set the Target Framework property in the project settings for this Role to .NET Framework 3.5.  

So, I can’t get my app running in the Azure Development Fabric due to my dependencies on .NET 4 (I was hoping that the development tools may support this scenario).

I decided to prepare the app for when this would be possible and so turned my attention towards RIA Services. It was at this stage that I realised that what I wanted to actually do was to create a Silverlight Business Application rather than a navigation application. This VS template is available when you install the RIA Services Beta. However, I had previously experimented with RIA Services using VS2008 and the corresponding RIA Services installs do not live together (at least, according to the installer as it insists that you remove any previous versions).  So, backing up a little bit, I uninstalled the VS2008 version and re-installed for 2010. I then ran through the above procedure again but chose the Business Application template rather than the navigation one.

SLBusAppTemplate

Having worked through the steps again, the result was very similar except for the addition login control:

SLBusinessApp 

So, hopefully I am now where I wanted to be, which was to have a solution that can be hosted easily in the cloud (when Azure supports .NET 4) with a development environment in which I can carry out some rapid, test-driven development.




Follow

Get every new post delivered to your Inbox.