Minggu, 23 Januari 2011

Index for Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update


Thanks to all of you who asked… I had to take a sick day today, so this was nice mindless work to get done. Hope you enjoy it!


Part 1: Navigation Basics [Spanish]


Part 2: Rich Data Query [Spanish]


Part 3: Authentication [Spanish]


Part 4: SEO, Export to Excel and Out of Browser


Part 5: Astoria, Add Service Reference and WinForms


Part 6: Data Transfer Objects (DTOs)


Part 7: ADO.NET Data Services Based Data Store


Part 8: WCF Based Data Source


Part 9: POCO and Authentication Provider


Part 10: LinqToSql


Part 11: The Client-Only World


Part 12: DataSet


Part 13: The New Class Library Project


Part 14: Visual Basic (VB) and WPF Support


Part 15: ASP.NET MVC


Part 16: Exposing a WCF Service


Part 17: Evolving an Application


Part 18: Custom Linq Provider


Part 19: ASP.NET Dynamic Data


Part 20: NHibernate


Part 21: Hierarchical Data


Part 22: Separate Solution Files


Part 23: Azure


Part 24: Stored Procedures


Part 25: ViewModel


Part 26: Authentication and Personalization


Comments about Adding…


Comments about extracting the samples from a zip file


Summary



Thanks to David Mora for translation of some of these posts into Spanish.

Lightweight Linq Parser

Lightweight Linq Parser:

Jason Allor, dev manager for my team, recently posted about a cool lightweight LINQ parser. The idea was to make it much easier to create a LINQ provider over any arbitrary data source (a web service, custom business objects, etc). This makes it much easier to get all the sorting, paging, filtering benefits of RIA Services for any arbitrary data store.

Check it out, and be sure to subscribe to Jason’s blog as I expect he will have more goodies coming in the future!

LinqLite

We Need Your Feedback on the Documentation


image The doc teams are looking for your feedback on the .NET Framework and Visual Studio docs. Help us improve the developer documentation by taking the Visual Studio and .NET Framework Content Survey. This survey will give us a better understanding of the type of applications you are developing as well as how you use Help and how we can improve it. The survey takes only 10 minutes, and we really appreciate your feedback! Feel free to forward the survey link.

http://silverlight.net/riaservices/ is Live!


We have been working for a while to get a community site together where we can aggregate all the great buzz, resources and discussions about .NET RIA Services. We hope you enjoy the content we already have up there and help us get more good stuff up there.

http://silverlight.net/riaservices/

image

Welcome to WCF RIA Services Beta!


Today at PDC09, Scott Guthrie Announced the beta of WCF RIA Services. As you may know RIA Services is a set of end-to-end experiences that makes it as easy to build Silverlight based n-tier applications as it is to build traditional 2 tier apps. I am very excited about this release as it represents a lot of great feedback we have heard from many of you.


A few goodies in this release based on your feedback



  • You told us you wanted to get up and running with the UI quicker, so we enabled the “DataSources” window that allows you to drag and drop “tables” exposed by your Domain Service onto the form and get meaningful UI to start with. No more guessing about the Xaml syntax for databinding, or incantation for layout, etc.

image



  • You told us error handling was not intuitive\consistent with the rest of the framework, so we we simplified error handing both on the server and client. This will help errors get noticed quicker at development time so they don’t sneak into production

  • You told us you wanted Inheritance support from the Data Model to just flow through the Domain Service to the client, so we gave it to you, works exactly as you’d expect.

  • You told us you didn’t always want to expose DAL types to the client, but rather a custom “presentation model” where you can customize the shape of the entities, so we gave it to you.. and in a complete way where we handle all the update and error cases.

  • You told us you wanted the fastest possible communication channel between the client and the server, so we gave an optimized binary channel by default!

  • You told us it was a pain to install so we integrated the RIA Services installer into the Silverlight 4 installer making it very easy to get started. We also created a “server only” installer to use on your production boxes.

  • You told us real world data models use a lot of Compositional hierarchy (Order->OrderDetails) so we have improved the handling of this scenario by making it more built in.

  • You told us you wanted GAC *and* bin deployment. So we gave it to you. By default RIA Services look for our server assemblies in the GAC as this is the most secure and efficient model, but just like ASP.NET MVC, you can simply select “copy local” in your VS project and those assemblies work fine bin deployed.

  • You told us you really liked the easy on ramp with the Business Application Template, but that you wanted it to be more complete. So we added globalization support, user state example, persisted sign in, etc.

  • You told us you wanted to go live today (and many of you already have) so we are providing .NET Framework 3.5\Visual Studio 2008\Silverlight 3 based bits that are ready for you to go live on today! We hope that this release will be a bridge to you that will make it easier for you to (eventually, when you are ready) move to our finally RTM bits which will be on .NET Framework 4, Visual Studio 2010 and Silverlight 4.

  • You gave us a LOT of other feedback as well, some of which will come in V1, and some will have to wait until the next releases, but please keep it coming.

WCF: What’s in a Name?


Many of you will notice the slight change in the branding around RIA Services. RIA Services is now part of the WCF family (as is ADO.NET Data Services, which is now WCF Data Services). This branding change is a direct result of some significant work we are releasing in this beta and it hopefully makes it very clear that we have one technology base for doing services on the .NET platform and that is WCF.


Our driving principle with using WCF has been “All the power and none of the complexity”. That is we wanted to enable all the power that WCF brings (for example, the binary end-point, data contract serialization, all the extensibility points, fully management support with the AppFabric we announced yesterday). But none of the complexity. Just look at the web.config file… there is no tedious config to get right.. there is no fragile contract interface and implementation class to keep in sync, none of the deployment headache around configuration. This is still the RIA Services model where you can expect a deep simplicity across development, deployment and maintenance. We accomplish this by creating a custom service host at runtime based on how you define your DomainService.


I am also very excited to get OData support into RIA Services. OData is the protocol from “Astoria” that is already widely used in products like Sharepoint and PowerPivot. Look for more details on that in future.


Check out Henrik Frystyk Nielsen’s talk Developing REST Applications with the .NET Framework which will cover how WCF Data Services and WCF RIA Services work together.


Call to Action


1. Please install Visual Studio 2010 and Silverlight 4 (this includes RIA Services)


2. Build something cool and tell us about it!


3. Send in your feedback.. we want to hear what rocked, and what needs more polish



(2/15/10) Note: This does not work on VS2010 RC -- Look for an update in the next month or so.



RIA Services: A DomainService IS A WCF Service – Add Service Reference


I made the fairly bold statement at my PDC09 talk that a DomainService IS A WCF Service. That is, everything you know about a WCF service should be true of a DomainService. I didn’t have time to get into this in my talk, so I thought I’d hit the highlights here. And in the process show how to consume a DomainService from a WinForms. You can also see more examples at: http://code.msdn.microsoft.com/RiaServices

You need:

You can download the completed solution as well. and be sure to check out the full talk.

1. Getting to the Service

The first thing we need to do is get at the data underlying service. In the mainstream Silverlight case this is all handled for you by the implicit link between the Silverlight client and the ASP.NET server. However, in the vanilla WCF case, you get the full control. The URL to the service is of the following format:

http://[hostname]/[namespacename]-[classname].svc

so in my case that is:

http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc

Hitting that URL in the browser gives you the very familiar WCF proxy help screen:

image

And tacking on the ?wsdl gives you the WSDL for this service

http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc?wsdl

image

The rest is easy for anyone halfway familiar with WCF… Create a new WinForms project and select Add Service Reference. Enter the URL (note discover doesn’t work for this sort of service yet)…

image

The you have a service!

2. Querying for the Data

Now, we have a service, let’s look at actually getting data out of it. In this case I already have a WinForms DataGridView on my form. So getting data into it should be no problem.

  1. private void Form1_Load(object sender, EventArgs e)
  2. {
  3. var context = new DishViewDomainServiceClient('BasicHttpBinding_DishViewDomainService');
  4. var plates = context.GetPlates(4);
  5. this.dataGridView1.DataSource = plates.RootResults;
  6. foreach (DataGridViewRow row in dataGridView1.Rows)
  7. {
  8. PlatesListOriginals.Add(ToPlate(row));
  9. }
  10. dataGridView1.CellEndEdit += dataGridView1_CellEndEdit;
  11. dataGridView1.SelectionChanged += dataGridView1_SelectionChanged;
  12. }

In line 3, we create a new instance of the web service client and point it at the right binding. The service exposes a couple of different bindings as you can see in the app.config file for the WinForms app:

  1. <endpoint address='http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc/soap'
  2. binding='basicHttpBinding' bindingConfiguration='BasicHttpBinding_DishViewDomainService'
  3. contract='ServiceReference1.DishViewDomainService' name='BasicHttpBinding_DishViewDomainService' />
  4. <endpoint address='http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc/binary'
  5. binding='customBinding' bindingConfiguration='BinaryHttpBinding_DishViewDomainService'
  6. contract='ServiceReference1.DishViewDomainService' name='BinaryHttpBinding_DishViewDomainService' />

In line 4, we call the service to get our list of Plates… in this case we are doing things synchronously.. you could of course do it async if you’d like.

In line 5, we bind the DataGridView to the results of this call.

In lines 6-9, we are saving off the “original” values.. for each item we got.. this will help us when we do updates.

In line 10, we handle the cell edit event, we will come back to look at that later.

in line 11, we sign up for the selection changed event so we can initialize the picture…

  1. void dataGridView1_SelectionChanged(object sender, EventArgs e)
  2. {
  3. Plate currentPlate = ToPlate(dataGridView1.CurrentRow);
  4. this.pictureBox1.ImageLocation = 'http://hanselman.com/abrams/Images/Plates/' + currentPlate.ImagePath;
  5. }

Be patient with this one… sometimes it takes a while load a picture. it is using hanselman’s server which gets slammed sometimes ;-)

image

Now we have our data, we can scroll through it and view the pretty pictures.

3. Updating the Data

But how do we update the data… well, let’s take a look at CellEditEnd event handler…

  1. void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
  2. {
  3. var context = new DishViewDomainServiceClient('BasicHttpBinding_DishViewDomainService');
  4. Plate currentPlate = ToPlate(dataGridView1.Rows[e.RowIndex]);
  5. ChangeSetEntry[] changeSet = new[] {
  6. new ChangeSetEntry{
  7. OriginalEntity = PlatesListOriginals[e.RowIndex],
  8. Entity = currentPlate,
  9. Operation = DomainOperation.Update
  10. }
  11. };
  12. context.SubmitChanges(changeSet);
  13. }

In line 3, we are creating a new context. we could be sharing with the load method, but I thought this would be cleaner to follow.

In line 4, we save off the currently selected plate.

In lines 5-10 we are building up a changeset to send to the server.

Notice we need to give it the original values we saved off in the load method. Getting the original values right is the likely the hardest part here. Keep in mind that assignment in C# (and VB) is by default by reference. So you can’t just store off a reference, you must actually make a copy of the original values.

Then in line 12, we submit the changes.

Make a change, tab off it.. This will call the server and post your update. Re run the app to see that it took.

image

Notice here we are sending one item in the change set. You could of course build up a change set on the client with many entries and then send them as a batch.

I hope that helps to make it clear how a DomainService IS A WCF Service… You can download the completed solution as well. and be sure to check out the full talk.

Silverlight and RIA Services: Implementing Search


Some of you likely noticed that my PDC09 demo included a stubbed out Search function that I didn’t really get to walkthrough during the talk. I thought I’d do a blog post showing how it is done.

To get started you need:

You can download the completed solution as well. and be sure to check out the full talk.

image

First let’s write a new method on the DomainService to return the search results. In this case I want to return any Plates who’s name matches (grouped by Restaurant) and any Restaurant who’s name matches.

  1. public IQueryable<Restaurant> SearchRestaurants(string term)
  2. {
  3. //Find all plates that match, grouped by restaurant
  4. var restaurantPlatesList = this.ObjectContext.Restaurants
  5. .Where (r => r.Plates.Any (p=>p.Name.Contains(term)))
  6. .OrderBy(k => k.ID);
  7. foreach (var restaurant in restaurantPlatesList)
  8. {
  9. var plateList = ObjectContext.Plates
  10. .Where(p => p.RestaurantID == restaurant.ID)
  11. .Where(p => p.Name.Contains(term));
  12. foreach (var plate in plateList)
  13. restaurant.Plates.Add(plate);
  14. }
  15. //Find all restaurants that match
  16. var restaurantsList = this.ObjectContext.Restaurants
  17. .Where(r => r.Name.Contains(term) &&
  18. !restaurantPlatesList.Contains(r));
  19. return restaurantPlatesList.Concat(restaurantsList);
  20. }

In line 1, you see that we are defining a new query that returns Restaurants… this one has a different name, and takes a search term as an argument.

In line 4-6, we are getting all the restaurants that that have any plates that match the search term.

In line 7-14, we are looping through all those restaurants and manually adding the plates that match to the collection we will return. Effectively, we are manually creating the instance in just the shape we need it for the client.

In line 17-18, we are getting the Restaurants that match and that are not already included.

and finally, in line 21, we return the concatenation of the two queries.

Notice that this query is designed to return Restaurants AND the Plates that are related. By default, related entities are not included (in order to save bandwidth).. so we need to go into the metadata file and explicitly include them.

  1. internal sealed class RestaurantMetadata
  2. {
  3. [Include]
  4. public EntityCollection<Plate> Plates;

Now, in the Silverlight client. First we need to wire up the Search button on MainPage.xaml.. There we just need to follow the same pattern we saw in the earlier post:

  1. private void button1_Click(object sender, RoutedEventArgs e)
  2. {
  3. ContentFrame.Navigate(new Uri('/Search?term=' + searchBox.Text,
  4. UriKind.Relative));
  5. }

Then the search page, again we build it the same way as we saw in the earlier post, by simply drag and dropping from the data sources window. Notice that we now have two different query methods for Restaurants. So we simply select the right one and then drag and drop on the form as we saw in the earlier post:

image

After we get the UI laid out correctly you end up with a pager and all the columns set right.

image

Then wire up the parameter to the SearchRestaurants query method to the query string…

  1. protected override void OnNavigatedTo(NavigationEventArgs e)
  2. {
  3. this.restaurantDomainDataSource.QueryParameters.Add(
  4. new System.Windows.Data.Parameter()
  5. {
  6. ParameterName = 'term',
  7. Value = NavigationContext.QueryString['term']
  8. });
  9. }

Now i’d like to display the Plates for each Restaurant that is returned. To do that, i’ll make use of the RowDetails feature of DataGrid.

  1. <data:DataGrid.RowDetailsTemplate>
  2. <DataTemplate>
  3. <ListBox ItemsSource='{Binding Plates}">
  4. <ListBox.ItemTemplate>
  5. <DataTemplate>
  6. <StackPanel Orientation="Horizontal">
  7. <Image Source='{Binding Path=IconPath, Converter={StaticResource ImagePathConverter1}}'
  8. Margin="40,0,10,0"></Image>
  9. <StackPanel>
  10. <TextBlock Text='{Binding Path=Name}' Width='400'
  11. TextWrapping='Wrap'
  12. FontWeight="Bold"></TextBlock>
  13. <TextBlock Text='{Binding Path=Description}' Width='400'
  14. TextWrapping="Wrap"></TextBlock>
  15. <HyperlinkButton Click='HyperlinkButton_Click'
  16. Content='Details...'
  17. NavigateUri='{Binding ID}" />
  18. </StackPanel>
  19. </StackPanel>
  20. </DataTemplate>
  21. </ListBox.ItemTemplate>
  22. </ListBox>
  23. </DataTemplate>
  24. </data:DataGrid.RowDetailsTemplate>

Run it and we get this sort of view…

image

Notice the URL includes the search term, so I can send this around in email to share my search results bookmark it for future reference.

Now, you might want to drill into the details on what of these plates… so let’s handle that “Details..” hyperlink. In the code behind for the search page, we handle navigating to the Plates page with a the right query string paramaters.

  1. private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
  2. {
  3. var button = sender as HyperlinkButton;
  4. var plate = button.DataContext as Plate;
  5. NavigationService.Navigate(
  6. new Uri('/Plates?restaurantId=' + plate.RestaurantID + "&" +
  7. 'plateId=' + plate.ID,
  8. UriKind.Relative));
  9. }

Now we need to make a slight tweak to the Plates page because it does no know about the plateId query string parameter.

  1. protected override void OnNavigatedTo(NavigationEventArgs e)
  2. {
  3. //Handle RestaurantID
  4. plateDomainDataSource.QueryParameters.Add(
  5. new System.Windows.Data.Parameter()
  6. {
  7. ParameterName = 'resId',
  8. Value = NavigationContext.QueryString['restaurantId']
  9. });
  10. //Handle PlateID
  11. var qs = NavigationContext.QueryString;
  12. if (qs.ContainsKey('plateId'))
  13. {
  14. this.plateDomainDataSource.FilterDescriptors =
  15. new FilterDescriptorCollection();
  16. this.plateDomainDataSource.FilterDescriptors.Add(
  17. new FilterDescriptor('ID',
  18. FilterOperator.IsEqualTo, qs['plateId']));
  19. }
  20. }

The first few lines to handle the RestaurantID were already there, so we just needed to add the code to handle the PlateID.. Notice we don’t need to change the query method on the server for this, we just add a new where clause that will get sent to the server and executed there.

The result:

image

Again, notice the URL, something we can bookmark or send around in email, etc.

I hope you got something valuable from this walkthrough… You can download the completed solution as well and be sure to check out the full talk.

Have fun!