Minggu, 23 Januari 2011

Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 21: Hierarchical Data


Wow – the gift that keeps giving.. I am *still* having fun updating my my simple Mix 09 Business Application demo. Would anyone be interested in a say 10 hour session at PDC2009 where we walk thought all of this stuff ;-). Anyway, in this section I want to take up a challenge that Ben Hayat sent me to show that Silverlight and RIA Services are really capable of building real business applications. That was too good bait for me to pass up. Apparently real business applications include more than one table, and even more than simple master details. But they also include hierarchal data. For example you have an Orders table that has an associated set of line items in a separate LineItems table, but the Orders keeps track of total sales, net, tax and number of lines, as each line gets added or deleted.

You can see the full series here.

The demo requires (all 100% free and always free):

  1. VS2008 SP1
  2. Silverlight 3 RTM
  3. .NET RIA Services July '09 Preview

Also, download the full demo files

To fit this to my SuperHero Placement Service, let’s consider the business problem of keeping up with the very witty quotes SuperHero’s often make.. And further, let’s use some advanced AI-based scoring technology to rate each quote then have the SuperEmployee keep up with their total points count.

To do this, first let’s add a Quotes table

image

Then let’s add the associations

image

And setup the foreign key relationship.. One SuperEmployee can have Many quotes.

image

Then we can refresh our Entity Framework model as we saw earlier and this gets easier to see…

image

Now we need set up the Quotes entity to return to the client. First, we need to tell Entity Framework to include Quotes in queries to the Database for SuperEmployee…

public IQueryable<SuperEmployee> GetSuperEmployees()



{



    



    return this.Context.SuperEmployeeSet



               .Include("Quotes")



               .Where(emp=>emp.Issues>10)



               .OrderBy(emp=>emp.EmployeeID);



}




Now, we need to make sure the table gets sent to the client, so in the SuperEmployeeDomainService.metadata.cs file, let’s add the Quotes property and mark it as included. This ensures that we are sending the minimal information over the wire by default.





internal sealed class SuperEmployeeMetadata



{



 



    // Metadata classes are not meant to be instantiated.



    private SuperEmployeeMetadata()



    {



    }



 



    [ReadOnly(true)]



    public int EmployeeID;



 



    [Include]



    public Quotes Quotes;




Now, we need to add an Insert method for quotes.. Notice we don’t need Query, or Add because we don’t support those operations. If you wanted to update the SuperEmployee server entity you could do that here as well…





   1: public void InsertQuote(Quotes quote)



   2: {



   3:     this.Context.AddToQuotes(quote);



   4: }




Now, on the client,I wanted a very simple UI, so I replaced the DataForm with a ListBox to list all the quotes already attributed to this SuperEmployee and then a very simple TextBox for adding a new quote.





   1: <StackPanel Margin="35,30,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="498" >



   2:     <ListBox x:Name="quotesList">



   3:         <ListBox.ItemTemplate>



   4:             <DataTemplate>



   5:                 <StackPanel Orientation="Horizontal" >



   6:                    <TextBlock Text="{Binding Quote}"></TextBlock>



   7:                     <TextBlock Text=" - "></TextBlock>



   8:                     <TextBlock Text="{Binding Points}"></TextBlock>



   9:                 </StackPanel>



  10:  



  11:             </DataTemplate>



  12:         </ListBox.ItemTemplate>



  13:     </ListBox>



  14:     <TextBlock Text="Quote:"></TextBlock>



  15:     <TextBox x:Name="newQuoteTextBox" Width="400" Height="25" TextWrapping="NoWrap" />



  16:     <Button Content="Ok" Click="Button_Click"> </Button>



  17: </StackPanel>




In lines 4-8 sets up a super simple DataTemplate for controlling how the quotes are displayed.. Then in lines 14-16 i setup a very simple form for adding a new quote.



Now, to write up the data into the form.. I could have done this with DataBinding, but I sometimes, doing it in code is just as easy..





   1: private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)



   2: {



   3:     var emp = dataGrid1.SelectedItem as SuperEmployee;



   4:     quotesList.ItemsSource = emp.Quotes;



   5: }




Finally, I need to handle the button click event..





   1: Random random = new Random(DateTime.Now.Second);



   2: private void Button_Click(object sender, RoutedEventArgs e)



   3: {



   4:     var emp = dataGrid1.SelectedItem as SuperEmployee;



   5:     var q = new Quotes();



   6:     q.Quote = newQuoteTextBox.Text;



   7:     q.SuperEmployee = emp;



   8:     



   9:     //todo: do a real point generator.. 



  10:     q.Points = random.Next(0,10);



  11:     emp.Quotes.Add(q);



  12:     newQuoteTextBox.Text = "";



  13: }




Note in line 10, we use the very advanced AI logic to determine the quality of each quote… I am told American Idol is moving to this model now that Paula Abdul is leaving ;-)



All the user needs to do now is hit Submit Changes and the the Insert method we wrote above is called for each item added.



image



Now, we’d like to show the total points for each employee.. in this scenario I only need the data for UI display, so I going to use a computed property on the client. We do that with a partial class of the SuperEmployee class, where we define the TotalCount property in the obvious way (lines 12-18). But we also need to raise a property changed notification when any new Quote is added. So we sign up for the property change notification when the Employee is loaded and rise this event (lines 3-10)..





   1: public partial class SuperEmployee 



   2: {



   3:     protected override void OnLoaded(bool isInitialLoad)



   4:     {



   5:         base.OnLoaded(isInitialLoad);



   6:         this.Quotes.EntityAdded += (s, e) =>



   7:             {



   8:                 this.RaisePropertyChanged("TotalPoints");



   9:             };



  10:     }



  11:  



  12:     public int? TotalPoints



  13:     {



  14:         get



  15:         {



  16:             return this.Quotes.Sum(q => q.Points);



  17:         }



  18:     }



  19: }




Then, we add a bit of xaml to the DataGrid and we get some our computed property..





   1: <data:DataGrid.Columns>



   2:    <data:DataGridTextColumn Header="Name" Binding="{Binding Name}" />



   3:    <data:DataGridTextColumn Header="Employee ID"  Binding="{Binding EmployeeID}" />



   4:    <data:DataGridTextColumn Header="Origin"  Binding="{Binding Origin}" />



   5:    <data:DataGridTextColumn Header="Total Points"  Binding="{Binding TotalPoints}" />   



   6: </data:DataGrid.Columns>




And the UI looks good and updates automatically.



image



Finally, let’s do a bit more data validation..



First, on the server, in the SuperEmployeeDomainService.metadata.cs we define our buddy class.. that lets us hang additional metadata..





[MetadataTypeAttribute(typeof(Quotes.QuotesMetadata))]



public partial class Quotes



{



    internal sealed class QuotesMetadata



    {



        private QuotesMetadata() { }



 



        [StringLength(140,



            ErrorMessage="Quote Text must be twitter length (less than 140 characters)")] 



        [Required]



        public string Quote;



    }



}




Then on the client, we add some UI to display the error and some code to set the error text…





<TextBlock x:Name="errorBox" Foreground="Red" ></TextBlock>






   1: try



   2: {



   3:     var q = new Quotes();



   4:     q.Quote = newQuoteTextBox.Text;



   5:     var emp = dataGrid1.SelectedItem as SuperEmployee;



   6:     q.SuperEmployee = emp;



   7:  



   8:     //todo: do a real point generator.. 



   9:     q.Points = random.Next(0, 10);



  10:     emp.Quotes.Add(q);



  11:     newQuoteTextBox.Text = "";



  12: }



  13: catch (Exception validationException)



  14: {



  15:     errorBox.Text = validationException.Message;



  16: }




image







Great, so in this part we looked at how to deal with Hierarchal Data which is very common in the orders, order detail scenario. We also showed how computed properties work. Hope you enjoy!

Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 22: Separate Solution Files


Still updating my Mix 09 Silverlight 3 + RIA Services talk with more fun stuff. This time I take up a challenge from Mr. Wildermuth. Shawn recently raised a very interesting issue with RIA Services development. He works with a lot of large enterprises that need to have separate solution files for their client and server projects. Generally we recommend the RIA Services Class Library projects for this sort of thing. But there are some cases where even more flexibility is needed. To take up this challenge, I have factored my (now famous?) SuperEmployee’s app into two solutions: one from client and one for server.


You can see the full series here.


The demo requires (all 100% free and always free):



  1. VS2008 SP1

  2. Silverlight 3 RTM

  3. .NET RIA Services July '09 Preview

Also, download the full demo files


The first thing to understand about this scenario is that all of the codegen from RIA Services actually done with MSBuild tasks that can be used outside from the VS IDE… this is important for build machine scenarios or more complex project structure (as we will see here). There are really two steps here… first we build out the web server solution, then we build a separate solution for the client. Thanks to our development manager, Jason Allor for working out these steps..


Build the Web Server:



  1. Open Visual Studio and create a new ASP.Net Web Application project. This results in a new solution containing just this server project.

  2. Add references to the RIA assemblies, including System.Web.Ria, and System.ComponentModel.DataAnnotations


image


3. Add the Silverlight div to the default.aspx page..




    <div id='silverlightControlHost'>
        <object data='data:application/x-silverlight-2,' type='application/x-silverlight-2' width='100%' height='100%'>
          <param name='source' value='ClientBin/MyApp.xap'/>
          <param name='onError' value='onSilverlightError' />
          <param name='background' value='white' />
          <param name='minRuntimeVersion' value='3.0.40624.0' />
          <param name='autoUpgrade' value='true' />
          <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style='text-decoration:none'>
               <img src='http://go.microsoft.com/fwlink/?LinkId=108181' alt='Get Microsoft Silverlight' style='border-style:none'/>
          </a>
        </object><iframe id='_sl_historyFrame' style='visibility:hidden;height:0px;width:0px;border:0px'></iframe>
    </div>

4. Hook in your back-end data source as I showed in past posts..


5. Author a DomainService class which exposes one or more query methods, insert and update methods, etc. Ensure that this class contains the EnableClientAccess attribute, again, just like i showed in past posts


6. Build the solution and ensure that it compiles without errors.


7. Close VS.


Build the Client:



  1. Open a new instance of Visual Studio and create a new Silverlight Application project. When prompted, uncheck the “Host the Silverlight application in a new Web site” checkbox. This also removes the “Enable .NET RIA Services” checkbox.
    image

  2. Add references to the RIA and data assemblies, including
    System.Windows.Ria, System.Windows.Ria.Controls, System.Windows.Data, System.Runtime.Serialization, and System.ComponentModel.DataAnnotations

  3. Save the solution.

  4. Right-click on the project node in Solution Explorer and select Unload Project
    image

  5. Right-click on the project node in Solution Explorer and select Edit .csproj

image


6. Find the <LinkedServerProject> node in the csproj file. It will initially be empty. Fill in the path to the .csproj file from the web server project that you created above.



<LinkedServerProject>..\..\MyApp.Web\MyApp.Web.csproj</LinkedServerProject>

7 . Save and close the .csproj file.


8. Right-click on the project node in Solution Explorer and select Reload Project


image


9. Build the project.


10. Notice that the Generated Code folder shows up and RIA Services has built your client-side entity model for you. You can now consume your data on the client using the standard RIA Services patterns.
image


11. Now we need to copy the resulting XAP to the web app so we can run the app correctly, this is easy enough from the project properties.


image


Open the web solution in a separate instance of VS (it is fine to have both solutions open at the same time).. After a little work in the client, we end up with this:

image


Notice, you can make changes to the client and rebuild, then just hit F5 in the browser and the new XAP will load.


So, by setting up the client's LinkedServerProject property manually, we can have the RIA Services client and server projects in different solutions, and\or build them separately in a command-line based build system. This gives you a lot of flexibility in how you manage large and complex build systems.


Hope that helps!

Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 23: Azure


Still updating my Mix 09 Silverlight 3 + RIA Services talk with more fun stuff. Azure represents a general trend in the industry towards a cloud computing model. As much as I love the development phase of projects, the operations side is often the most cost intensify part. With economies of scale, cloud computing has the potential to greatly reduce those costs. I think example I took advantage of Windows Azure to host the web-tier of the application and Sql Azure to host the data. Those are actually independent decisions. You could decide to host one and not the other.

You can see the full series here.

The demo requires (all 100% free and always free):

  1. VS2008 SP1
  2. Silverlight 3 RTM
  3. .NET RIA Services July '09 Preview
  4. Windows Azure Tools for Microsoft Visual Studio July 2009 CTP
  5. Windows Azure Invitation (apply here)
  6. Sql Azure Invitation (apply here)

Check out the live site (of course, hosted on Azure) Also, download the full demo files

So in this example, I can going to move the data from on premises Sql Server to SQL Azure AND move the applogic code from running on my own web server to running on Windows Azure. The main client will stay Silverlight.

image

Setting up SQL Azure

Once you get an invite code for SQL Azure, you can go to http://sql.azure.com and add a new database, just by clicking add new Database

image

Then you get a nice connection string..

image

Then you need to populate the database. If you have an existing database, the best way to do this likely to use Sql Management Studio to get a Sql script and then using ADO.NET to execute that script on this Sql Azure database.

But for no good reason, I wrote a very simple bit of ADO.NET code to read the data out of my local database and insert it into the Sql Azure based one. You can find it in the default.aspx.cs page if you want to check it out.

Setting up Windows Azure

That sets up our database in the cloud, now let’s create a place for our web application to run. Once you get an invite code from Windows Azure you can go in an setup your service. Go to windows.Azure.com and add a new service

image

Select the Hosted Service

image

Setting up the Project

Start up Visual Studio as an administrator… the “local cloud” support requires this right now.

image

Then create a new Cloud Service

image

For this example, all we need is a Web Role

image

I renamed “WebRole1” to MyApp.Web to match the pervious examples.

Hitting F5 starts up the “local cloud” and runs it there..

image

Creating the Application

Now, let’s start building the application. First let’s build out the data layer. I already had a Linq2Sql model from a pervious example, so I just copied that. I did tweek a few of the datatypes to match what I loaded into Azure… Entity Framework would have worked just as well.

image

Then I changed the connection string in Web.config to match the connection strings of the Sql Azure database.

<add name="NORTHWNDConnectionString" connectionString="Server=tcp:jyfmd9f7te.ctp.database.windows.net;Database=northwind;User ID=[add userid];Password=[add password];Trusted_Connection=False;"



  providerName="System.Data.SqlClient" />




Now it is just like the original walk through..



Create the Service



image



Write some code in it…







public IQueryable<SuperEmployee> GetSuperEmployees()



{



    return this.Context.SuperEmployees



               .Where(emp => Convert.ToInt32(emp.Issues) > 100)



               .OrderBy(emp => emp.EmployeeID);



}






public void InsertSuperEmployee(SuperEmployee superEmployee)



{



    this.Context.SuperEmployees.InsertOnSubmit(superEmployee);



}



 



public void UpdateSuperEmployee(SuperEmployee currentSuperEmployee)



{



    this.Context.SuperEmployees.Attach(currentSuperEmployee, this.ChangeSet.GetOriginal(currentSuperEmployee));



}




Now we need to add a Silverlight Project



image



And associate it with the MyApp.Web project and enable RIA Services.



image



Now you can simply use the exact same Silverlight client we built before.



image



You get sorting, paging, filtering, validated editing, batching all with no plumbing code and now, all hosted in cloud!





Deploying to the Cloud



Now that we have our app done, let’s deploy it to the cloud.



First we need to create the deployment package. To do that right click on the MyApp.CloudService project and Select Publish



image



The publish directory will up in explorer with your application deployment package and a separate settings file



image



The deployment package is just a zip file that contains the silverlight xap an the code and resources to execute on the server. The settings file contains things like number of front-end servers you want to create, etc.



Now, go back to windows.azure.com and select the project we created earlier.



There are two places for your app to run.. Staging and Production. The idea is you can do testing in the real environment before you switch your live site over.



Select upload to add your project to staging.



image



Select deploy..



image



The add the package and settings and give it some deployment label to help us keep track of its movements between staging and production.



image



Then click run to start the instance running. Then it will say “initializing…” for a few minutes



image





Finally, when it says “started” you can run the app by clicking on the test link:





image





Next swap this into the deployment stage and you are live!



image





And we are done!



image



image



In this section we talked about how to deploy your RIA Services application to the cloud using Sql Azure for the data and Windows Azure for the web tier. You can mix and match those however you want.



Also, check out Nikhil’s example on Azure, it uses Windows Azure table storage which is very cool as well.