| More
All posts by tim star
 


Guidance for Early Adopters of Visual Studio 2010

If you are an early adopter of Visual Studio 2010 I want to point you to some very good online resources.  All of these links point to resources created by the ALM RangersI am one of the ALM Rangers which means I volunteer to spend some of my off hours (aka nights and weekends) working with the MS product groups to identify missing features and/or come up with  guidance for common scenarios.   It is really cool to be able to work with the product teams (the folks who write the VS code) to find out exactly how features were meant to be used and maybe even cooler to work with the product teams and other rangers (literally around the world) to figure out how to use Visual Studio to do things it wasn?t designed to do.

The official ranger mission is to provide out of band solutions for missing features or guidance. The content in these links was created with support from Microsoft Product Group, Microsoft Most Valued Professionals (MVPs) and technical specialists from technology communities around the globe, giving you a real-world view from the field, where the technology has been tested and used.

Web and Load Testers:

If you do any sort of web performance testing or load testing with visual studio you need to read this.  This is top notch information.  If I have any questions about  the behavior of the tool or best practices, this is my first and often only stop.

http://vsptqrg.codeplex.com/releases/view/42484

TFS Integration:

Here is some great guidance on making the move to TFS 2010.

We were actually able to use the the TFS Integration tools to get one customer hooked up with the new test tools when TFS 2010 was still in beta and keeping the TFS 2008 production database synched with the new TFS 2010 database until the release bits were available a couple months later.

http://tfsintegration.codeplex.com/releases/view/35476

Here is some more links to to some information and or labs where I at most acted as a reviewer on some small portion of the content.

Visual Studio 2010 Quick Reference Guidance

Visual Studio Team Foundation Server Branching Guide 2010

Visual Studio 2010 Team Foundation Server Requirements Management Guidance

Visual Studio 2010 and Team Foundation Server 2010 VM Factory

Coming Soon:

(looking forward to this as so many of my customers use database projects to manage their database)

Visual Studio 2010 Database Guidance

Visual Studio 2010 Architecture Tooling Guidance


Posted by: Tim Star
Posted on: 7/13/2010 at 7:21 PM
Tags: , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Silverlight 3, the CollectionViewSource and sorting

In my previous post we were introduced to the CollectionViewSource. We have multiple ItemSources binding to the same collection using a CollectionViewSource ? very powerful.  Now what if we want to sort the items displayed in one of our grid cells?  We will focus on row one and column one for the rest of this post.

I?ve added some more default data to the Control?s constructor to make things a little more interesting and also notice I have added a few buttons to the bottom of our control:

image

The Sort by Name button adds a sort description to the CollectionViewSource

theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeName",ListSortDirection.Ascending));

This code tells the CollectionViewSource to sort the items in it?s view by the SomeName property in ascending order.  Click the button and we see the following:

image

Click the Clear Sort Descriptions button.

The Sort By Value button tells the CollectionViewSource to sort the items in the view by the SomeValue Property in ascending order.

theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeValue", ListSortDirection.Ascending));

Click the button to see the following:

image

This time our list is sorted by the SomeValue property.

Where things get interesting is that you can add multiple sort descriptions.  Try clearing the filter, then adding the name and value filter (in that order) and we see the following:

image

The collection is first sorted by name, then by value.

Clear the filter and click the Value, then name button and we see this:

image

The collection is first sorted by SomeValue, then by SomeName.

The order is important!  If you want to add a sort description to the top of the sort order, simply use the Insert method as follows:

theDataContext[0].Column1.View.SortDescriptions.Insert(0,new SortDescription("SomeValue", ListSortDirection.Ascending));

Here is the modified XAML:



Code Snippet



  1. <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="SilverlightApplication1.MainPage"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  6.     <UserControl.Resources>
  7.         <Style TargetType="Button">
  8.             <Setter Property="FontSize" Value="9"/>
  9.         </Style>
  10.     </UserControl.Resources>
  11.   <Grid x:Name="LayoutRoot">
  12.         <ScrollViewer VerticalScrollBarVisibility="Auto">
  13.             <StackPanel Orientation="Vertical">
  14.             <data:DataGrid x:Name="TheMainDataGrid"  AutoGenerateColumns="False" ItemsSource="{Binding}" HeadersVisibility="Column">
  15.                 <data:DataGrid.Columns>
  16.                     <data:DataGridTemplateColumn Header="Column 1">
  17.                         <data:DataGridTemplateColumn.CellTemplate>
  18.                             <DataTemplate>
  19.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column1.View}" >
  20.                                     <ListBox.ItemTemplate>
  21.                                         <DataTemplate>
  22.                                             <StackPanel Orientation="Horizontal">
  23.                                                 <TextBlock Margin="10" Text="{Binding Path=SomeName}" />
  24.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  25.                                             </StackPanel>
  26.                                         </DataTemplate>
  27.                                     </ListBox.ItemTemplate>
  28.                                 </ListBox>
  29.                             </DataTemplate>
  30.                         </data:DataGridTemplateColumn.CellTemplate>
  31.                     </data:DataGridTemplateColumn>
  32.                     <data:DataGridTemplateColumn Header="Column 2">
  33.                         <data:DataGridTemplateColumn.CellTemplate>
  34.                             <DataTemplate>
  35.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column2.View}" >
  36.                                     <ListBox.ItemTemplate>
  37.                                         <DataTemplate>
  38.                                             <StackPanel Orientation="Horizontal">
  39.                                                 <TextBlock Margin="10" Text="{Binding SomeName}" />
  40.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  41.                                             </StackPanel>
  42.                                         </DataTemplate>
  43.                                     </ListBox.ItemTemplate>
  44.                                 </ListBox>
  45.                             </DataTemplate>
  46.                         </data:DataGridTemplateColumn.CellTemplate>
  47.                     </data:DataGridTemplateColumn>
  48.                 </data:DataGrid.Columns>
  49.             </data:DataGrid>
  50.             <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
  51.                 <TextBlock Margin="5" Text="Name:"/>
  52.                 <TextBox x:Name="tbNAme" Margin="5" Width="100"></TextBox>
  53.                 <TextBlock Margin="5" Width="100" Text="Value:"/>
  54.                 <TextBox x:Name="tbValue" Margin="5" Width="100"></TextBox>
  55.                 <Button x:Name="AddButton" Margin="5" Content="Add" Click="AddButton_Click"/>
  56.             </StackPanel>
  57.             <StackPanel HorizontalAlignment="Left">
  58.                 <Button x:Name="ChangeNameButton" Width="150" Content="Change Name" Click="ChangeNameButton_Click"/>
  59.                 <Button x:Name="ChangeValueButton" Width="150" Content="Change Value" Click="ChangeValueButton_Click"/>
  60.                 <Button x:Name="refresh" Width="150" Content="Refresh CVS" Click="refresh_Click" />
  61.                 <Button x:Name="AddNameSort" Width="150" Content="Sort by Name" Click="AddNameSort_Click"/>
  62.                 <Button x:Name="AddValueSort" Width="150" Content="Sort by Value" Click="AddValueSort_Click"/>
  63.                 <Button x:Name="ClearSort" Width="150" Content="Clear Sort Descriptions" Click="ClearSort_Click"/>
  64.             </StackPanel>
  65.         </StackPanel>
  66.             </ScrollViewer>
  67.   </Grid>
  68. </ UserControl>




Here is the modified code behind:



Code Snippet



  1. using System.Collections.ObjectModel;
  2. using System.Windows.Controls;
  3. using System.Windows.Data;
  4. using System.ComponentModel;
  5. namespace SilverlightApplication1
  6. {
  7.     public partial class MainPage : UserControl
  8.     {
  9.         ObservableCollection<TestClass> theMainCollection ;
  10.         ObservableCollection<TheRowClass> theDataContext;
  11.         public MainPage()
  12.         {
  13.             InitializeComponent();
  14.             theMainCollection = new ObservableCollection<TestClass>()
  15.                 { new TestClass() {SomeName="A",SomeValue=11},
  16.                     new TestClass() {SomeName="A",SomeValue=12},
  17.                     new TestClass() {SomeName="A",SomeValue=10},
  18.                     new TestClass() {SomeName="AB",SomeValue=21},
  19.                     new TestClass() {SomeName="A",SomeValue=13},
  20.                     new TestClass() {SomeName="B",SomeValue=13},
  21.                     new TestClass() {SomeName="B",SomeValue=14},
  22.                     new TestClass() {SomeName="AB",SomeValue=11},
  23.                     new TestClass() {SomeName="BC",SomeValue=22},
  24.                     new TestClass() {SomeName="AB",SomeValue=23},
  25.                     new TestClass() {SomeName="BC",SomeValue=24}
  26.                 };
  27.           
  28.             theDataContext = new ObservableCollection<TheRowClass>();
  29.             theDataContext.Add(new TheRowClass(theMainCollection));
  30.             theDataContext.Add(new TheRowClass(theMainCollection));
  31.             TheMainDataGrid.DataContext = theDataContext;
  32.             AddFilter(theDataContext[0].Column1, 1, 1);
  33.             AddFilter(theDataContext[0].Column2, 1, 2);
  34.             AddFilter(theDataContext[1].Column1, 2, 1);
  35.             AddFilter(theDataContext[1].Column2, 2, 2);
  36.            
  37.         }
  38.         private void AddFilter(CollectionViewSource cvs, int row, int column)
  39.         {
  40.             cvs.Filter += delegate(object o, FilterEventArgs eArgs)
  41.             {
  42.                 //Simple filter saying if column = 1 look for an A in the SomeName property
  43.                 //Else look for a B.  If we are in Row 1 look for a 1 in the SomeValue property
  44.                 //Else look for a 2
  45.                 string columnFilter;
  46.                 if (row == 1)
  47.                     columnFilter = "A";
  48.                 else
  49.                     columnFilter = "B";
  50.                 if ((eArgs.Item as TestClass).SomeValue.ToString().Contains(column.ToString()) && (eArgs.Item as TestClass).SomeName.Contains(columnFilter))
  51.                     eArgs.Accepted = true;
  52.                 else
  53.                     eArgs.Accepted = false;
  54.             };
  55.         }
  56.         private void AddButton_Click(object sender, System.Windows.RoutedEventArgs e)
  57.         {
  58.             TestClass tc = new TestClass();
  59.             tc.SomeName = tbNAme.Text;
  60.             tc.SomeValue = int.Parse(tbValue.Text);
  61.             theMainCollection.Add(tc);
  62.         }
  63.         private void ChangeNameButton_Click(object sender, System.Windows.RoutedEventArgs e)
  64.         {
  65.             theMainCollection[theMainCollection.Count-1].SomeName = "B";
  66.         }
  67.         private void ChangeValueButton_Click(object sender, System.Windows.RoutedEventArgs e)
  68.         {
  69.             theMainCollection[theMainCollection.Count - 1].SomeValue = 25;
  70.         }
  71.         private void refresh_Click(object sender, System.Windows.RoutedEventArgs e)
  72.         {
  73.             theDataContext[0].Column1.View.Refresh();
  74.             theDataContext[1].Column1.View.Refresh();
  75.             theDataContext[0].Column2.View.Refresh();
  76.             theDataContext[1].Column2.View.Refresh();
  77.         }
  78.         private void AddNameSort_Click(object sender, System.Windows.RoutedEventArgs e)
  79.         {
  80.             theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeName",ListSortDirection.Ascending));
  81.         }
  82.         private void AddValueSort_Click(object sender, System.Windows.RoutedEventArgs e)
  83.         {
  84.             theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeValue", ListSortDirection.Ascending));
  85.         }
  86.         private void ClearSort_Click(object sender, System.Windows.RoutedEventArgs e)
  87.         {
  88.             theDataContext[0].Column1.View.SortDescriptions.Clear();
  89.         }
  90.     }
  91. }




See my previous post to get the supporting code.


Posted by: Tim Star
Posted on: 7/5/2010 at 8:56 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Silverlight 3 and the CollectionViewSource

I have been working on a really cool Silverlight application for the last several weeks.  The application is a graphically intensive businesses application with some pretty challenging lists nested in a grid and some very cool drag and drop functionality.  Here is the scenario: 

When the app starts up we make a call to a WCF service and get several hundred to several thousand objects back in an observable Collection.  We slice, dice, and manipulate the ObservableCollection<T> and now the challenge is we have to present several dozen to several hundred different views into this same data.  Throwing several hundred different views of the same data at a user all at once is not particularly useful so the views are stored in cells of a scrollable grid.  As I mentioned, each cell in the grid is a list of items that needs to be generated based on some attributes of the column and row in the grid.  Conceptually I want to break my original collection into a bunch of smaller collections but I didn?t want to physically break the collection  into multiple collections because the drag and drop functionality I spoke of would force me to get into the business of removing an item from one collection and adding it to another.  Additionally once the items are placed where we want them I would have to reassemble the master collection and send it back to the server.  Using a single ObservableCollection<T>) collection is a good fit.  How do we get multiple independent views into this ObservableCollection<T>) ? 

Enter the CollectionViewSource.  The CollectionViewSource allows us to set a source property (in our case the ObservableCollection<T> we got from the WCF call) then set one or many filters and finally we bind each cell to the View property of the CollectionViewSource to allow us to get a list of objects that satisfy our filter criteria.

Let?s whip up a very simple application and demonstrate the behavior. 

Here is the screen:  We have a grid with 2 rows and 2 columns.  Each Cell represents a list of type TestClass.  TestClass has two properties, SomeName ? a string property, and SomeValue an integer property.  Below the grid we have two text boxes and and add button.  This will allow us to demonstrate that we can add items to the observable collection and have the UI automatically update.  Ignore the bottom 3 buttons for the moment.

image

Rather than make a WCF call we are building the ?Main? collection of objects  in the MainPage constructor:



Code Snippet



  1.             theMainCollection = new ObservableCollection<TestClass>()
  2.                 { new TestClass() {SomeName="A",SomeValue=11},
  3.                     new TestClass() {SomeName="A",SomeValue=12},
  4.                     new TestClass() {SomeName="B",SomeValue=13},
  5.                     new TestClass() {SomeName="B",SomeValue=14},
  6.                     new TestClass() {SomeName="AB",SomeValue=21},
  7.                     new TestClass() {SomeName="BC",SomeValue=22},
  8.                     new TestClass() {SomeName="AB",SomeValue=23},
  9.                     new TestClass() {SomeName="BC",SomeValue=24}
  10.                 };



Now we need to create a collection for the grid to bind to.  Remember, what we want is for each grid cell to be a view into our ?main? collection.  In our Example we are using a DataGrid object to display our views and we want the DataGrid to automatically update if we add an object to or remove an object from the main collection .  What we need here is an ObeservableCollection of objects with a CollectionViewSource property for each column.  In our example we have called the object TheRowClass. TheRowClass has two properties of type CollectionViewSource; Column1 and Column2.

Here is the code I use to initialize the ObservableCollection<TheRowClass>:



Code Snippet



  1.             theDataContext = new ObservableCollection<TheRowClass>();
  2.             theDataContext.Add(new TheRowClass(theMainCollection));
  3.             theDataContext.Add(new TheRowClass(theMainCollection));



We have two items in the ObservableCollection<TheRowClass>, each item has two properties of type CollectionViewSource for a total of four CollectionViewSource objects ? one for each cell.  We are passing the main collection into the constructor and using it as the Source property for all of our CollectionViewSources.  All we have left to do is add a filter to each of these collections.  The filter we are adding is very simple:

if TestClass.SomeName contains the letter A and TestClass.SomeValue contains the number 1 display the item in row 1, column 1

if TestClass.SomeName contains the letter A and TestClass.SomeValue contains the number 2 display the item in row 1, column 2

if TestClass.SomeName contains the letter B and TestClass.SomeValue contains the number 1 display the item in row 2, column 1

if TestClass.SomeName contains the letter B and TestClass.SomeValue contains the number 2 display the item in row 2, column 2

Here is the code to pull that off:



Code Snippet



  1.             AddFilter(theDataContext[0].Column1, 1, 1);
  2.             AddFilter(theDataContext[0].Column2, 1, 2);
  3.             AddFilter(theDataContext[1].Column1, 2, 1);
  4.             AddFilter(theDataContext[1].Column2, 2, 2);



And the AddFilter method:



Code Snippet



  1.         private void AddFilter(CollectionViewSource cvs, int row, int column)
  2.         {
  3.             cvs.Filter += delegate(object o, FilterEventArgs eArgs)
  4.             {
  5.                 //Simple filter saying if column = 1 look for an A in the SomeName property
  6.                 //Else look for a B.  If we are in Row 1 look for a 1 in the SomeValue property
  7.                 //Else look for a 2
  8.                 string columnFilter;
  9.                 if (row == 1)
  10.                     columnFilter = "A";
  11.                 else
  12.                     columnFilter = "B";
  13.                 if ((eArgs.Item as TestClass).SomeValue.ToString().Contains(column.ToString()) && (eArgs.Item as TestClass).SomeName.Contains(columnFilter))
  14.                     eArgs.Accepted = true;
  15.                 else
  16.                     eArgs.Accepted = false;
  17.             };
  18.         \



Run the app, notice that the item with SomeName=?AB? and SomeValue=21 is in all of the cells, this is because that item satisfies the filter criteria for each CollectionViewSource in each cell.  Very cool. 

 

Add the letter ?A? to the first textbox and 111 to the second textbox and click the add button.  Notice that an item is added to column 1 and row 1 of the grid without doing anything more than making a call to Collection.Add.

image

Play around with a few more combinations and see how the grid updates.

Now let?s look at the bottom 3 buttons, the first button changes the SomeName value of the last item in the main collection to ?B?.  Here is the event handler:

theMainCollection[theMainCollection.Count-1].SomeName = "B";

Again, add the letter ?A? to the first textbox and 111 to the second textbox and click the add button.  Now click the Change Name button.  Notice that nothing happens.  I assure The item has changed.

Now click the Change Value button.  Notice that the last item in row 1 cell 1 has changed, the value has become 25.

image

We changed the SomeValue property to 25 in the button click event handler.   Here is the event handler:

theMainCollection[theMainCollection.Count - 1].SomeValue = 25;

Why did SomeValue change while SomeName remained the same?  Take a look at the TestClass and you will notice that it implements the INotifyPropertyChanged interface and in the SomeValue setter we call NotifyPropertyChanged which raises an event.  Silverlight has a Pub/Sub eventing system that the UI uses to determine when to update databound objects.  With just a few lines of code we were able to wire TestClass into this eventing system and force the UI to update anytime our SomeValue property changes.

This is all fine and good, call that event for SomeName and we are good to go, well?almops.  We still have a problem with the UI!  The last item in cell 1 column 1 is displaying an incorrect SomeName value, and since we changed SomeValue to 25 it should no longer be visible in this cell.  What is happening is that the Filter on the collection view source is applied when an item is added to or removed from the collection but NOT when an item changes.  To manually reapply the filter call CollectionViewSource.View.Refresh (for each CollectionViewSource) and you will see the grid display correctly.  Click the Refresh CVS button to see this functionality in action:

image 

Below is all of the source code.

Click here to see how to sort the grid cells.

Here is the XAML:



Code Snippet



  1. <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="SilverlightApplication1.MainPage"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  6.     <UserControl.Resources>
  7.     </UserControl.Resources>
  8.   <Grid x:Name="LayoutRoot">
  9.         <StackPanel Orientation="Vertical">
  10.             <data:DataGrid x:Name="TheMainDataGrid"  AutoGenerateColumns="False" ItemsSource="{Binding}" HeadersVisibility="Column">
  11.                 <data:DataGrid.Columns>
  12.                     <data:DataGridTemplateColumn Header="Column 1">
  13.                         <data:DataGridTemplateColumn.CellTemplate>
  14.                             <DataTemplate>
  15.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column1.View}" >
  16.                                     <ListBox.ItemTemplate>
  17.                                         <DataTemplate>
  18.                                             <StackPanel Orientation="Horizontal">
  19.                                                 <TextBlock Margin="10" Text="{Binding Path=SomeName}" />
  20.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  21.                                             </StackPanel>
  22.                                         </DataTemplate>
  23.                                     </ListBox.ItemTemplate>
  24.                                 </ListBox>
  25.                             </DataTemplate>
  26.                         </data:DataGridTemplateColumn.CellTemplate>
  27.                     </data:DataGridTemplateColumn>
  28.                     <data:DataGridTemplateColumn Header="Column 2">
  29.                         <data:DataGridTemplateColumn.CellTemplate>
  30.                             <DataTemplate>
  31.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column2.View}" >
  32.                                     <ListBox.ItemTemplate>
  33.                                         <DataTemplate>
  34.                                             <StackPanel Orientation="Horizontal">
  35.                                                 <TextBlock Margin="10" Text="{Binding SomeName}" />
  36.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  37.                                             </StackPanel>
  38.                                         </DataTemplate>
  39.                                     </ListBox.ItemTemplate>
  40.                                 </ListBox>
  41.                             </DataTemplate>
  42.                         </data:DataGridTemplateColumn.CellTemplate>
  43.                     </data:DataGridTemplateColumn>
  44.                 </data:DataGrid.Columns>
  45.             </data:DataGrid>
  46.             <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
  47.                 <TextBlock Margin="5" Text="Name:"/>
  48.                 <TextBox x:Name="tbNAme" Margin="5" Width="100"></TextBox>
  49.                 <TextBlock Margin="5" Width="100" Text="Value:"/>
  50.                 <TextBox x:Name="tbValue" Margin="5" Width="100"></TextBox>
  51.                 <Button x:Name="AddButton" Margin="5" Content="Add" Click="AddButton_Click"/>
  52.             </StackPanel>
  53.             <StackPanel HorizontalAlignment="Left">
  54.                 <Button x:Name="ChangeNameButton" Width="100" Content="Change Name" Click="ChangeNameButton_Click"/>
  55.                 <Button x:Name="ChangeValueButton" Width="100" Content="Change Value" Click="ChangeValueButton_Click"/>
  56.                 <Button x:Name="refresh" Width="100" Content="Refresh CVS" Click="refresh_Click" />
  57.             </StackPanel>
  58.         </StackPanel>
  59.   </Grid>
  60. </ UserControl>




Here is the code behind:



Code Snippet



  1. using System.Collections.ObjectModel;
  2. using System.Windows.Controls;
  3. using System.Windows.Data;
  4. namespace SilverlightApplication1
  5. {
  6.     public partial class MainPage : UserControl
  7.     {
  8.         ObservableCollection<TestClass> theMainCollection ;
  9.         ObservableCollection<TheRowClass> theDataContext;
  10.         public MainPage()
  11.         {
  12.             InitializeComponent();
  13.             theMainCollection = new ObservableCollection<TestClass>()
  14.                 { new TestClass() {SomeName="A",SomeValue=11},
  15.                     new TestClass() {SomeName="A",SomeValue=12},
  16.                     new TestClass() {SomeName="B",SomeValue=13},
  17.                     new TestClass() {SomeName="B",SomeValue=14},
  18.                     new TestClass() {SomeName="AB",SomeValue=21},
  19.                     new TestClass() {SomeName="BC",SomeValue=22},
  20.                     new TestClass() {SomeName="AB",SomeValue=23},
  21.                     new TestClass() {SomeName="BC",SomeValue=24}
  22.                 };
  23.           
  24.             theDataContext = new ObservableCollection<TheRowClass>();
  25.             theDataContext.Add(new TheRowClass(theMainCollection));
  26.             theDataContext.Add(new TheRowClass(theMainCollection));
  27.             TheMainDataGrid.DataContext = theDataContext;
  28.             AddFilter(theDataContext[0].Column1, 1, 1);
  29.             AddFilter(theDataContext[0].Column2, 1, 2);
  30.             AddFilter(theDataContext[1].Column1, 2, 1);
  31.             AddFilter(theDataContext[1].Column2, 2, 2);
  32.            
  33.         }
  34.         private void AddFilter(CollectionViewSource cvs, int row, int column)
  35.         {
  36.             cvs.Filter += delegate(object o, FilterEventArgs eArgs)
  37.             {
  38.                 //Simple filter saying if column = 1 look for an A in the SomeName property
  39.                 //Else look for a B.  If we are in Row 1 look for a 1 in the SomeValue property
  40.                 //Else look for a 2
  41.                 string columnFilter;
  42.                 if (column == 1)
  43.                     columnFilter = "A";
  44.                 else
  45.                     columnFilter = "B";
  46.                 if ((eArgs.Item as TestClass).SomeValue.ToString().Contains(column.ToString()) && (eArgs.Item as TestClass).SomeName.Contains(columnFilter))
  47.                     eArgs.Accepted = true;
  48.                 else
  49.                     eArgs.Accepted = false;
  50.             };
  51.         }
  52.         private void AddButton_Click(object sender, System.Windows.RoutedEventArgs e)
  53.         {
  54.             TestClass tc = new TestClass();
  55.             tc.SomeName = tbNAme.Text;
  56.             tc.SomeValue = int.Parse(tbValue.Text);
  57.             theMainCollection.Add(tc);
  58.         }
  59.         private void ChangeNameButton_Click(object sender, System.Windows.RoutedEventArgs e)
  60.         {
  61.             theMainCollection[theMainCollection.Count-1].SomeName = "B";
  62.         }
  63.         private void ChangeValueButton_Click(object sender, System.Windows.RoutedEventArgs e)
  64.         {
  65.             theMainCollection[theMainCollection.Count - 1].SomeValue = 25;
  66.         }
  67.         private void refresh_Click(object sender, System.Windows.RoutedEventArgs e)
  68.         {
  69.             theDataContext[0].Column1.View.Refresh();
  70.             theDataContext[1].Column1.View.Refresh();
  71.             theDataContext[0].Column2.View.Refresh();
  72.             theDataContext[1].Column2.View.Refresh();
  73.         }
  74.     }
  75. }




Here is the TestClass Code:



Code Snippet



  1. using System;
  2. using System.ComponentModel;
  3. namespace SilverlightApplication1
  4. {
  5.     public class TestClass:INotifyPropertyChanged
  6.     {
  7.         public String SomeName { get; set; }
  8.         private int _someValue;
  9.         public int SomeValue
  10.         {
  11.             get
  12.             {
  13.                 return _someValue;
  14.             }
  15.             set
  16.             {
  17.                 if (value != this._someValue)
  18.                 {
  19.                     this._someValue = value;
  20.                     NotifyPropertyChanged("SomeValue");
  21.                 }
  22.             }
  23.         }
  24.         #region INotifyPropertyChanged Members
  25.         public event PropertyChangedEventHandler PropertyChanged;
  26.         #endregion
  27.         private void NotifyPropertyChanged(String info)
  28.         {
  29.             if (PropertyChanged != null)
  30.             {
  31.                 PropertyChanged(this, new PropertyChangedEventArgs(info));
  32.             }
  33.         }
  34.     }
  35. }




Here is TheRowClass:



Code Snippet



  1. using System.Windows.Data;
  2. using System.Collections;
  3. namespace SilverlightApplication1
  4. {
  5.     public class TheRowClass
  6.     {
  7.         public TheRowClass(IEnumerable source)
  8.         {
  9.             Column1 = new CollectionViewSource();
  10.             Column1.Source = source;
  11.             Column2 = new CollectionViewSource();
  12.             Column2.Source = source;
  13.         }
  14.         public CollectionViewSource Column1 { get; set; }
  15.         public CollectionViewSource Column2 { get; set; }
  16.     }
  17. }





Posted by: Tim Star
Posted on: 7/5/2010 at 8:51 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Ignoring Users and Roles On Deployment using VS2010 DB Project

I have run into this problem a couple times now:

The problem: Using a Visual Studio database project, I want to ignore users and roles on deployment AND keep the "Generate Drop statements for objects that are in the target database  but that are not in the database project" option checked.

There is no configurable way of doing this during the deploy with VS2010 (and earlier).  Please note that that is my post above and I am note shamelessly plagiarizing someone else's work!

A little background:  I am a consultant and have introduced this tool (visual studio database projects) to several customers.  Developers tend to like it and DBAs are on the wall (at best).  The DBAs don't care how developers create sprocs, tables, indexes and such but they want absolute control over users and roles and I frankly don?t blame them.  While I would certainly like to see this capability added, here is what I have done to deal with users and roles (bare in mind for this scenario I need to use a deploy, schema compare does offer some nice alternatives if that is not the case):

The environment: We have a development, test, and production environment.  We (QA and developers) prove things out in the dev environment, we promote the code and DB changes to Test for more QA and BA scrutiny, and when all tests have passed we promote to production.  This is a very typical process I see from customer to customer.

Developers have full access to the dev environment, limited (read only) access to test, and no access to production.  In other words, each environment has a different set of users and roles but we, of course, want to use the same database project for each environment. Oh and we do want to use the "generate drop statements" option to help manage our changes (dropping a sproc, index, whatever...).

The solution: Because we want to make the production script as clean as possible we synch the database project with a copy of the production database minus any confidential information (which is not trivial to do).  The project, therefore, contains all of the production users and roles (but none of the development and test users or roles).  If the production users tend to be static, deploying changes to production will create a script without create user and modify role statements which is something the DBAs demand of the production update scripts. If the users and roles in production are more dynamic we have to either work with the DBA to manage user/role scripts in the project at the same time they are changed in production (assuming they prefer to add users without using Visual Studio) or we have to add a step to our process to synch the users and roles with production before we create the final deploy to production script.  Using this process means our deploy to production scripts are nice and clean and void of any user and role changes - our primary goal is satisfied.

The down side of synching to production is that the development or test deploy script will generate "create user" and add rolemember statements for production users that we don?t want in the database and it will generate drop user statements for the users we do want in the dev and test environment .  Remember the users we have in the project only map to the users in production.  Well this is not the end of the world; it just means that we have some post deployment work to do.  We add a post deploy script to the project that is responsible for dropping all (production) users that the script created for us and adding the dev or test users and role membership.
The script we deploy to say dev might look something like this:

USE [$(DatabaseName)]

GO

 Pre-Deployment Script Template  
...

The VS generated script

CREATE USER [domain\ProdUser1] FOR LOGIN [domain\ProdUser1];
GO
PRINT N'Creating [domain\ProdUser1]...';
GO
CREATE USER [domain\ProdUser2] FOR LOGIN [domain\ProdUser2];
GO
EXECUTE sp_addrolemember @rolename = N'DataReader', @membername = N'domain\ProdUser1';
GO
EXECUTE sp_addrolemember @rolename = N'DataReader', @membername = N'domain\ProdUser2';
GO
DROP USER [domain\Dev1];
GO
DROP USER [domain\Dev2] ; 
GO 
Alter table, drop sproc...
GO

Post-Deployment Script 

IF (@@servername = 'DevDBServerName' )
begin

CREATE USER [domain\Dev1] FOR LOGIN [domain\Dev1];
CREATE USER [domain\Dev1] FOR LOGIN [domain\Dev2];
EXECUTE sp_addrolemember @rolename = N'DataReader', @membername = N'domain\Dev1';
EXECUTE sp_addrolemember @rolename = N'DataReader', @membername = N'domain\Dev2';';
EXECUTE sp_addrolemember @rolename = N'DataWriter', @membername = N'domain\Dev1';
EXECUTE sp_addrolemember @rolename = N'DataWriter', @membername = N'domain\Dev2';';
...
end

IF (@@servername = 'TestDBServerName' )
begin
CREATE USER [domain\Dev1] FOR LOGIN [domain\Dev1];
CREATE USER [domain\Dev1] FOR LOGIN [domain\Dev2];
EXECUTE sp_addrolemember @rolename = N'DataReader', @membername = N'domain\Dev1';
EXECUTE sp_addrolemember @rolename = N'DataReader', @membername = N'domain\Dev2';';
...

end

I would love to hear how others manage this problem, drop me a comment if you are willing to share.


Posted by: Tim Star
Posted on: 6/24/2010 at 5:27 PM
Tags: , ,
Categories: .NET | Visual Studio
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Windows Azure Storage Options

In my last post I described how to configure your machine to get started developing Windows Azure applications.  We built a simple web role (web site host) and took a look at what role the development fabric played in developing a Windows Azure application.  As soon as we try to build more than the most basic web application we are going to have to start considering our storage options.  Following is a high level overview of the four main storage options.  More details for each option will follow in separate posts.

Table Services : The table service is designed to store non-relational entity type data, serialized DTOs for example.  Redundancy is built in. The size of each row of data is limited to 1 MB and there are some fairly restrictive rules around what types of data may be stored in the table.  We should consider using the Table Service rather than a relational database to persist our data.  Not everything has to be stored in a relational database.  If you can keep your entity relationships simple, live without the joins and manage the relationships yourself, Table Storage looks to be a more simple, cost effective, and scalable approach.  Consider using table storage instead of a traditional database and there is even an ASP.Net session provider available in the SDK that uses table storage.

Blob Storage : There are two kinds of blob storage.  Block Blobs are designed to store up to 200GB of binary data and are optimized for streaming.  A Page Blob can store up to 1 TB of binary data and is designed for random access.  Like table storage, redundancy is built in.  This storage mechanism is ideal for Streaming videos, waves, images, or working with random access files such as fixed length field flat file

Queue Service : The queue service supports 8 KB XML messages and also has redundancy built in.  We are not going to let the size limitation scare us, the intention is not to allow us to put large binary objects on the queue for processing rather the intent is that the message would contain some sort of instruction and if need be the message can reference some object already in storage via a key (the message just contains a pointer to the object we want the worker role to act upon).  The key with using a Queue is this is not a chatty interface this should be used more as a  fire and forget.  Queue messages should be communicating a task the sender wants performed, not performing some type of query that requires a response.

SQL Azure : For the sake of this post we can say SQL Azure is similar to the Sql Server we know and love and that it is designed to store and mange relational data.


Posted by: Tim Star
Posted on: 4/20/2010 at 5:50 AM
Tags: , , , ,
Categories: .NET | Cloud Computing
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Getting Started with Windows Azure

Before you we can play around with Azure we need to install a few things.  Note: These instructions work for Visual Studio 2010 RC on Windows 7.

Download and install Windows Azure platform AppFabric SDK V1.0

http://www.microsoft.com/downloads/details.aspx?FamilyID=39856a03-1490-4283-908f-c8bf0bfad8a5&displaylang=en

Setup IIS for ASP.Net

To enable IIS 7.0 with ASP.NET on Windows 7

1.     Click the Start button, click Settings, click Control Panel, click Programs, and then click Programs and Features.

2.     Click Turn Windows Features On or Off.

3.     Under Internet Information Services, expand World Wide Web Services.

4.     Under Application Development Features, click ASP.NET.

5.     Under Common HTTP Features, click Static Content.

6.     Install the selected features.

http://msdn.microsoft.com/en-us/library/dd179419.aspx

If you don't have SQL Express installed:

open the Windows Azure SDK Command Prompt:

And enter the following text:

dsinit /sqlinstance:.

The ?dot? represents the default SQL Server instance and may optionally be replaced with any valid instance name.

Next lets open visual studio as administrator.  Select File -  New ? Project.  Select the cloud template and give the project a meaningful name like?MyFirstCloudService?.

Select Ok.

Next we need to select a role:

image

Let?s keep it simple and select an ASP.Net Web Role (we are indicating that we will be hosting a web site in the cloud).

image

Click the ?(pencil) icon to rename the Role to something meaningful ? ?MyFirstWebSiteRole? will do.

Select OK

Notice we have 2 projects

image

The MyFirstCloudService project contains all of our configuration information such as the size of our VM, the endpoint configurations, configuration settings and local storage.  The second project, MyFirstWebRole, contains the asp.Net web site with a couple additions, 1) We have a file named WebRole.cs which is used to recycle the role instance if a configuration change is made and 2) notice we have several references to Microsoft.WindowsAzure.* which gives us access to the Azure APIs.

Before we run let?s add a multi-line textbox to the aspx page name Textbox1 and add the following code to the corresponding Page_Load event

 

Code Snippet
  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3. ????TextBox1.Text += "Running In Azure: " + RoleEnvironment.IsAvailable.ToString() + "\n";
  4. ????TextBox1.Text += "Deployment Id: " + RoleEnvironment.DeploymentId + "\n";
  5. ????TextBox1.Text += "Role Instance Id: " + RoleEnvironment.CurrentRoleInstance.Id + "\n";
  6. }

 

Press F5 and we will see our page with an output similar to this:

image

Notice when you started the app a little blue icon showed up in your systray.

 

image

This is the dev app fabric ? we are not actually running in the cloud yet!

 

Right click on the icon and select Show Development Fabric UI.

image

If we highlight some the various nodes on the left, we see how things in the Development Fabric line up with things on our ASPX page.

So what is a role?

A role is just another name for your app.  It specifically refers to the base virtual machine image that hosts your app.  In our case, a web role, we are talking about a VM that hosts your application within IIS.

What is the development fabric?

The development fabric simulates the Windows Azure fabric on your local computer so that you can run and test your service locally before deploying it.  That is a lot of information for one post, we will go over the fabric, configuration, and actually deploying the service in another post (that will require an azure account and a credit card.


Posted by: Tim Star
Posted on: 3/22/2010 at 9:09 PM
Tags: ,
Categories: Cloud Computing | .NET
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Software as a Service (SaaS)

I have had a very busy couple months ramping up on the new Visual Studio 2010 testing tools and starting a new project.  Now that things have settled down a little bit, cloud computing has hit my radar again.  Several years ago I wrote a white paper on what it would take to deploy my former employers COTS product in a Software as a Service (SaaS) model.  While it wouldn’t be appropriate to share the entire white paper, I reworked the introduction to share my thoughts on SaaS with the community.  What is interesting is that this was written in late 2007 or 2008 and while technology has changed considerably, and the maturity levels may have been renumbered and grown,  I believe all of the concepts are still relevant today. After I spend a little time learning about the new .Net cloud technologies at our disposal, I’ll revisit this post and start discussing how to pull all of this off.

Definition

Software as a Service is a deployment model where an application is hosted as a service over the internet.

Business benefits to the customer

· Reduced cost of maintenance and support. The hardware cost and associated support staff is the vendor’s or a 3rd party’s responsibility.

· Reduced cost of entry by reducing the initial cost of purchasing licenses a pay per use model is generally employed.

Business benefits to the application vender

· Better protection of intellectual property. The customer has much less access to the system. For example there are little or no on-site IT responsibilities so the customer would be much less knowledgeable about things like the data store or application architecture.

· Establishes an ongoing revenue stream. The pay as you go model establishes a more predictable revenue stream.

· Reduced cost of maintenance by controlling the platform that the application is deployed on. For example an application could be deployed and certified only on Windows 2008 and Sql Server 2008. Certifying on one very predictable environment simplifies the QA and support burden.

Risks and Challenges

· Trust. What is the long term viability of the application service provider? How secure is the data? What is the disaster recovery plan? What level of service can I expect?

· Integration. Integrating with on-premise applications is difficult enough, how do we integrate with hosted applications?

· Regulations. Are there regulations around data access and storage that would increase the difficulty of deploying an application in the Software as a Service model?

· Portability. If I want to move the application and data to a new service provider, will my application and data port?

· Support. Who fields help desk type calls

· Management. Maintenance and upgrades are controlled by the service provider.

· Identity Management. Customer will have less control over how users and Identity are managed.

Software as a Service maturity levels

Maturity Level 1 – Each customer has its own customized instance of the application running on the host servers. This is essentially the same as the Application Server Provider (ASP) model of the late 90’s that never really took off. Each customer is executing different code on different machines.

 

 

 

Maturity Level 2 - Each customer has its own configurable instance of the application running on the host servers. Each customer is executing the same code on a different machine.

 

Maturity Level 3 – The vendor hosts all of the customers in a single configurable instance of the application and uses authorization and security policies to ensure that each customer’s data is kept separate from the other customers. This is called multi-tenant; each customer is executing the same code on the same machine.

 

Maturity Level 4 - – The vendor hosts all of the customers in a load balanced farm of identical instances of the application and uses authorization and security policies to ensure that each customer’s data is kept separate from the other customers.

 

A Note on Application Security

When architecting an application to be deployed in an SaaS model it is important to architect the application with security in mind. CIAA is a security acronym standing for Confidentiality, Integrity, Authenticate, and Authorize and as architects we need to consider these things.

Confidentiality - Make sure nobody else can read the messages by encrypting confidential communications.

Integrity - Validate that the message has not been tampered with by using digital signatures.

Authenticate - Make sure the user is who they say they are.

Authorize - Make sure the user is allowed to do what they are trying to do.

A Note on Delivering Large Resources

Distributing large resources in the SaaS deployment model presents a particularly difficult performance challenge when you are trying to deliver large resources. Assume you have to deliver a resource that is 10’s of megabytes in size. The performance a customer can expect is equal to what a customer gets when downloading a similar size file from the internet. Software designers should consider an architecture which supports distributing these large resources to local stores. These local stores would host the large documents at various locations around the world, within an intranet, or ideally on a local network. The hosted service would still own and manage the documents but the local stores would have to communicate with the master store to ensure that the large resources are up to date.

 

 

 


Posted by: Tim Star
Posted on: 3/5/2010 at 4:07 PM
Tags: , ,
Categories: .NET
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed