Using WPF and C#
-
Recent Posts
Archives
Categories
Meta
Using WPF and C#
Nowadays, ETL technologies are widely used in the software industries.
SQL Server Integration Services
Integrating data into applications or reports is one of the most important, expensive, and exacting activities in building enterprise data warehousing applications. SQL Server Integration Services with the acronym SSIS, is a high performance solution consisting of multiple tools dedicated to this task in MS SQL Server 2005 and is tightly integrated with the .NET Framework tools. SSIS is a product which has evolved over time from MS SQL Server 7.0, where it was called Data Transformation Service (DTS). Although DTS was the forerunner of SSIS, it is not just an improvement, but an entirely new product, which has all the features of the earlier tool, but with a vastly improved development interface, an extensible architecture, enlarged set of tools to address the varied and changing pattern of data in line with the latest developments in data related technology, looping structures, ease of integrating with Analysis Services, better management and performance.
ETL that is, extracting, transforming, and loading takes care of all the activities needed to accomplish data integration. This process consists of extracting data from a source, transforming the extracted data and then loading the modified data to a target server to be used by the application. There are a large number of independent vendors such as Ascential, Informatica, and so on, as well as several database vendors such as Oracle, IBM, and Microsoft in this market.
Enterprise data can be of very different kinds ranging from flat files to data stored in relational databases, with the more recent trend of data being stored in XML data stores. The extraordinary number of database related products, and their historic evolution, makes this task exacting.
SSIS Basics
[ ]
It is not very frequent that extracted data is usable as it is, and may need some kind of transformation. Often, the extracted data has inconsistencies. There are many reasons for inconsistencies, as computer applications may be affected by changes in technology (version changes, new methodologies, etc.), poor or no validation, which are the norms in legacy data software changes (for example, the date and time will be new data types in SQL 2008 to address several issues of awareness of time zones, higher precision needed in financial applications, address compatibility with third party vendors, etc.), changes in the way applications interact with backend data, localization, etc.
Some inconsistencies can be understood quite easily, such as the same information stored in different data formats, while others could be more difficult.
The efficiency and agility of an enterprise would depend on the quality of data in its databases. Hence it is not only necessary to cleanse or scrub data, but also modify it to be internally consistent, and have the correct data quality. Low quality data has been one of the main causes of accidents and frauds in recent times. SSIS has built-in transformations such as fuzzy lookup and fuzzy grouping that can be used to clean, and standardize the data required by the target database. It is very important to standardize data across an enterprise, especially when data is originating from several subdivisions of the enterprise, where different sets of standards may be operative, or where data is consolidated from distributed systems.
Further, the target database may have a different data type from the one that is extracted, and in which case the data has to be modified. Data transformation or modification can range from very simple to very complex. While changing the data type of an extracted data to match the data type of the target database, or changing the case of the extracted data (string) can be simple, combing several columns based on some logic can be complex. What is nice about SSIS is that you can concatenate several simple tasks to arrive at a complex transformation with each link providing a specific change. As an alternative to concatenating transformations or tasks, using the script task with the full force of the .NET framework behind it, it can produce an optimized solution. This is indeed an awesome tool that should be put to good use.
When it comes to loading data to the target database, there is the challenging aspect of loading the data to match the metadata of the target. Many middleware programs, such as Siebel, have their own proprietary database on which they have built their application. In such cases, the incoming data has to be properly matched to the target database schema. Since the middleware uses data from different database products (MS SQL Server, Oracle, etc.), the ETL process has to step up to this task. In fact SSIS is best suited for this task, as a plethora of data access methods are provided. You will see an example of data going between SQL Server and an Oracle database in Chapter 13.
Chapter 1
[ ]
Objects Used in SSIS
SSIS is governed by an object model. Working with SSIS requires a clear understanding of this model. This section describes some important and salient features of the object model. The objects used in building an Integrated Services Package, and the manner in which they work together in the package workflow, are as follows:
Package: This contains all objects in a single unit of work that can be retrieved, executed, or saved at one of several locations.
Control Flow: These elements consist of tasks and containers, and take care of the work flow by preparing data, and arranging for interaction with other processes, but the ordering of tasks is controlled by precedence constraints, which is another important constituent of flow control.
Data Flow: These components consist of a source (adapter) and destination (adapter) for the data, as well as any data transformation that may be needed. The source, destination, and the transformation stages are connected by a sequencing path to maintain the order of flow.
Connection Managers: They stand apart, and facilitate connection to various sources for both extracting (sources) and loading (destination).
Variables: They are used to communicate between various objects. The objects are isolated from one another by design to bestow security, manageability, and maintainability. The variables that support dynamic updating of column values, conditions used in establishing precedence of flow, and reference to record-set destination are some of the examples.
Event Handlers: They run in response to events raised by other objects at run time. Every container has an event handler associated with it that executes at run time.
Log Providers: These are objects, used for logging package run time information.
In addition to this, there are features that help in debugging and diagnosing problems in design. In the following sections, you will learn some of the basic details about each of the above topics. You will also be using them in the various exercises that you will be doing.
• • • • • • • SSIS Basics [ 10 ]
The SSIS Package
The package is an assembly consisting of the several objects described briefly in the previous section. It may also include other packages (nested package). Specifically it can consist of:
Connections
Control Flow Elements
Data Flow Elements
Event Handlers
Variables
Configurations
All elements are not necessary to build a package. In fact, a package can be empty; however, being empty, it will achieve nothing. In order to have functionality, it should have a control flow element, and one or more data flow elements.
A package can be either assembled using the graphical design tools that SSIS provides, or can be built by writing a program. The package built using these methods can be saved to Sql server, an SSIS Package store, or to a file system. The saved package can be retrieved, executed, modified, and saved.
The Control Flow Elements
As mentioned previously, the control flow elements consist of containers and tasks. The following figure shows the various items that you can find in the Toolbox that belongs to Control Flow Items. Tasks do all the work, and the containers provide the tasks with a medium to orchestrate them. Containers may contain other containers as well. The precedence constraints further orders the flow.
The list of tasks in SSIS includes most of the tasks that one would find in its earlier embodiment, the DTS designer. The various tasks can be more generically classified under—data preparation tasks, analytical services related tasks, scripting tasks, SQL Server tasks, maintenance tasks, etc. You will have the opportunity to work with some of these tasks in the various chapters of this book.
• • • • • • Chapter 1 [ 11 ]
Of the available control flow tasks, the following are used in this book :
Send Mail Task
Data Flow Task
Bulk Insert Task
XML Task
Web Service Task
Transfer Database Task
Execute Process Task
File System Task
Execute Package Task
• • • • • • • • • SSIS Basics [ 12 ]
Execute SQL Task
FTP Task
ActiveX Script Task
Script Task
Maintenance Plan Task
Data Flow Components
SSIS provides three types of data flow components— sources, destinations and transformations. Basically, data coming from the source is transformed before being loaded to the destination.
Data Source Components
Since extraction of data is one of the operations needed for ETL, SSIS is equipped with a rich interface for retrieving or merging data from heterogeneous data stores.
The variety of data sources that SSIS can connect to is rather large, using .NET, OLEDB, and ODBC data access methods. While .NET and OLEDB access methods are used for connecting to relational data, ODBC data access is used for legacy data. The ODBC data access method is especially useful, because there are ODBC drivers for practically every kind of database product. In addition to this, SSIS can also leverage data from flat files, XML files, MS Excel spread sheet files, and use MSOLAP provider, to access Analysis Services. The following figure shows various options for data source components available in the Visual Studio 2005 IDE (Standard Edition). It should be mentioned that the DataReader Source, a component of the .NET Data Access Strategy, connects to many of the vendors thorough the optimized .NET provider.
• • • • • Chapter 1 [ 13 ]
Of the available Data Flow Sources, the following are used in this book:
DataReader Source
Excel Source
OLE DB Source
While .NET data providers were developed recently, OLEDB and ODBC have existed for a considerable amount of time and there exists a large number of both Microsoft and non-Microsoft products for establishing connectivity. The following figure shows a list, DataLink.udl Properties, which depends on OLEDB data access providers. It can be seen that there are a large number of providers, all of which can be used for connecting to data sources.
• • • SSIS Basics [ 14 ]
The following data access providers have been used in this book:
.NET Provider\SqlClient Data Provider
OLE DB Provider
Microsoft Jet 4.0 OLE DB Provider
Native OLE DB\Sql Native Client
Native OLEDB\Microsoft OLE DB Provider for SQL Server
Native OLEDB\Microsoft OLE DB Provider for Oracle
In SSIS, a source is typically a data flow component that conduits data from external data sources to other data flow components in a package. The following figure is a schematic representation of a Source obtaining its input from an external source and delivering it to the transformation stage. The error output has two columns describing errors in addition to the columns originating from the source.
Data Transformation
SSIS has built-in transformation that can be used to clean or scrub and standardize the data required by the target database. Standardizing data in an enterprise, especially when data is originating from multiple subdivisions having different standards, is important. Also, with globalization of enterprises, the importance of transformation has risen. The following figure shows the built-in data transformations available in Visual Studio 2005 (Standard Edition).
WINDOWS PRESENTATION FOUNDATION (WPF)
2. Windows Presentation Foundation Services
3. Data Binding
4. Apply Property Triggers on a Control
5. Usage of Document Viewer Control
6. Usage of Menu
7. Usage of ListBox
8. Expander Control
9. Writing Your Own Controls
10. Properties
• CLR Properties limited in functionality
11. Custom Controls
The purpose of this paper is to introduce you to a number of the concepts and features of the Windows Presentation Foundation (WPF, formerly code-named “Avalon”), part of the Windows Vista developer platform, and the foundation for the next generation of Windows applications and content.
WPF enables software developers to achieve a new level of quality in the “User Experience” (UX) aspect of applications. Developers will be able to use WPF to build applications similar to those they construct atop Win32 today… but they will also use WPF to create and display media-rich interactive content, animations, and traditional documents. With the initial release of WPF, developers will be able to write applications that match the functionality and experience that is prevalent for Win32 or DHTML applications and content. Twitch games and high-end scientific visualization/CAD applications, typically the focus of Direct3D developers, are not within the scope for WPF in its first release; however, even in those application domains, there are ways that WPF (particularly its support for documents) can be used to add UI value.
The WPF is not just about graphics. Its coverage of all common forms of presentation—UI, media, vector graphics, and documents—represents a degree of unification that is new to the Windows platform. The “silo” effect found in today’s current disjoint set of client-side technologies has been replaced by a single managed set of classes providing a consistent set of base properties and methods. The result is an unprecedented ability to “mix and match” UI controls, content (document and media), and vector graphics in a seamless manner.
WPF also has features intended to streamline the development process, by reducing the amount of procedural code in application specification, and by enabling a greater and more direct collaboration by members of the development team—UI designers and graphic artists, as well as software engineers. The fundamental technology behind this marriage of design and implementation is a declarative markup language called XAML.By building the WPF atop the cutting-edge Direct3D graphics engine, Microsoft makes it possible for everyday applications to take advantage of the rapidly evolving power of graphics hardware, without the need for direct access to the Direct3D layer. WPF applications will automatically benefit from the optimization capabilities found in Direct3D now and in the future.
| Base Services | XAML, Property System, Input and Eventing, Accessibility |
| Media Services | 2D, 3D, Audio, Video, Text, Imaging, Animation, Effects, Composition Engine |
| Document Services | XPS Documents, Open Packaging Conventions |
| User Interface Services | Application Services, Deployment, Controls, Layout, Data Binding |
The design principles behind Windows Presentation Foundation can be categorized as follows:
The document specifies how the data binding and event handling can be done with WPF controls.
Namespace binding
{
Class Person
{
String Name;
String name
{
get
{
return Name;
}
set
{
Name=value;
}
}
Public void Person ()
{}
Public void Person (string name)
{
Name=name;
}
}
}
Xmlns: local=”clr-namespace: binding”
<Window. Resources>
<local: Person x: Key=”xyz” Name=”Gill Cleeren” Age=”27″/>
</Window. Resources>
{Binding Path=Name} etc…..
You are done with binding it to a class!!!
I.e. Data Context=Patient List.
<Window.Resources>
<DataTemplate x:Key=”DataKey”>
<TextBlock Text=”{Binding Path=Column_Name}” FontWeight=”Bold” />
</DataTemplate>
</Window.Resources>
Column_Name === Column to be bound to the Combobox
x:Key === Unique Key name for DataTemplate(Any name U can give)
<ComboBox ItemTemplate =”{StaticResource ListTemplate}” Name=”comboBox1″ ItemsSource=”{Binding}”></ComboBox>
i.e. ListView1. Items Source=Patient List;
We can’t set Data Set as the ItemsSource for List View.
<ListView Name=”lsvPatient”>
<ListView.View>
<GridView AllowsColumnReorder=”True”>
<GridViewColumn DisplayMemberBinding=”{Binding Path= Column_Name}” Header=”Patient Name” Width=”100″/>
</GridView>
</ListView.View>
</ListView>
| Binding Mode | Description |
| Two-Way | Moves changes, from either the bound control or the source of the binding, to one other in a bi-directional way. (This is the default mode.) |
| OneWay | Moves changes only from the source to the control. As changes occur in the source, the bound control’s data is changed. |
| OneTime | Data is bound only at startup, and changes to the source are ignored once the control is filled with data the first time. |
You can specify the mode by simply including the mode in the markup, as follows.
i.e. {Binding Element Name, Path=Text, Mode=OneWay}
In addition to the Mode, you can also specify when the binding pushes the changes, by using the UpdateSourceTrigger. You can specify that the binding make the change only at specified times, by specifying the UpdateSourceTrigger type.
The UpdateSourceTrigger property specifies when to update the source with changes. This is valid only with Mode=TwoWay bindings (which is the default). The valid values for the UpdateSourceTrigger are shown in Table 2.
| UpdateSourceTrigger | Description |
| Explicit | The source is updated only by explicitly calling the BindingExpression.UpdateSource method. |
| LostFocus | The source is updated as the bound control loses focus. |
| PropertyChanged | Changes are updated to the source every time the property changes. This is the default behavior. |
Suppose you wish to bind a label to a text box, meaning whatever changes happens in the textboxes should be reflected real time in the label, then follow the steps
4. Apply Property Triggers on a Control
Property triggers are triggers that come into picture when we are on watch for some property like IsMouseOver etc being true
<Style x: Key=”normal”>
<Setter Property=”Control.FontSize” Value=”12″ />
<Setter Property=”Control.HorizontalAlignment” Value=”Center” />
<Setter Property=”Control. Margin” Value=”2″ />
</Style>
<Button style= {Static Resource normal}/>
Now whatever is specified in the style will be applicable to that particular control that refers to it
<Style. Triggers>
<Trigger Property=”Control.IsMouseOver” Value=”true”>
<Setter Property=”Control.FontStyle” Value=”Italic” />
<Setter Property=”Control.Foreground” Value=”Green” />
</Trigger>
<Trigger Property=”Button.IsPressed” Value=”true”>
<Setter Property=”Control.Foreground” Value=”Red” />
</Trigger>
</Style.Triggers>
but this entire thing should be in the resources tag so the completed thing becomes
<Grid. Resources>
<Style x: Key=”normal”>
<Setter Property=”Control.FontSize” Value=”12″ />
<Setter Property=”Control.HorizontalAlignment” Value=”Center” />
<Setter Property=”Control. Margin” Value=”2″ />
<Style.Triggers>
<Trigger Property=”Control.IsMouseOver” Value=”true”>
<Setter Property=”Control.FontStyle” Value=”Italic” />
<Setter Property=”Control.Foreground” Value=”Green” />
</Trigger>
<Trigger Property=”Button.IsPressed” Value=”true”>
<Setter Property=”Control.Foreground” Value=”Red” />
</Trigger>
</Style.Triggers>
</Style>
</Grid. Resources>
5. Usage of Document Viewer Control
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”*” />
<RowDefinition Height=”*” />
</Grid.RowDefinitions>
<DocumentViewer Name=”dvZoomSource” Grid.Row=”0″ />
<TextBox Grid.Row=”1″
FontSize=”{Binding ElementName=dvZoomSource, Path=Zoom}” Text=”{Binding ElementName=dvZoomSource, Path=Zoom,Mode=OneWay}”/>
<Image Source=”C:\Program Files\Movie Maker\Shared\Sample1.jpg”
Height=”{Binding ElementName=dvZoomSource, Path=Zoom}” Width=”{Binding ElementName=dvZoomSource, Path=Zoom}”/>
</Grid>
6. Usage of Menu
This example shows how to create MENU controls. The example illustrates Menu controls with submenus and Menu Item elements with ToolTip controls. The example also shows how to use the Is Checkable property to make Menu Item controls that can be checked.
<Grid>
<Menu Width=”30″ Margin=”10, 10, 5, 5″ HorizontalAlignment=”Left” Background=”White”>
<MenuItem Header=”_File”>
<MenuItem Header=”_New” IsCheckable=”true”/>
<MenuItem Header=”_Open” IsCheckable=”true”/>
<MenuItem Header=”_Close” IsCheckable=”true”/>
<Separator/>
<MenuItem Header=”Menu with Submenu”>
<MenuItem Header=”_submenuitem1″ IsCheckable=”true”/>
<MenuItem Header=”_submenuitem2″ IsCheckable=”true”>
<MenuItem Header=”_submenuitem2.1″ IsCheckable=”true”/>
</MenuItem>
</MenuItem>
<Separator/>
<MenuItem Header=”_Menu item with ToolTip”>
<MenuItem.ToolTip>
<ToolTip>ToolTip Information.</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
</MenuItem>
</Menu>
</Grid>
7. Usage of ListBox
public class MyStrings : List<String>
{
public MyStrings()
{
this.Add(“Hello”);
this.Add(“Goodbye”);
this.Add(“Heya”);
this.Add(“Cya”);
}
}
This code should be included in the xaml page.
<Window.Resources>
<ObjectDataProvider x:Key=”MyStringData” ObjectType=”{x:Type local:MyStrings}” />
</Window.Resources>
<StackPanel>
<TextBlock HorizontalAlignment=”Center” FontWeight=”Bold”>
Simple Source Example </TextBlock>
<ListBox Name=”theListBox” Width=”200″ Height=”300″
ItemsSource=”{Binding Source={StaticResource MyStringData}}”/>
</StackPanel>
</Window>
8. Expander Control:
The following example shows how to create an expander control in Add Patient:
<Expander
HorizontalAlignment=”Left” Header=”C
ExpandDirection=”Down” ToolTip=”Patient Info “/>
Gets or sets the direction in which the Expander content window opens.
The following example shows how to set the Expand Direction property.
<Expander Name=”myExpander1” Expand Direction=”up” Header =” Patient Info” Content=”expanderContent”/>
Gets or sets whether the Expander content window is visible. The following example shows how to set Is Expand property.
<Expander Name=”myExpander1” Is Expanded=”True” Header =”Patient Info” Content=”expanderContent”/>
An Expander allows a user to view a header and expand that header to see further details, or to collapse a section up to a header.
The following example shows how to create an Expander. The example uses a Bullet Decorator control, which contains an image and text, in order to define the Header. A Scroll Viewer control provides a method for scrolling the expanded content.
Note that the example sets the Height property on the Scroll Viewer instead of on the content. If the Height is set on the content, the Scroll Viewer does not work correctly because it does not know about the size limitation. The Width property is set on the Expander control and this setting applies to the Header and the expanded content.
<Grid>
<Expander Width=”200″ HorizontalContentAlignment=”Stretch”>
<Expander.Header>
<BulletDecorator>
<BulletDecorator.Bullet>
<Image Width=”50″ Source=”C:\Program Files\Movie Maker\Shared\Sample1.jpg”/>
</BulletDecorator.Bullet>
<TextBlock Margin=”20,0,0,0″>My Expander</TextBlock>
</BulletDecorator>
</Expander.Header>
<Expander.Content >
<ScrollViewer Height=”50″>
<TextBlock TextWrapping=”Wrap”>
</TextBlock>
</ScrollViewer>
</Expander.Content>
</Expander>
</Grid>
9. Writing Your Own Controls
• Two ways
• Typical WPF-like Development Experience
<UserControl x: Class=”CustomWPF.MyUserControl”
xmlns=”…” xmlns:x=”…”>
<Grid>
<Ellipse Width=”50″ Height=”50″ />
<Path Fill=”Black” Data=”M18,12 18,38 35,25″/>
</Grid>
</UserControl>
Public partial class MyUserControl : UserControl
{
Public MyUserControl ()
{
PlayIcon.Opacity = .5;
}
}
• Use Controls by importing the CLR Namespace
<Window x:xmlns=”…” xmlns:x=”…”
xmlns:cust=”clr-namespace:CustomWPF”>
<cust:MyUserControl />
</Window>
<Window x:Class=”Tester.MainWindow” xmlns=”…” xmlns: x=”…”
xmlns:cust=”clr-namespace:CustomWPF, MyAssembly”>
<cust:MyUserControl />
</Window>
10. Properties
• Adding Properties is simple
Public partial class MyUserControl : UserControl
{
// …
Brush _iconColor = Brushes.Black;
Public Brush IconColor
{
get { return _iconColor;
}
set {
_iconColor = value;
PlayIcon.Fill = value;
PauseIcon.Fill = value;
} }
}
• CLR Properties limited in functionality
<StackPanel>
<Rectangle Fill=”Red” />
<TextBlock>User Control:</TextBlock>
<!– Simple Assignment Works –>
<cust:MyUserControl IconColor=”Blue” />
<!– Data Binding Does Not –>
<cust:MyUserControl IconColor=”{Binding ElementName=aRect, Path=Fill}” />
</StackPanel>
• DependencyProperties
• DependencyProperties
Public static readonly DependencyProperty IconColorProperty =
DependencyProperty.Register(“IconColor”, // Property Name
typeof (Brush),
// Type of the Property
Typeof (PlayButton));
// Type of the Owner
// of the Property
• DependencyProperties
Public Brush IconColor
{
get { return (Brush)GetValue(IconColorProperty);
}
set { SetValue(IconColorProperty, value);
}
}
11. Custom Controls
• New control can derive from any of the classes
public class MyNewControl : Control {
// …
}
public class MyNewControl : FrameworkElement {
// …
}
public class MyNewControl : Button {
// …
}
• Also creates (or adds to generic.xaml)
<!– Generic.xaml –>
<ResourceDictionary xmlns=”…” xmlns:x=”…” xmlns:local=”clr- namespace:CustomWPF”>
<Style TargetType=”{x:Type local:MyNewControl}”>
<Setter Property=”Template”>
<Setter.Value>
<ControlTemplate TargetType=”{x:Type local:MyNewControl}”>
<Border Background=”{TemplateBinding Background}”
BorderBrush=”{TemplateBinding BorderBrush}”
BorderThickness=”{TemplateBinding BorderThickness}”>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
• Themes allow you to create designs that fit the OS
• Theme files are resource dictionaries
For more details refer www.wildermuthconsulting.com,www.adoguy.com
12.Event Handlers in WPF
Whenever you click on a button or type some text into a form in your browser, you are using events. More than likely, you never think about this, because it just works. You don’t worry about how the mouse click is recognized, how your application knows which key was pressed, etc. The reason is that many of these lower level details are handled by your graphics framework itself. Even as a developer, your work with events is largely on the surface of what really goes on behind the scenes, but there is plenty of surface area to cover though! So, in this tutorial, I will explain how to use event handlers in the Windows Presentation Foundation (WPF).
An event handler is simply a method (function) that receives the input from a device such as a mouse or keyboard and does something with it. Like its name implies, it handles events – more specifically, input events. For example, the following code shows an event handler called ButtonOKClicked that is fired when a button is clicked:
Private Void ButtonOKClicked (object sender, RoutedEventArgs e)
{
This. Close ();
}
There are really two things that need to be done when using events:
In this article, I will go over event handlers and how to use them with events. I will provide code examples to reinforce what I will be explaining, but this is more of an article explaining event handlers as opposed to a tutorial where you create a small application using event handlers.
[ assigning a click event to an event handler called ButtonOkClicked ]
In the visual view, you simply find the event that you wish to bind your control to such as Click, and specify the name your event handler will take. When you press Enter you will be taken to the code view where you can see the event handler displayed with the name you provided.
Trivia – Look at the XAML!
If you look at the XAML for the above event-binding, you will see both the Click event as well as our event handler ButtonOkClicked appear:
<Button HorizontalAlignment=”Left” Margin=”130, 92, 0, 86” x: Name=”btnOK” Width=”80” Content=”OK” Click=”ButtonOkClicked”/>
It is the above XAML definition that tells your final application to route all Click events attached to the button to the ButtonOkClicked event handler. A program like Blend or Visual Studio’s Designer serves primarily to provide a nice interface for creating the XAML snippet I posted above.
On the next page, let’s take a look at the code based approach.
In the previous page you got a brief overview of what event handlers are and how to add them usual a visual editor like Visual Studio or Expression Blend.
Let’s say we have a button called btnOK, and our goal is to attach an event to it using just code. All you have to do is pick the appropriate event you wish to use and link it to a new RoutedEventHandler with the name of your event handler:
btnOK.Click += new RoutedEventHandler (ButtonOkClicked);
If you use Visual Studio for writing the code, the inline auto-complete is very handy:
[ auto-complete is quite helpful in times like this ]
Right now you may be wondering how anybody could know to use RoutedEventHandler without the help of AutoComplete. If you are just getting started with WPF, it may be difficult to know that, but just remember that you need to bind an event to an event handler. Later in this article and in greater detail in subsequent articles, I will explain these in more detail.
Like I hinted at in the previous paragraph, one disadvantage with the code approach is that if you want to use events or event handlers beyond the common ones that you are familiar with, you will have to spend some extra time combing through the displayed methods and properties for your object to find the appropriate event:
[ browsing through methods and properties ]
In the visual approach, all events are grouped together in one pane, but in the larger scheme of things, it is just a minor detail that you should be aware of.
private void ButtonOkClicked (object sender, RoutedEventArgs e)
{ This. Close (); }
Notice that the event hander takes in two arguments from the event that fired – the sender and the event. Basically, the sender is the control you clicked on, and the event (e) is the particular input that triggered the event handler such as a key press or a mouse click.
If you took the visual approach, the above event handler would automatically be created for you. If you used the code approach, you would need to define an event handler following a similar structure with the two arguments. Let’s look in greater detail at what the two arguments actually do on the next few pages.
private void ButtonOkClicked (object sender, RoutedEventArgs e)
{ this. Close (); }
But, you will run into cases where you want to do more than just have one control bound to an event handler. When dealing with many interactive controls created using code, you’ll find that knowing more about the sender and event can be helpful.
The Sender
If you want know exactly which control triggered the event, you will need to modify the sender object. The problem, though, is that your controls are of types like Button, Text Field, Checkbox, etc. The argument is of type object. Unfortunately, you cannot simply type in Button foo = sender.
What you need to do is typecast the object into the type of the control that called it. The following code shows an example of how I access the sender Button:
private void ButtonOkClicked(object sender, RoutedEventArgs e)
{ Button clickedButton = (Button) sender;
MessageBox.Show (clickedButton.Name); }
Notice that I create a new Button object called clickedButton that casts my sender object as a Button also. This allows me to access my button’s properties just as if I were manipulating my button directly by name.
When casting, you should make sure that you are actually allowed to cast to that object. For example, I cannot cast my sender object as Checkbox even though both it and a Button can be based on an object. Despite us not knowing what the object type for sender really refers to, internally, WPF has a good idea: P
If you do try to cast something to a type that it cannot be cast to, you will receive an InvalidCastException:
[ I receive an InvalidCastException when tricking a checkbox to behave like a button ]
If you are in a situation where you don’t know what the type of the control you clicked on is, you can use the GetType property to find out:
private void ButtonOkClicked (object sender, RoutedEventArgs e)
{ MessageBox.Show (sender.GetType().Name); }
Now that you have a good idea of how to access the sending object, let me touch back on a topic that I left incomplete earlier. Earlier, I mentioned that the above approach is just as good as if I were “manipulating my button directly by name.” The question is, why am I not directly manipulating the button?
The reason is that, when you have many controls such as an array of Buttons that map to the same event handler, you cannot easily explicitly access each button individually. For example, check out the following mini-application that draws many styled buttons and allows you to interact with them individually:
[ a small demo that shows why code-based event handling is often needed ]
Click here to run the WPF demo and view/download the source code for the above application.
In the above application, when you click on a button, the button’s name is displayed a as a message box. Because I am creating each button dynamically using code, I have to cast the sender object in my event handler so that I can know which button has been rolled over.
Note – Determining the Object without Using Sender
In this section, I explained how to use the sender object to determine what object is passed in. You are not limited to using just the sender though. You can actually use your event e itself to cast the source of the event into what you want:
Button clickedButton = e.Source as Button;
The reason I did not explain the above code in this section because it logically makes more sense to have two arguments standing for two different things – the object and the event
We are almost done with this article! On the next page page I will wrap things up and discuss what the event argument can help you to do.
private void ButtonOkClicked(object sender, RoutedEventArgs e)
{ this.Close(); }
You will get more specialized event arguments depending on what you are trying to do, and those arguments provide you with greater flexibility to deal with these events. Let’s say you have a textbox, and each time you type a character, you want to do something. For this scenario, you are looking at a very particular type of event argument – one that takes key presses into account.
Let’s give our text box the name txtBoxMain, and let’s bind a KeyDown event to an event handler called KeyCount. Visually in Expression Blend, you would simply enter your KeyCount event handler and press Enter:
[Again, you can use a visual approach to bind an event to an event handler]
In code, you would do the following:
txtBoxMain.KeyDown += new KeyEventHandler(KeyCount);
Regardless of which approach (visual or code) that you took, your event handler would look like the following:
private void KeyCount (object sender, KeyEventArgs e)
{ }
Notice that my event argument is now KeyEventArgs. The KeyEventArgs class contains a lot of useful methods that I can use to do more with my event than just recognize that it happened. For example, the following code shows me displaying the letter/name of the key pressed in a message box:
private void KeyCount (object sender, KeyEventArgs e)
{ MessageBox.Show (“Key pressed is: ” + e.Key.ToString()); }
The following is the image you see when you run the above code and press the letter k:
[by using Key I can determine which key was pressed ]
While this example dealt with keyboard arguments, you can bind similar events to their respective event handlers for the mouse, the stylus, etc. Depending on which event handler variation you use, the number of properties you can access will vary. The differences in code among the various actions are too minor to cover in detail in this article. It would also be a bit too boring!
To give you a flavor of the extent of the variation, your argument e’s type may be MouseEventArgs for a mouse related event, and you may not have a Key structure from which to determine key presses, but you will have access to the Left and Right mouse buttons. You can find similar yet distinct variations among the other input methods.
A FrameworkElement could possible contain other sibling and child elements, which forms a tree of elements. In the WPF, the parent element can provide information to child elements, providing usage, customization, and visibility to the potentially smaller, nested objects. “Control Composition” is a term used to describe the creation or design concepts used when creating controls and providing thought to the handling of information within the tree. It leads developers to creating better structure for controls to control and direct the logical flow of events for the tree and its members.
There are 3 types of routed events:
* Tunneling
* Bubbling
* Direct
A Bubbling event is the first place we will start, as it provides event handling from the originating element of a Visual Tree to the root of the tree. Tunneling events, on the other hand, fire in the opposite direction and are used to hook into the pre-condition of the controls within the visual tree from the top of the visual tree down. Tunneling and Bubbling event models assist in the complicated logic associated to events raised from / by controls within other controls. Developers can hook into an action before or after it occurs in context to its parent and child controls.
The goal of this article and other articles on this site (and I’m sure others) is to give you a brief overview of the common uses of a particular use of technology and cover in greater detail the subtle, not-so-common uses that cause endless frustration and sleepless nights. At least that is how I justify having the trivial Note boxes and providing similar sets of code for the same task haha.
One of the most important thing is to update the IIS application pool settings while deploying an application.
The following code helps to update the application pool settings.
#region Using Statements
using System;
using System.DirectoryServices;
#endregion
namespace AppPoolSetter
{
/// <summary>
/// This class updates the settings of applicatin pool.
/// </summary>
static class Program
{
#region Methods
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//Call the method for difference application pools.
SetPoolSettings(“poolname1″);
}
private static void SetPoolSettings(string poolName)
{
//Creates an instance of DirectoryEntry using specified application pool path.
DirectoryEntry appPool = new DirectoryEntry(“IIS://localhost/W3SVC/AppPools/” + poolName + “”);
if (appPool != null)
{
try
{
//Updates the properties in application pool
appPool.InvokeSet(“PeriodicRestartTime”, 0);
appPool.InvokeSet(“IdleTimeout”, 90);
appPool.InvokeSet(“AppPoolRecycleTime”, false);
appPool.CommitChanges();
//Log entry for changed settings.
System.Diagnostics.EventLog.WriteEntry(“AppPoolSetter “, “Changed settings for app pool ” + poolName);
}
catch (Exception ex)
{
//log entry for failed settings.
System.Diagnostics.EventLog.WriteEntry(“AppPoolSetter “, “Failed settings for app pool” + poolName + ” Error stack: ” + ex.Message);
}
}
}
#endregion
}
}
Introducing Windows Azure
Implementing Security in a Windows Domain
In the following exercises, you will see how to use transport and message level security in some common scenarios that can arise within a single organization. Because it is easier to demonstrate and explain things this way around, you will start by learning how to implement message confidentiality by encrypting messages. You will then see how to authenticate users running in a Windows environment, and finally, how to use the Windows Token Role provider to authorize access to operations.
Protecting a TCP Service at the Message Level
Message encryption is a very common requirement of most distributed systems; so much so that the majority of the standard bindings available in the WCF library encrypt messages by default. For example, the NetTcpBinding binding automatically encrypts data at the transport level if you have configured SSL over TCP. The NetTcpBinding binding also supports encryption at the message level, giving you a greater degree of control over the encryption algorithm used and without requiring you to configure SSL. You will use message level security to implement message encryption in the first exercise.
Enable message level encryption for the NetTcpBinding binding for the WCF service
This solution contains three projects: the ProductsService service, the ProductsService-Host application, and the ProductsClient. These projects are configured to catch and handle SOAP faults, as described in Chapter 3, “Making Applications and Services Robust.”
The WCF Service Configuration Editor generates a binding configuration with the default settings for the NetTcpBinding binding.
These settings cause the binding to use message level security. Users will be expected to provide a valid Windows username and password, and all messages will be encrypted by using the Advanced Encryption Standard (AES) 128-bit algorithm. This is a widely used algorithm that is relatively quick to perform but should provide sufficient privacy for messages inside an organization (if you are sending messages across a public wide area network such as the Internet, you might prefer to use Basic256, which is the default value).
| Note | If you set the Mode to None, then the binding will not encrypt data and any settings you specify for transport or message level security will be ignored. The Transport mode selects transport level security (SSL) rather than message level security, and the TransportWithMessageCredential mode uses message level security to provide the identity of the user for authorization purposes, while performing encryption at the transport level. Transport level encryption is usually more efficient than message level encryption, although it requires more configuration on the part of the administrator. |
This action associates the binding configuration with the binding. All messages sent by using the ProductsServiceTcpBinding will use message level security and will be encrypted.
12.…
13.<system.serviceModel>
14. <bindings>
22. </bindings>
23. <services>
31. </services>
32. …
</system.serviceModel>
Be careful not to change anything in this file. Close the App.config file when you have finished examining it.
The service will expect clients that connect to the endpoint for this binding to use the same message level security settings. You will configure the client next.
Enable message level encryption for the NetTcpBinding binding for the WCF client
| Note | The client configuration file already contains a binding configuration for the basicHttpBinding that was generated in Chapter 1, “Introducing Windows Communication Foundation.” Be careful not to modify this binding configuration by mistake! |
| Note | If you select a different algorithm suite for the client and server, they will not be able to decipher each other’s communications. This will result in a runtime exception in the channel stack. If you are curious about this, try setting the AlgorithmSuite to TripleDes (for example) and examine the exception that occurs when you run the solution later. |
This exercise has shown you how easy it is to configure a WCF service and client application to secure messages by performing encryption, but how do you actually know that the messages have been encrypted? To answer this question, you can enable message tracing and then examine the messages as they flow in and out of the service.
Configure message tracing for the WCF service
The LogEntireMessage property specifies whether the trace output should include the body of messages sent and received. Setting this property to True includes the body of the message. The default value, False, only traces the message header. Setting the LogMessagesAtServiceLevel property to True traces messages as they are presented to the service and as they are output from the service. If you are using message level security, this trace will show the unencrypted messages after they have been received and decrypted at the message level (for incoming messages) or before they are encrypted (for outgoing messages). Setting the LogMessagesAtTransportLevel property to True traces messages as they are sent to or received from the transport level. If you are using message level security, the messages traced at this point will be encrypted, although if you are using transport level security messages will already have been decrypted (for incoming messages) or not yet encrypted (for outgoing messages) at this point.
| Important | Tracing at the message level records messages in their unencrypted form. You should ensure that you protect the trace files that are generated and only let authorized users examine this data. |
All tracing information for WCF is received from one or more trace sources. In this case, you will use the MessageLogging source, which traces messages. You can also use other sources. For example, the ServiceModel source traces events that occur in a service, such as tracking when a service starts listening, receives requests, and sends responses.
A listener object is responsible for receiving data from the trace sources, formatting and filtering them, and then sending them to a destination.
The InitData property specifies the name of the file that the listener will use for saving trace data. When tracing starts, if this file does not exist, the listener will create it; otherwise, it will append trace information to the end of any existing data in the file.
Run the WCF client and service and examine the trace output
| Tip | Expand the Action column in this pane to see more of the name for each action. |
Protecting an HTTP Service at the Transport Level
If you recall, the ProductsServiceHost application exposes two endpoints for clients to connect to: one based on the TCP protocol and the other using HTTP. The HTTP endpoint is configured to use the BasicHttpBinding binding. The BasicHttpBinding binding conforms to the WS-BasicProfile 1.1 specification and is intended for use with existing legacy Web services and clients. It is fully interoperable with ASP.NET Web services. By default, this binding provides minimal security; it does not support message level encryption or authentication, for example. To implement message confidentiality and remain interoperable with ASP.NET Web services, you should use transport level security. This requires you to configure HTTPS.
| Note | The BasicHttpBinding binding also supports message level security. Ordinary ASP.NET Web services and client applications do not implement the WS-Security specification, and so will not be able to communicate with a service that implements message level security. However, Microsoft Web Services Enhancements (WSE) does support WS-Security, so Web services that you create by using WSE can communicate with a WCF service through an endpoint based on the BasicHttpBinding binding by using message level security. |
Specify transport level security for the BasicHttpBinding binding for the WCF service
In this mode, message security is provided by using HTTPS. You must configure SSL for the service by using a certificate. The client authenticates the service by using the service’s SSL certificate. The service authenticates the client by using the mechanism specified by the TransportClientCredentialType property. The default value of None does not provide any authentication–you will examine some of the other values you can specify for this property later in this chapter.
https://localhost:8000/ProductsService/ProductsService.svc
The next step is to reconfigure and modify the client to connect to the service by using the endpoint corresponding to the BasicHttpBinding binding.
Specify transport level security for the BasicHttpBinding binding for the WCF client
https://localhost:8000/ProductsService/ProductsService.svc
10.ProductsServiceClient proxy = new ProductsServiceClient(“BasicHttpBinding_
IProductsService“);
If you try and run the client and service at this point, the client will fail with a CommunicationException, like this:
This error occurs because you have not yet configured transport security for the HTTPS protocol. In the next exercise, you will create a certificate for the WCF service, and configure SSL for the service by using the httpcfg utility.
Configure the WCF HTTP endpoint with an SSL certificate
A command prompt window opens, with an environment configured for running the Windows SDK tools.
makecert -sr LocalMachine -ss My -n CN=HTTPS-Server -sky exchange -sk HTTPS-Key
The makecert utility is a useful tool for creating test certificates that you can use for development purposes. The command shown here creates a certificate that is stored in the Personal certificates store for the LocalMachine account. For detailed information about the options for the makecert utility, see the Windows SDK Documentation installed with the Windows SDK.
| Important | Certificates that you create by using the makecert utility should not be used in a production environment as they are not certified by a verifiable certification authority. Remember that the service sends this certificate to the client to prove its identity. The client must be able to trust that this certificate was created by a reliable source that can verify the veracity of the service. When deploying a production service, you should obtain your certificates from recognized certification authority, such as VeriSign or Thawte. Alternatively, you can use Windows Certificate Services, which enables an enterprise to generate its own certificates. |
To use the httpcfg utility to configure SSL for the service, you need to find the thumb-print of the certificate. The thumbprint is a hexadecimal string that uniquely identifies the certificate. You can obtain this information by using the Certificates Microsoft Management Console snap-in.
mmc
This command starts the Microsoft Management Console, displaying the default Console Root window.
| Tip | You might find it useful to simply select the text in the lower window and copy it to the Windows clipboard. |
httpcfg set ssl -i 0.0.0.0:8000 -h c390e7a4491cf97b96729167bf50186a4b68e052
If this command is successful, it should report the message “HttpSetServiceConfiguration completed with 0.”
| Note | Be very careful to specify the correct thumbprint. If you type an invalid thumb-print, the command still succeeds, but the client will not be able to communicate with the service as the thumbprint does not refer to a valid certificate. |
This command binds the certificate with the thumbprint indicated with the –h flag to the port indicated by the –i flag. The port is specified as the IP address of the computer followed by the port. Specifying an IP address of 0.0.0.0 denotes the local computer.
| Note | Under Windows Vista, use the netsh command to configure SSL rather than httpcfg., like this: netsh http add sslcert ipport=0.0.0.0:8000 certhash= c390e7a4491cf97b96729167bf50186a4b68e052 appid={}. The certhash parameter specifies the thumbprint. The appid parameter is a GUID that identifies this binding of the certificate to the port; you can use any unique GUID. |
| Warning | When a client application receives a certificate from a server, the WCF runtime attempts to ascertain that the certificate is valid and that the authority that issued it is trusted. The WCF runtime will fail this check when using the certificate that you have just installed. The following exercise shows how to force the WCF runtime to override this check and allow this certificate to be used. You should never do this in a production environment! The code is provided as-is, and without further explanation (it is not the author’s work–it was written by developers at Microsoft and is included in one of the WCF technology samples provided with the Windows SDK). In the real world, you should go out and buy a valid certificate. |
Add code to the WCF client to override certificate validation checking
3. using System.Security.Crytography.X509Certificates;
using System.Net;
| Note | The code for this class is available in the PermissiveCertificatePolicy.cs file in the Chapter 4 folder, if you don’t want to type it in manually. |
5. // WARNING: This code is only needed for test certificates such as those
6. // created by makecert. It is not recommended for production code.
7. class PermissiveCertificatePolicy
8. {
18.
23.
31.
34.}
36.…
37.PermissiveCertificatePolicy.Enact(“CN=HTTPS-Server”);
38.ProductServiceClient proxy = new ProductServiceClient(…);
39.…
Run the WCF client and service
Protecting an HTTP Service at the Message Level
You can configure the BasicHttpBinding binding to provide message level security by selecting the Message security mode for the binding. In this mode, the service uses SOAP message level security to encrypt the message. The service must have a certificate installed, and the client uses the public key from the service’s certificate to perform the encryption. The service can send the certificate containing its public key at the start of the message exchange, or an administrator can install the service certificate on the client computer before the client application (in which case you must specify how to locate the service certificate in the client certificate store by adding a service behavior using the <serviceCredentials> element to the client configuration file). You will learn more about this in Chapter 5. Additionally, the only authentication mechanism supported by a WCF service that uses this mode requires that the client application identifies itself with a certificate–you cannot use authentication mechanisms such as Windows Integrated Security with this mode.
One other option is to use the TransportWithMessageCredential security mode. This is a hybrid combination of message level and transport level security. The service uses the HTTPS protocol and a certificate to provide message integrity and confidentiality. Client authentication is handled at the message level by using SOAP message security, and the client application can provide a username and password to identify the user. You will learn more about this security mode in Chapter 5.
If you really want to implement message level security for a WCF service with the minimum of fuss and configuration, you can opt to use the WSHttpBinding binding. The WSHttpBinding binding conforms to the current WS-* specifications and follows the WS-Security specification for encrypting messages and authenticating users by default. The following exercises demonstrate how to use the WSHttpBinding binding to implement message level security over HTTP.
Configure the WCF service to use the WSHttpBinding binding
| Open table as spreadsheet Property | Value |
| Name | ProductsServiceWSHttpEndpoint |
| Address | http://localhost:8010/ProductsService/ProductsService.svc |
| Binding | wsHttpBinding |
| Contract | Products.IProductsService |
Configure the WCF client to use the WSHttpBinding binding
| Open table as spreadsheet Property | Value |
| Name | WSHttpBinding_IProductsService |
| Address | http://localhost:8010/ProductsService/ProductsService.svc |
| Binding | wsHttpBinding |
| Contract | ProductsClient.ProductsService.IProductsService |
6. ProductsServiceClient proxy = new
ProductsServiceClient(“WSHttpBinding_IProductsService“);
Run the WCF client and service and examine the trace output
The WSHttpBinding binding uses the 256-bit version of the AES encryption algorithm to encrypt data by default. You can select a different algorithm by creating a binding behavior and specifying the algorithm to use in the AlgorithmSuite property of the behavior, as you did when configuring message level security for the NetTcpBinding binding earlier in this chapter.
Authenticating Windows Users
So far, you have seen how to configure the NetTcpBinding, BasicHttpBinding, and WSHttp-Binding bindings to support confidentiality and privacy by encrypting messages. However, transporting messages securely is only useful if a service can verify the identity of the user running the client application. In the exercises that follow, you will look at how a service can authenticate a user when the client application and service are both running within the same Windows domain. In Chapter 5, you will see how to perform authentication when a client and service are located in different, possibly non-Windows, security domains.
You will start by adding code to the ProductsService service that displays the name of the user calling the ListProducts operation. You will then be able to see the effect that the authentication options available in WCF have on the identity passed from a client application to a service.
| Note | You can configure authentication to be largely transparent to the WCF service. You will see in the exercises in this section that most of the actual authentication process is performed by the WCF runtime executing the service. All the service needs to do is specify the type of authentication it requires. |
Display the name of the user calling an operation in the WCF service
This file contains the code that implements the operations for the ProductsService service.
4. using System.Threading;
using System.Windows.Forms;
6. string userName = Thread.CurrentPrincipal.Identity.Name;
7. MessageBox.Show(“Username is ” + userName,
“ProductsService Authentication”);
The first statement retrieves the name of the Windows user that the current thread is running on behalf of. The second statement displays the username in a message box.
9. ProductsServiceClient proxy = new
ProductsServiceClient(“BasicHttpBinding_IProductsService“);
A message box appears, displaying the user name sent by the client application. The user name will appear to be missing. This is not an error. By default, the BasicHttpBinding binding does not send authentication information about users. All messages are sent as the anonymous user.
In the next set of exercises, you will revisit the BasicHttpBinding binding and implement user authentication. Many of the authentication options available for this binding apply to other bindings as well.
Configure the BasicHttpBinding binding for the WCF service to use Basic authentication
Notice that the TransportClientCredentialType property is currently set to None, so the service is not expecting client applications to provide authentication information about users, and anyone who can connect to the service can send it messages and invoke operations.
When using Basic authentication, the client application must provide a username and password, which is transmitted to the service. The WCF runtime executing the service can use this information to authenticate the user running the client application, and if the user is valid, it will provide the identity of the user to the service.
The client fails with a MessageSecurityException exception, “The HTTP request is unauthorized with client authentication scheme ‘Anonymous’… .” The WCF runtime for the service was expecting the client application to provide a username and password, which it has not done.
Modify the WCF client to supply the user credentials to the service
8. ProductsServiceClient proxy = new
10.proxy.ClientCredentials.UserName.UserName = “LON-DEV-01\\Student”;
11.proxy.ClientCredentials.UserName.Password = “Pa$$w0rd”;
The ClientCredentials property of a WCF proxy object provides a mechanism for a client application to provide the credentials to send to the service. The UserName property of ClientCredentials can hold a username and password. Other properties are available, such as ClientCertificate, which enable you to supply different types of credentials information as required by the service configuration.
| Warning | This code is for illustrative purposes in this exercise only. In a production application, you should prompt the user for their name and password. You should never hard-code these details into an application. |
A message box appears, displaying the user name sent by the client application. This time, the user name appears as expected, verifying that the operation is executing with the credentials of the user.
Using Basic authentication, you can provide the username and password of the user, and the WCF runtime executing the service will check that these credentials are valid. If you provide an invalid username of password, the WCF runtime will reject the request and the client will receive another MessageSecurityException exception with the message “The HTTP request was forbidden… .”
Basic authentication is a good solution if the user running the client application is not currently logged into the security domain used by the service.
| Note | You can also configure the NetTCPBinding and WSHttpBinding bindings at the message level to require Username authentication. This is very similar to Basic authentication at the transport level as far as client application is concerned, although somewhat different as far as the service is concerned, as it takes responsibility for authenticating the user itself (typically using a custom database of usernames and passwords). However, usernames and passwords are not encrypted at the message level, so WCF insists that the underlying transport provide encryption to prevent the credential details being transmitted across an open network as clear text. |
If the user is logged in to the domain, then you can make use of Windows Integrated Security to provide the user’s credentials automatically, rather than prompting the user for them again (or worse still, hard-coding them in your application!).
Configure the BasicHttpBinding binding for the WCF service and client to use Windows authentication
The message box appears displaying your Windows username, which was sent by the client application. However, rather than you having to supply the username and password, the WCF runtime executing the client application picked this information up from the user’s process automatically.
| Note | If you omitted to comment out the lines that populated the ClientCredentials object, the solution still works; the credentials provided are simply ignored. However, note the ClientCredentials property has a Windows property that you can use to provide a domain, username, and password to the service if you want the service to run as a different Windows user. Any values that you specify in the Windows property override those retrieved from the user’s login process. The usual warnings about hard-coding usernames and password in your code still apply:
proxy.ClientCredentials.Windows.ClientCredential.Domain = “LON-DEV-01″; proxy.ClientCredentials.Windows.ClientCredential.UserName = “Administrator”; proxy.ClientCredentials.Windows.ClientCredential.Password = “P@ssw0rd”; |
When you use Windows Integrated Security, usernames and passwords are not transmitted as clear text. You can use Windows Integrated Security at the message level with the NetTCPBinding and WSHttpBinding bindings without needing to implement encryption at the transport level.
Examine the authentication mechanism used by the NetTcpBinding binding
You have been using Windows Integrated Security without realizing it in earlier exercises!
| Note | The WSHttpBinding binding also defaults to using Windows Integrated Security. |
7. ProductsServiceClient proxy = new
ProductsServiceClient(“NetTcpBinding_IProductsService“);
The familiar message box appears, displaying your Windows user name, proving that the NetTcpBinding automatically picks up your identity from Windows.
Authorizing Users
After a service has established the identity of the user, it can then determine whether the service should perform the requested operations for the user. Different operations in a service could be considered more privileged than others. For example, in the ProductsService service, you might wish to let any staff who work in the warehouse query the product information in the AdventureWorks database but limit access to operations such as ChangeStockLevel, which modify data, to staff members who are stock controllers. WCF can use the features of the .NET Framework to enable a developer to specify which users and roles have the authority to request operations. You can perform this task declaratively (by using attributes) or imperatively (by adding code to the operations).
The authorization mechanism used by WCF requires access to a database defining users and the roles that they can fulfill. If you are performing authentication by using Active Directory, it makes sense to use the Active Directory database to hold the roles for each user as well. Therefore, the first step is to ensure that the WCF service is configured to retrieve roles from Active Directory by using the Windows Token Role Provider.
Configure the WCF service to use the Windows Token Role Provider
The ProductsBehavior behavior currently contains the serviceDebug element. You added this behavior to the service in Chapter 3.
The serviceAuthorization behavior is added to the list of behaviors.
By default, WCF uses the Windows Token Role Provider to authenticate users, so you don’t actually need to change anything. However, you can configure the serviceBehavior element to specify a different role provider, such as the SQL Role Provider or the Authorization Store Role Provider mentioned earlier in this chapter. (You will configure the service to use the SQL Role Provider in Chapter 5.)
The next step is to define the roles that can request the operations in the WCF service. When using the Windows Token Role Provider, Active Directory groups correspond to roles, so you define groups in the Active Directory database and add users to these groups.
| Note | The following exercise assumes you do not have access to the Active Directory database for your organization, so it uses the Windows local users and groups database instead. The principles are the same, however. |
Create groups for warehouse staff and stock controller staff
The Computer Management console appears.
The two new groups should appear in the list of groups in right pane of the Computer Management console.
| Open table as spreadsheet Property | Value |
| User | name Fred |
| Password | Pa$$w0rd |
| Confirm password | Pa$$w0rd |
| User must change password at next logon | Unchecked |
| Open table as spreadsheet Property | Value |
| User name | Bert |
| Password | Pa$$w0rd |
| Confirm password | Pa$$w0rd |
| User must change password at next logon | Unchecked |
The two new users should appear in the list in the right pane of the Computer Management console.
Bert is added to the WarehouseStaff group.
Fred is added to the WarehouseStaff and StockControllers groups–he has two roles.
You can now use the groups you have just defined to specify the roles that can request each of the operations in the ProductsService service. To show how to specify authorization declaratively and imperatively, you will use attributes to specify the role for the operations that simply query the AdventureWorks database, but you will write code to specify the role that can modify the database.
Specify the roles for the WCF service operations
3. using System.Security;
4. using System.Security.Permissions;
using System.Security.Principal;
6. [PrincipalPermission(SecurityAction.Demand, Role="WarehouseStaff")]
7. public List<string> ListProducts()
8. {
}
The PrincipalPermission attribute specifies the authorization requirements of the method. In this case, the SecurityAction.Demand parameter indicates that the method requires that the user meet the criteria specified by the following parameters. The Role parameter indicates that the user must be a member of the WarehouseStaff role.
You can identify specific users by using the optional Name parameter. However, if you specify Name and Role, then the user must match both criteria to be granted access (if the user is not a member of the specified role, they will not be allowed to execute the method). If you require users to be granted access to the method if they have a specific name or are a member of a specific group, you can use the PrincipalPermission attribute twice, like this:
[PrincipalPermission(SecurityAction.Demand, Role="WarehouseStaff")]
// LON-DEV-01\Student is not a member of the WarehouseStaff group
[PrincipalPermission(SecurityAction.Demand, Name="LON-DEV-01\\Student")]
public List<string> ListProducts()
{
…
}
You can also specify SecurityAction.Deny as the first parameter to the PrincipalPermission attribute. If you do this, the specified users and roles will be explicitly denied access to the method.
11.[PrincipalPermission(SecurityAction.Demand, Role="WarehouseStaff")]
12.public Product GetProduct(string ProductNumber)
13.{
15.}
16.[PrincipalPermission(SecurityAction.Demand, Role="WarehouseStaff")]
17.public int GetStockLevel(string ProductNumber)
18.{
}
21.public bool ChangeStockLevel(…)
22.{
}
The first statement retrieves the identity information for the user and uses it to create a WindowsPrincipal object. Note that the identity returned by the current thread must be cast to a WindowsIdentity object. A WindowsPrincipal object is a representation of the user. It exposes the IsInRole method that this code uses to determine whether the user is a member of the StockControllers role. The IsInRole method returns true if the user is a member of the role, false otherwise. If the user is not a member of the role, the code throws a SecurityException exception with the message “Access Denied.”
| Warning | It is tempting to provide more detail in the SecurityException exception. This practice is not recommended, as it could provide an attacker with useful information that they might be able to use to try and infiltrate your system. Keep the exception message bland! |
Test the authorization for the WCF service
Assuming you are not currently logged in to Windows as Fred or Bert, the client application stops and reports the message “Access is denied” when attempting to invoke the ListProducts operation. This is because the authenticated Windows account for the client application must be a member of the WarehouseStaff role:
6. ProductsServiceClient proxy = new
8. proxy.ClientCredentials.Windows.ClientCredential.Domain = “LON-DEV-01″;
9. proxy.ClientCredentials.Windows.ClientCredential.UserName = “Bert”;
10.proxy.ClientCredentials.Windows.ClientCredential.Password = “Pa$$w0rd”;
These statements explicitly set the Windows credentials for the user to those of Bert. The WCF runtime on the client will send these credentials to the service, rather than using those in the user’s login process.
This time, Bert is a member of the WarehouseStaff role and is granted access to the ListProducts, GetProduct, and CurrentStockLevel operations.
17.proxy.ClientCredentials.Windows.ClientCredential.Domain = “LON-DEV-01″;
18.proxy.ClientCredentials.Windows.ClientCredential.UserName = “Fred”;
proxy.ClientCredentials.Windows.ClientCredential.Password = “Pa$$w0rd”;
Fred is a member of the WarehouseStaff role and the StockControllers role, and so he is able to invoke all the operations in the ProductsService service.
Using Impersonation to Access Resources
Authenticating a user establishes the identity of the user to the WCF service, which can then perform authorization checks to verify that the user should be allowed to perform the requested operation. The method that implements the operation might require access to resources on the computer running the WCF service. By default, the service will attempt to gain access to these resources by using its own credentials. For example, when a method in the ProductsService service connects to the AdventureWorks database, it does so as the account running the service. When using Windows authentication, it is possible to specify that the WCF service should access resources by using the authenticated identity of the user instead. So, if Fred has been granted access to the AdventureWorks database, the WCF service can connect to SQL Server as Fred and will have access to all the database resources to which Fred has been granted access. If the user connects as Bert, the WCF service might be able to use a different set of resources in the database, depending on Bert’s access rights. The same principle applies to other resources, such as files, folders, and network shares. Using impersonation gives an administrator fine-grained control over the ability of a WCF service to read or write possibly sensitive information and can provide an additional degree of security–just because the user can connect to the WCF service, they might not be able to perform operations that retrieve or modify confidential data unless the administrator has explicitly granted the user access to this data.
You can enable impersonation for an operation by setting the Impersonation property of the OperationBehavior attribute, like this (shown in bold):
[PrincipalPermission(SecurityAction.Demand, Role="WarehouseStaff")]
[OperationBehavior(Impersonation=ImpersonationLevel.Required)]
public List<string> ListProducts
{
…
}
Specifying the value ImpersonationLevel.Required enforces impersonation. The client application must also agree to this requirement and specify the level of impersonation that the WCF service application can use (you will see how to do this shortly). You can also specify the ImpersonationLevel.Allowed, which enables the WCF service to impersonate the user if the client application permits, but executes as the identity running the service application if not, and ImpersonationLevel.NotAllowed, which disables impersonation.
If you need to specify an impersonation level setting for all operations, you can set the ImpersonateCallerForAllOperations attribute of the <serviceBehavior> element of the service behavior to true in the service configuration file, as shown in bold below:
<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
…
<system.serviceModel>
…
<services>
<service behaviorConfiguration=”ProductsBehavior” name=”Products.ProductsServiceImpl”>
…
</services>
<behaviors>
<serviceBehaviors>
<behavior name=”ProductsBehavior”>
<serviceAuthorization principalPermissionMode=”UseWindowsGroups”
impersonateCallerForAllOperations=”false” />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
You configure the client application to indicate the level of impersonation that the service can use by defining a behavior for the endpoint and specifying the AllowedImpersonationLevel property. The following fragments of a client configuration file highlight the pertinent elements:
<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name=”ImpersonationBehavior”>
<clientCredentials>
<windows allowedImpersonationLevel=”Impersonation” />
…
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
…
<client>
…
<endpoint
address=”http://localhost:8010/ProductsService/ProductsService.svc”
behaviorConfiguration=”ImpersonationBehavior”
binding=”wsHttpBinding”
contract=”ProductsClient.ProductsService.IProductsService”
name=”WSHttpBinding_IProductsService” />
</client>
</system.serviceModel>
</configuration>
You can specify one of the following values for the AllowedImpersonationLevel property: