Tuesday, April 20, 2010

Silverlight Custom Authentication Domain Service with RIA Services

Silverlight Data and Controls Series

Introduction

One of the next things that I would like to cover is how to do a custom log in with Silverlight 4. Many of the examples and demonstrations out there show how to do windows integrated or Forms Based Authentication along with the ASP.net Authentication. These examples are not really that great when you need to hook into a custom username and password repository that is not Active Directory or SQL Server based. You could try to come up with some sort of SSO solution or write your own ASP.net custom authentication provider. However sometimes you just do not need to fight those battles because doing that is not in scope for the implementation that you have in front of you.

In the MIX 2010 conference a speaker brushed over this topic for about five minutes in his presentation (blog) and this is exactly what I needed. I was able to download the code, pull it into my solution and I was off and running. This solution gave me exactly what I wanted because it was simple, straight forward and is very flexible. His example is the basis for the solution I will present to you.

This solution will use the HTTP context along with the Silverlight’s authentication mechanism to secure all RIA Service calls. This will effectively make sure your application is somewhat secure in that no data can be retrieved unless the user session has been created via a custom authentication domain service..

Getting Started

I am going to take the MVVM solution that I created in an earlier blog and start from there. I recommend review that blog to get an understanding of the solution.

We will need to add several things to this solution to get this working:

  • Added LogonView.xaml to the View folder for the logon control.
  • Added both LogonCommand.cs and LogonViewModel.cs to the ViewModel folder.
  • Added both FormsAuthenticationService.cs and CustomAuthenticationService.cs to the Services folder. Please note that when adding this two new services to the domain model we will not be using the Authentication Domain Service, instead just use Domain Service template.
  • Added UserDTO.cs to the Model folder.
  • Added a Global.asax file which will use to instanciate domain services with the domain object.

Here is a screenshot of my updated solution.

Untitled3

User Class

Let us first start out with the user class. There is really not much to it; all we are using it for is to make accessible other data that we might retrieve as part of the Logon in process. In this case we are going to keep email address and display name for the user in the HTTP session so we do not have to continually retrieve that information to display it to the screen.

namespace MVVM.Web.Model
{
public class UserDTO : UserBase
{
public string Email { get; set; }

public string DisplayName { get; set; }
}
}

Also take note that we inherit from System.ServiceModel.DomainServices.Server.ApplicationServices.UserBase.

Forms Authentication Domain Service

Next we have the class that was provided in this presentation from the MIX 2010 conference. I made only one minor modification to it to return an error in the HTTPContext if there is an issue logging in.

This Domain Service is an abstract class that has must be implemented. It has two methods (GetCurrentUser and ValidateCredentials) that the concrete domain service must implement. As you will see the purpose of this class is to implement the System.ServiceModel.DomainServices.Server.ApplicationServices.IAuthentication interface which will be used in correlation with the RIA Services to check to see if a user object has been properly created for the session.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using System.ServiceModel.DomainServices.Server.ApplicationServices;
using System.Web;
using System.Web.Security;
using System.Security.Principal;

namespace MVVM.Web.Service
{
public class FormsAuthenticationLogonException : System.Exception
{
public FormsAuthenticationLogonException(string message) : base(message) { }
}

public abstract class FormsAuthenticationService<TUser> : DomainService, IAuthentication<TUser> where TUser : UserBase
{

protected abstract TUser GetCurrentUser(string name, string userData);

protected virtual TUser GetDefaultUser()
{
return null;
}

protected abstract TUser ValidateCredentials(string name, string password, string customData, out string userData);

public TUser GetUser()
{
IPrincipal currentUser = ServiceContext.User;
if ((currentUser != null) && currentUser.Identity.IsAuthenticated)
{
FormsAuthenticationTicket ticket = null;

FormsIdentity userIdentity = currentUser.Identity as FormsIdentity;
if (userIdentity != null)
{
ticket = userIdentity.Ticket;
if (ticket != null)
{
return GetCurrentUser(currentUser.Identity.Name, ticket.UserData);
}
}
}

return GetDefaultUser();
}

public TUser Login(string userName, string password, bool isPersistent, string customData)
{
string userData;
TUser user = ValidateCredentials(userName, password, customData, out userData);

if (user != null)
{
FormsAuthenticationTicket ticket =
ticket = new FormsAuthenticationTicket(/* version */ 1, userName,
DateTime.Now, DateTime.Now.AddMinutes(30),
isPersistent,
userData,
FormsAuthentication.FormsCookiePath);

string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);

HttpContextBase httpContext = (HttpContextBase)ServiceContext.GetService(typeof(HttpContextBase));
httpContext.Response.Cookies.Add(authCookie);
}
else
{
HttpContextBase httpContext = (HttpContextBase)ServiceContext.GetService(typeof(HttpContextBase));
httpContext.AddError(new FormsAuthenticationLogonException("Username or password is not correct."));
}

return user;
}

public TUser Logout()
{
FormsAuthentication.SignOut();
return GetDefaultUser();
}

public void UpdateUser(TUser user)
{
throw new NotImplementedException();
}
}

}

Custom Authentication Domain Service

Next is our Custom Authentication Domain Service which inherits form the Forms Authentication Domain Service we just created. It implements both getting and validating a user. Getting the user basically gets data from the session and sets the data in to UserDTO object. The validate method is where you will need to add all custom code to go out to a custom repository of usernames and passwords to validate the user. If UserDTO that is returned is null, then you will ensured that the Silverlight call to the RIA Services will fail, not allowing the user access to data.

The example below is just checking against some hard coded values as part of a proof of concept. Please change it to fit your needs.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using MVVM.Web.Model;

namespace MVVM.Web.Service
{
[EnableClientAccess()]
public class CustomAuthenticationService : FormsAuthenticationService<UserDTO>
{
protected override UserDTO GetCurrentUser(string name, string userData)
{
string[] userDataParts = userData.Split(':');

return new UserDTO()
{
Name = name,
DisplayName = userDataParts[0],
Email = userDataParts[1]
};
}

protected override UserDTO ValidateCredentials(string name, string password, string customData, out string userData)
{
UserDTO user = null;
userData = null;

//TODO: Add code to authenticate user against custom repository of username and passwords
//Below is just checking against two hard coded values and if that succeeds, then
//create the user object.

if (name == "japergis" && password == "Password!")
{
//Create the user object
user = new UserDTO();
user.Name = name;
user.DisplayName = "Apergis, Jason";
user.Email = "japergis@foo.com";
}

if (user != null)
{
//Set custom data fields for HTTP session
userData = user.DisplayName + ":" + user.Email;
}

return user;
}
}
}

Logon View Model

In this example I continue to use the MVVM pattern that I presented in an earlier blog.

The following is a ViewModel object I created for the Logon User control. There is nothing exciting except for the code within the Logon method itself. You will notice there is no code to actually go off and call a logon RIA Service “method”. Instead, you use the System.ServiceModel.DomainServices.Client.WebContextBase to log in. If there is an error with logging in, you will have to put some code in here to send a message to the user through whatever means you want.

using System;
using System.Windows.Input;
using System.ComponentModel;
using System.Collections.Generic;
using System.ServiceModel.DomainServices.Client;
using System.ServiceModel.DomainServices.Client.ApplicationServices;

using MVVM.Web;
using MVVM.Web.Model;
using MVVM.Web.Service;

namespace MVVM.ViewModel
{
public class LogonViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _username;
private string _password;
private EmployeeViewModel _evm;

public string Username {
get {
return _username;
}
set {
_username = value;
RaisePropertyChanged("Username");
}
}

public string Password {
get {
return _password;
}
set {
_password = value;
RaisePropertyChanged("Password");
}
}

public EmployeeViewModel EmployeeViewModel {
get {
return _evm;
}
set {
_evm = value;
}
}

public ICommand LogonCommand
{
get
{
return new LogonCommand(this);
}
}

public void Logon() {
LoginParameters loginParameters =
new LoginParameters(Username, Password);

WebContextBase.Current.Authentication.Login(loginParameters,
delegate(LoginOperation operation)
{
if (operation.HasError)
{
operation.MarkErrorAsHandled();

//Raise message to UI that Log in did not succeed
return;
}
else {
_evm.LoadEmployees();
}
}, null);
}

private void RaisePropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
}
}

Logon Command

Here is the code for the logon on command so that it can be wired up to the button; nothing new here.

using System;
using System.Windows.Input;

namespace MVVM.ViewModel
{
public class LogonCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private LogonViewModel _lvm = null;

public LogonCommand(LogonViewModel lvm) {
_lvm = lvm;
}

public bool CanExecute(object parameter)
{
return true;
}

public void Execute(object parameter)
{
_lvm.Logon();
}
}
}

Logon User Control

Finally here is the xaml code for the logon that uses both the LogonViewModel and LogonCommand to perform the actual logon.

<UserControl x:Class="MVVM.View.LogonView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="48" d:DesignWidth="349">

<Grid x:Name="LayoutRoot" Background="White">
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="txtUsername" VerticalAlignment="Top" Width="120"
Text="{Binding Username, Mode=TwoWay}"/>
<PasswordBox Height="23" HorizontalAlignment="Left" Margin="138,12,0,0"
Name="txtPassword" VerticalAlignment="Top" Width="120"
Password="{Binding Password, Mode=TwoWay}"/>
<Button Content="Logon" Height="23" HorizontalAlignment="Left"
Margin="264,12,0,0" Name="btnLogon" VerticalAlignment="Top"
Width="75" Command="{Binding LogonCommand}"/>
</Grid>
</UserControl>

Web.config

Next we need to do some configuration. First we need to simply modify the web.config to use forms authentication.

<authentication mode="Forms" />

App.xaml

Next we will need to add the following to the App.xaml file in the project. For more information read this - http://msdn.microsoft.com/en-us/library/ee707353(VS.91).aspx. What this basically does is make sure the Silverlight application is using forms based authentication for the login.

<Application.ApplicationLifetimeObjects>
<app:WebContext>
<app:WebContext.Authentication>
<appsvc:FormsAuthentication></appsvc:FormsAuthentication>
</app:WebContext.Authentication>
</app:WebContext>
</Application.ApplicationLifetimeObjects>

If you do not add this to the App.xmal class you will get an error like the following when you try to do the login:

No contexts have been added to the Application's lifetime objects. For WebContextBase.Current to resolve correctly, add a single context to the lifetime objects.

Using User Object on RIA Service

One of the first things that you will want to do is use the user object in your RIA Services. A very simple approach would be to System.Web.HttpContext.Current.User to get information about the user. You can absolutely do that because all that data is right there for you. However if you want to actually use the UserDTO object here is a simple approach.

What we want to do is when a domain service is instantiated, have it receive a UserDTO object that has been loaded form the HTTP context. To do this we need to implement a DomainServiceFactory. First add a Global.asax file to the RIA Services project and in it put the following code.

You will see there is a new class called DomainServiceFactory that implements the IDomainServiceFactory interface. In the CreatedDomainService method it will check to see of the service is of a particular type; in this case an EmployeeDomainService. If so, it will create an instance of the CustomAuthenticationService, get the UserDTO object and set it into the constructor of the EmployeeDomainService.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.ServiceModel.DomainServices.Server;
using MVVM.Web.Service;
using MVVM.Web.Model;

namespace MVVM.Web
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
if (!(DomainService.Factory is DomainServiceFactory))
{
DomainService.Factory =
new DomainServiceFactory(DomainService.Factory);
}
}


internal sealed class DomainServiceFactory : IDomainServiceFactory
{
private IDomainServiceFactory _defaultFactory;

public DomainServiceFactory(IDomainServiceFactory defaultFactory)
{
_defaultFactory = defaultFactory;
}

public DomainService CreateDomainService(Type domainServiceType, DomainServiceContext context)
{
if ((domainServiceType == typeof(EmployeeDomainService)))
{
DomainServiceContext authServiceContext =
new DomainServiceContext(context, DomainOperationType.Query);

CustomAuthenticationService authService =
(CustomAuthenticationService)_defaultFactory.CreateDomainService(typeof(CustomAuthenticationService), authServiceContext);

UserDTO currentUser = authService.GetUser();

DomainService domainService = (DomainService)Activator.CreateInstance(domainServiceType, currentUser);
domainService.Initialize(context);

return domainService;
}
else
{
return _defaultFactory.CreateDomainService(domainServiceType, context);
}
}

public void ReleaseDomainService(DomainService domainService)
{
if ((domainService is EmployeeDomainService))
{
domainService.Dispose();
}
else
{
_defaultFactory.ReleaseDomainService(domainService);
}
}
}

}
}

Then we just need to modify the EmployeeDomainService so that the constructor looks like the following. Another VERY IMPORTANT note is that I have now decorated the EmployeeDomainService with the [RequiresAuthentication()] attribute. Having this is what ultimately locks down the domain service requiring that a user session be created.

    [EnableClientAccess()]
[RequiresAuthentication()]
public class EmployeeDomainService : DomainService
{
UserDTO _user;

public EmployeeDomainService(UserDTO user)
{
_user = user;
}

Note if you had not done what I have just shown and tried to reference the CustomAuthenticationService directly in the constructor of the EmployeeDomainService, you will get errors stating that the CustomAuthenticationService has not been properly initialized. This is considered the best practice for controlling the way your RIA Domain Services are to be created.

Using User Object on Silverlight Client

The last thing you will want to do is potentially get the UserDTO in your Silverlight controls. Well there is no difference. All you need to do is:

public void LoadEmployees() {
//Load the user
CustomAuthenticationContext authContext = new CustomAuthenticationContext();
authContext.Load<UserDTO>(_authContext.GetUserQuery(), UserLoaded, false);
}

private void UserLoaded(LoadOperation<UserDTO> lo)
{
foreach (var user in lo.Entities)
{
System.Diagnostics.Debug.WriteLine(user.Email);
System.Diagnostics.Debug.WriteLine(user.DisplayName);
}
}

Custom Authentication Providers

If you want do something really cool – you can also check into building your own custom authentication provider….

References

Friday, April 16, 2010

Silverlight 4 using MVVM Pattern, RIA Services and Custom Domain Service

Silverlight Data and Controls Series

Introduction

In this blog I am going to give a quick introduction to using Silverlight 4, RIA Services, MVVM Pattern and Custom Domain Service for persistence. I found lots of blogs available on MVVM but none of the code samples were end-to-end and many got a little “preachy”. Plus none of the examples showed integration with RIA Services. I also chose to use a Custom Domain Service because it seems like all blogs these days are using Entity. In my scenario, it is not possible to use Entity because we have to use existing services that have already been created.

In this blog I will give you a straight up introduction to all of these and get you right to the code.

MVVM Background

If you want to read a decent write up about MVVM please read this - http://www.nikhilk.net/Silverlight-ViewModel-Pattern.aspx. I basically three types of classes:

1. Model – These will be the Data Transfer Objects (DTO) that will be defined in the RIA Services.

2. View – These are the Form and User Controls.

3. ViewModel – These are classes that define the binding and event logic and act as a mediator between the Model and View.

If you know anything about MVVM the basic goal is minimize the about of code-behind code in your Silverlight Form and User Control files. I think it a little ridiculous to try to remove all code, and in this solution I have very little, just three lines. There will be times where complex UIs will require code and that just cannot be avoided. However I would try to make sure that all logic that you would traditionally put in event handlers be moved to ModelView objects.

I think that is about as simple as you make it, now let’s get to some code.

Project Creation

Just create a Silverlight application and enable RIA Services when creating the project.

The Solution

The solution is pretty basic. As you will see there is the RIA Service project and the Silverlight project. Take note that I have created a folder called Model in the RIA Services project. This is where all the DTO objects should be placed. In the Silverlight project there is a ViewModel and View folders were respective files will be placed. This is mostly done for organization purposes.

Untitled

The Form

The form that has been created is very simple. It is actually made up of two user controls and a single form. The goal of this is to demonstrate how easy it is to get this wired up using the MVVM pattern with Silverlight and RIA Services; the goal is not make a full application. The user will be able to select an item in the grid which will populate the textboxes at the bottom. Pressing the save button will save all the employee changes back to the Custom Domain Service.

Untitled2

Employee DTO

Here is the Employee DTO object. It is real basic as the important thing as we will soon learn is how to get all this wired up.

namespace MVVM.Web.Model
{
public class EmployeeDTO
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }

public string FirstName { get; set; }

public string LastName { get; set; }

public DateTime HireDate { get; set; }

public int Title { get; set; }
}
}


Domain Service

The following is the Employee Domain Service which will return a list of employees and also support insert, update and delete operations. All you will need to do is put in code within these stubs to call whatever existing service you have to persist the data. For the query method, I just return a hard coded list of Employee DTO objects.

Note that RIA Domain Service handles all the plumbing of calling the correct method based on the state of the object. I was literally blown away with how little work I now need to do.

namespace MVVM.Web.Service
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;

using Model;

// TODO: Create methods containing your application logic.
[EnableClientAccess()]
public class EmployeeDomainService : DomainService
{

public List<EmployeeDTO> GetEmployees() {
List<EmployeeDTO> employees = new List<EmployeeDTO>();

EmployeeDTO employee = new EmployeeDTO();
employee.ID = 1;
employee.FirstName = "Jason";
employee.LastName = "Apergis";
employees.Add(employee);

employee = new EmployeeDTO();
employee.ID = 2;
employee.FirstName = "Ethan";
employee.LastName = "Apergis";
employees.Add(employee);

employee = new EmployeeDTO();
employee.ID = 3;
employee.FirstName = "Caroline";
employee.LastName = "Apergis";
employees.Add(employee);

return employees;
}

private static int count = 10;
public void InsertEmployeeDTO(EmployeeDTO employee) {
//Code to insert employee
employee.ID = count++;
}

public void UpdateEmployeeDTO(EmployeeDTO employee)
{
//Code to update employee
}

public void DeleteEmployeeDTO(EmployeeDTO employee)
{
//Code to delete employee
}
}
}


Employee View Model

The following is the employee view model there are several things going on.

  • In the constructor we create an instance of the Domain Service Context and then load up all the employees.
  • There is a property called SelectedEmployee which is used to keep track of what Employee is selected.
  • Next there is a property that returns a list of employees so that they can be bound to the grid.
  • There is a method to handle creation and deletion of employee objects.
  • There is a save method that called the Domain Service Context SubmitChanges method. This will push all the changes back to the RIA Services.
  • Finally there are methods used for return an object that implements the ICommand interface. They are used to wire up a click event of a button without having to put code into the code behinds.

using MVVM.Web;
using MVVM.Web.Model;
using MVVM.Web.Service;

namespace MVVM.ViewModel
{
public class EmployeeViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private EmployeeDomainContext _context = null;
private EmployeeDTO _selectedEmployee = null;

public EmployeeViewModel()
{
_context = new EmployeeDomainContext();
_context.Load(_context.GetEmployeesQuery());
}

public EmployeeDTO SelectedEmployee
{
get
{
return _selectedEmployee;
}
set
{
_selectedEmployee = value;
RaisePropertyChanged("SelectedEmployee");
}
}

public EntitySet<EmployeeDTO> Employees
{
get
{
return _context.EmployeeDTOs;
}
}

public ICommand SaveCommand
{
get
{
return new SaveCommand(this);
}
}

public void Save()
{
if (_context.HasChanges)
{
_context.SubmitChanges();
RaisePropertyChanged("Employees");
}
}

public ICommand InsertCommand
{
get
{
return new InsertCommand(this);
}
}

public void Insert() {
EmployeeDTO employee = new EmployeeDTO();
employee.ID = 0;

_context.EmployeeDTOs.Add(employee);
RaisePropertyChanged("Employees");
}

public ICommand DeleteCommand
{
get
{
return new DeleteCommand(this);
}
}

public void Delete() {
_context.EmployeeDTOs.Remove(SelectedEmployee);
RaisePropertyChanged("Employees");
}

private void RaisePropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
}
}

Save, Insert and Delete Commands

Here is the code of the class that implements the ICommand object to create a new employee object. This class has a reference to the View Model and will call the appropriate method.

namespace MVVM.ViewModel
{
public class DeleteCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private EmployeeViewModel _evm = null;

public DeleteCommand(EmployeeViewModel evm)
{
_evm = evm;
}

public bool CanExecute(object parameter)
{
return true;
}

public void Execute(object parameter)
{
_evm.Delete();
}
}
}

namespace MVVM.ViewModel
{
public class InsertCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private EmployeeViewModel _evm = null;

public InsertCommand(EmployeeViewModel evm)
{
_evm = evm;
}

public bool CanExecute(object parameter)
{
return true;
}

public void Execute(object parameter)
{
_evm.Insert();
}
}
}

namespace MVVM.ViewModel
{
public class SaveCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private EmployeeViewModel _evm = null;

public SaveCommand(EmployeeViewModel evm)
{
_evm = evm;
}

public bool CanExecute(object parameter)
{
return true;
}

public void Execute(object parameter)
{
_evm.Save();
}
}
}

Personally I think this is a little overboard, and if you just want to call the View Model method directly from an event handler in the code behind, that is really not that bad. There is really is very little code in the code behind at this point.

Employee Grid Control

Below is the xaml code for the Employee Grid Control that I created. Just take note of the ItemsSource and SelectedItem attributes and how they bind to the EmployeeViewModel properties. Also note that Command attribute on both of the buttons and how the bind to the command classes we created.

<UserControl x:Class="MVVM.View.EmployeeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="210" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid AutoGenerateColumns="True" Name="grdEmployees"
Height="168" VerticalAlignment="Top"
HorizontalAlignment="Left" Width="400"
ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee, Mode=TwoWay}"/>
<Button Content="Insert" Height="23" HorizontalAlignment="Left" Margin="232,176,0,0"
Name="btnInsert" VerticalAlignment="Top" Width="75"
Command="{Binding InsertCommand}"/>
<Button Content="Delete" Height="23" HorizontalAlignment="Left" Margin="313,176,0,0"
Name="btnDelete" VerticalAlignment="Top" Width="75"
Command="{Binding DeleteCommand}"/>
</Grid>
</UserControl>

Employee Detail Control

This is the second user control that will populate based on the select item in the grid in the other user control. The following is the xaml code for that control. Notice that the textboxes bind to the SelectedEmployee.* property of the EmployeeViewModel.

<UserControl x:Class="MVVM.View.UpdateView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="48" d:DesignWidth="347">

<Grid x:Name="LayoutRoot" Background="White">
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0"
Name="txtFirstName" VerticalAlignment="Top" Width="120"
Text="{Binding SelectedEmployee.FirstName, Mode=TwoWay}"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="138,12,0,0"
Name="txtLastName" VerticalAlignment="Top" Width="120"
Text="{Binding SelectedEmployee.LastName, Mode=TwoWay}"/>
<Button Content="Save" Height="23" HorizontalAlignment="Left"
Margin="264,12,0,0" Name="btnSave" VerticalAlignment="Top"
Width="75" Command="{Binding SaveCommand}"/>
</Grid>
</UserControl>

Form XAML

Next we have the xaml code for the main form that has the two user controls that we have created. All we had to do was add the xmlns:view="clr-namespace:MVVM.View" namespace.

<UserControl x:Class="MVVM.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:view="clr-namespace:MVVM.View"
mc:Ignorable="d"
d:DesignHeight="278" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="White">
<view:EmployeeView x:Name="vwEmployee" Height="207" VerticalAlignment="Top" HorizontalAlignment="Left" Width="400" />
<view:UpdateView x:Name="vwUpdate" Height="45" VerticalAlignment="Top" HorizontalAlignment="Left" Width="354" Margin="23,221,0,0" />
</Grid>
</UserControl>

Form Code Behind

Finally we have three lines of code to create the ViewModel object and then set it as the DataContext to both of the controls.

namespace MVVM
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();

EmployeeViewModel emv = new EmployeeViewModel();
vwEmployee.DataContext = emv;
vwUpdate.DataContext = emv;
}
}
}

Summary

That is about the quickest introduction I can provide to using MVVM with RIA Services and a Custom Domain Service.

Thursday, March 25, 2010

Silverlight 4 Hands On Lab

Silverlight Data and Controls Series

Hands On Lab for Silverlight 4

After the Mix 2010 Conference there has been a lot more excitement for Silverlight 4 and RIA Services. If you want to start to learn Silverlight 4 and RIA Services this is what I would recommend. First go the Silverlight website and review some of the learning materials there - there is a ton of good stuff there (http://www.silverlight.net/learn/).

However most of us require a more hands to actually learn something. I recently found that the Swiss MSDN Team published a very detailed Silverlight 4 Hands On lab here - http://blogs.msdn.com/swiss_dpe_team/archive/2010/01/19/hands-on-lab-silverlight-4-line-of-business-application-using-wcf-ria-services.aspx.

This lab is absolutely great in that is over 100 pages of step by step labs that taught me the following:

  • How to create a Silverlight and RIA Service Project.
  • Created a simple Entity Model and wired up a grid.
  • Incorporated both Server and Client side filtering of data in the grid with RIA Services and LINQ.
  • Added a Busy Indicator and DataPager controls to the grid.
  • Added custom formatting and sorting to grid columns.
  • Added an AutoComplete control to filter the grid.
  • Added persistence to the grid to perform CRUD operations. Plus added concurrency checking to the grid.
  • Created a data form for master and detail data to support for CRUD operations
  • Implemented server side validation attributes to domain classes and created custom validation classes.
  • Added custom lookups into the data form.
  • Created a custom Data Transfer Object (DTO).
  • Created custom domain methods.

There were several other labs which I did not complete on:

  • New printing capabilities of Silverlight 4.
  • Add right click menu items
  • Using the clip board.
  • Using COM Interop to send data to Excel.

Creating Lab Environment

I really found the labs a great way to learn Silverlight 4.

This lab was built for Silverlight 4 with Visual Studio Beta 2. As you know with the announcements at the Mix Conference, you can now use Silverlight 4 with Visual Studio 2010 RC. Plus a new RIA Service RC was also released. To get started doing this lab do the following:

Modifications to Lab

Since this lab was published there have been some changes and not all things worked. There were a few small things that you will have to differently to complete the lab. This is because RIA Services RC release changed up several things.

Page 21 – Use System.ServiceModel.DomainServices.Client instead of System.Windows.Ria. Reference - http://code.msdn.microsoft.com/RiaServices/Release/ProjectReleases.aspx?ReleaseId=3570

Page 41 – Use the following for Code Snippet 7 instead of what is provided:

<riaControls:DomainDataSource.QueryParameters>

<riaControls:Parameter ParameterName="city" Value="{Binding ElementName=shipCityBox, Path=Text}" />

</riaControls:DomainDataSource.QueryParameters>

References

Page 45 - Data Conflict Detection – This section was seems to be out of place, skip it and complete later.

Page 63 - Snippet 12 – Need to modify the code slightly.

return new ValidationResult("Maximum freight cost for UK exceeded.", new string[] { "Freight" });

Saturday, March 20, 2010

RIA Services RC Changes for Visual Studio 2010 RC

There were a lot of announcements that came out of the MIX 2010 Conference around Silverlight 4. One was that RIA Services now has a Release Candidate. I have a project that will be utilizing Silverlight 4 with RIA services. I had some functional design stuff that I had built with Silverlight 4 and RIA services with Visual Studio 2010 Beta 2 because until the MIX conference that was the only way.

Well a couple things stopped working when I tried to go to Visual Studio 2010 RC with new release of Silverlight 4 that works with VS 2010 RC and RIA Services RC.

I found this reference that described the changes for the RIA Release Candidate that you need to be aware of. Please go here and read through the changes - http://code.msdn.microsoft.com/RiaServices/Release/ProjectReleases.aspx?ReleaseId=3570

Some important things you should be aware of:

  • Several of the assemblies changed names.
  • There was a bunch of refactoring and classes have moved between different libraries.
  • Working with the ValidationException class has changed up a little and will require some modifications.
  • There were several changes to the DomainDataSource which broke with my old code.
  • There is a conversion wizard upgrade RIA service built in VS 2008 to VS 2010.
  • And several more…

SharePoint 2010 Logical Architecture

Introduction

In the previous blog in this series I introduced the new service architecture of SharePoint 2010. There are several changes and understanding these changes will drive both the logical and physical topologies of SharePoint 2010. When I refer to the logical topology, this means the logical organization of SharePoint services that will be used to satisfy the business needs. The physical topology is the installation and configuration of those services. The goal of both topologies it to provide a solution that is:

  1. Scalable
  2. Secure
  3. Maintainable
  4. Has high performance
  5. Fault tolerant

In this blog I will take these themes and apply them to various way in which SharePoint 2010 can be configured to support the logical topology. I will go into the physical topology in the next part.

SharePoint 2010 Changes

The biggest change in SharePoint 2010 is the flexibility you now have with the configuration and re-use of services. In the initial blog about services, I identified several rules that you should know which will directly apply to how the logical architecture of SharePoint 2010 would be done. Here they are again:

  1. Multiple instances of the same Service Applications can be initiated within a farm.
  2. Service Applications are shared across Web Applications within a farm by Service Group.
  3. Some Service Applications can be shared across multiple farms while others cannot.
  4. Service Groups can logically contain Service Applications that reside in other farms.
  5. Web Applications have the flexibility to use multiple instances of the same type of Service Application (regardless of which farm hosts that service).
  6. Service Applications can have its data partitioned and only accessible to specific subscribers.
  7. Service Groups can be used to logically scale for performance, security and scalability.

In the following sections I have indentified some common scenarios for configuration of SharePoint 2010 logical topology based on these rules. It will not be possible in this blog to go over every permutation these rules.

As well best practices for the configuration of these services is still not very well known other than the information is being provided to us by Microsoft and our experiences with SharePoint 2007. However you should be able to take these examples and use them as a way to start doing some early planning for implementation SharePoint 2010.

Side Note – If you are familiar with some of the logical topology diagrams that are provided by Microsoft they usually include Application Pools in those diagrams. I have decided to exclude that from these diagrams to make understanding the logical topology simple. Albeit understanding how the Applications Pools are incorporated into the topology is extremely important (security and fault tolerance). Usually when architecting a SharePoint topology I start with understanding how I want to organize service to achieve the best possible performance, security and redundancy. Once I have that organized, I will then use Application Pools to help attain those tenants.

Single Farm Single Service Group

This first topology depicts a standard SharePoint farm that has a single service group that is shared across all types of sites. Any time new sites are added within this farm, they will have access to all of the services that are available in the Default Application Group.

Untitled 1

Advantages:

  • Most simple architecture to implement.
  • All web applications added into the farm have immediate access to all the available services.
  • All services are grouped together and can be managed centrally.

Disadvantages:

  • Does not allow for isolation of data used within the services. This means that all websites have access to each other’s data.
  • Cannot create dedicate services for specific web applications.
  • Will not scale well because publishing and collaboration would be occurring in the same farm.

Recommendations:

  • This is an initial place to start as your configuration for SharePoint 2010. It can always be scaled or reconfigured to support more demand.
  • It would be recommended to make sure that each service that can be partitioned be made partitioned. Service partition is introduced in the first part of this series. This is important because it not possible to partition a service after has been created. If services are partitioned from the beginning you have the ability to create some isolation of data between site collections.
  • These sites could all be installed on a single web application but that is not recommended. It is better to created dedication Web Applications for each running within their own application pool. This provides long-term scalability.

Single Farm Multiple Service Groups

This second example is a reconfiguration of the previous farm; the difference is that multiple Application groups have been created. There is a Default Application Group which has some of the core Service Applications that can be used by all the sites. Some sites, however, will have dedicated services. You will want to have dedicated services for scalability, performance and security reasons and this will be covered later on from a physical perspective.

Untitled 2

Observations:

  • First thing that is interesting about this configuration is you see the Managed Metadata Service is configured into each application group. One interesting thing we will see with SharePoint 2010 is better ability to manage the Information Architecture, a key ingredient for Governance. We now have the ability to create reusable Information Architectures across sites and farms as well as created dedicated ones.
  • Second you will notice that Search has been centralized as part of the default application group and has been made a resource to all the sites. This is because it is a resource intensive process and it is not likely you will be creating dedicated search farms for a specific site. Note that FAST search cannot be used in this manner. FAST can either be used behind the firewall or outside but the same servers cannot be used for both.
  • Third application services have been distributed and dedicated. For instance Excel Services, BCS, Access Service, etc. are broken out.

Advantages:

  • Supports the ability for multiple goals to be met in the same farm.
  • Provides the ability to have better isolation of services. This provides the ability to have a granular implementation that better support scalability, performance and security.
  • Allows for divisional, department, or program administration.

Disadvantages:

  • Complex configuration to manage.
  • Requires more Governance and administration.
  • Will require more hardware/virtual resources to host.

Recommendations:

  • This configuration is good for a small SharePoint implementation within a department, division or business unit.

Multi-Farm

In this farm configuration we have broken it out and created several independent SharePoint farms that still use central services farm.

Untitled 3

Observations:

  • Sites have been separated into dedicated farms based on usage such as public internet, extranet, internet and intranet.
  • Each farm has dedicated application groups of services that specific to their use.
  • All the farms have access to shared services in central services farm.

Advantages:

  • There is a clear separation between the sites by placing them in dedicated farms. This is very important because publishing farms and collaboration farms have been separated.
  • Organizations can be segregated from each other and prevents the initiatives of one from interfering with the initiatives of another.
  • Farms can be configured with service groups that are specific to their needs.
  • Central services can still be shared across farms where it makes sense.

Disadvantages:

  • A more complex configuration that requires more considerable time to administer and govern.

Recommendations:

  • This is an enterprise farm configuration.
  • This configuration should be implemented when a company has the need to optimize administration and hosting at the enterprise level.
  • This configuration is needed when data and services need to be isolated but there is still opportunity for reuse.

Multi-Farm with Application Service Partitioning

This farm configuration introduces service partitioning into the pervious diagram. Service partition is introduced in the first part of this series. It is recommended that all services be partitioned when they are initially set up, even if one partitioned is only used. It is not possible to change a service to allow for partitioning after it has been started in an un-partitioned state.

Untitled 4

Observations:

  • In this diagram you will see in Farm A both the Managed Metadata and Business Connectivity Services have been partitioned. Others could have but just selected these for discussion. Now Farms B through E and utilize partitions that are just for them and they will have isolation.
  • Notice in Farm D we were able to remove those services from the configuration. This makes the management of that farm less complex.
  • Notice in Farm E we kept the Managed Metadata service. There is nothing from stopping that and can still be considered completely legitimate. For instance the administrator in Farm C does not have access to Farm A, and all they are using from Farm A is a partition of Managed Metadata that all the farms are using. If that is the case, they may want to still have a Managed Metadata service in Farm C so that they can make tactical additions to the Information Architecture.

Advantages:

  • Provides even more granularity and isolation of data management within SharePoint 2010.

Disadvantages:

  • Again an even more complex scenario for managing services for an enterprise farm.

Recommendations:

  • Using service partitioning must be a strategy considered when setting up an enterprise SharePoint 2010 farm.

Hosted Partitioned Farm

The last farm configuration I will introduce to you is the concept of a completely hosted farm. This is a very interesting scenario because you were not empowered to do this very well with SharePoint 2007. Some SharePoint service companies provided SharePoint hosted solutions but given what was available in SharePoint 2007. Now with SharePoint 2010 and Microsoft Azure those limitations have been removed.

Untitled 5

Observations:

  • This scenario should not just be considered for providing SharePoint 2010 hosted solutions by a services company. A company itself can consider creating a solution like this for creating on demand solutions for provisioning full farms in a rapid fashion.
  • This scenario would also make sense for a parent company that has many subsidiary companies that they want to service in an economical fashion.
  • As you will notice this approach heavily uses service partitioning. The goal is to make the farms as lightweight as possible.

Advantages:

  • Utilizes many of the enterprise best practices discussed so far.
  • Since partitioning is utilized, there is isolation of data between farms.
  • Governance and administration is centralized into a single location and is not spread across farms.

Disadvantages:

  • Because all services are centralized, it is not possible to create customizations at the farm level. This means that if a farm has a special functional, performance or security requirement it may be hard to support that requirement.

Recommendations:

  • If you are creating a hosted solution and the expectations can be set ahead of time this is a good approach. However this is very hard to do and more often than not there will be requirements that will require dedicated services within the farms. It is ok to set out with this in mind but you must remain flexible to support an architecture like the one in the previous section.

What’s Next

In the next part of this series we will continue this line of discussion but for the physical topology of SharePoint 2010. We will focus on what is a small, medium and large farms.

References

Wednesday, February 24, 2010

K2 User March 2010 User Group – K2 blackpearl 4.5

There is a K2 User Group meeting Tuesday, March 9, 11am-1pm central time. In this meeting we will be going over the new release of K2 blackpearl 4.5! If you are a K2 professional or you use K2 at your company I highly recommend that you attend to learn about it and use this as a forum to ask questions. This is a community driven event and is not a sales event.

------------------------------------------------------

Hello Everyone,

Phillip Knight from Merit Energy will be hosting the K2 user group meetings at Merit Energy, located at 13727 Noel Road, 2nd Floor Conference room, Tower 2, Dallas , Texas 75240. Parking information is included in the linked map below. Remote attendance information is included at the bottom of this message.

Link to map: http://www.meritenergy.com/content/MeritMap.pdf. Reminder: Merit Energy is on the 5th floor, but the meeting will be held in the 2nd floor conference room. Once off the elevator, go to the reception area and we will bring you back to the conference room.

Please RSVP to me via email whether you are attending via live meeting or if you will be attending in person (so that we can plan for the number of people to order food for).

Check out the K2 Underground site and our user group at http://www.k2underground.com/groups/greater_texas_user_group/default.aspx. We are posting webexes/live meetings from our meetings at this site.

4/13/2010 11am – 1pm
5/11/2010 11am – 1pm
6/8/2010 11am – 1pm
7/13/2010 11am – 1pm
8/10/2010 11am – 1pm

Meeting Agenda:

11-11:15 Networking/Refreshments
11:15-11:30 Announcements/Intros of New people
11:30-11:45 Tips & Tricks
11:45-12:45 Technical Presentation
12:45-1:00 Meeting Wrapup

The Announcements section of the meeting will include any information regarding K2 upcoming events and user group events as well as brief introductions of our presenter and refreshment provider.

The Tips & Tricks Presentation is when we as members can pose questions to each other on projects that we are working on and having difficulty with. It is also a time when if we have learned something that we feel will be helpful to others, we can share it with the group. Bring yours to share/ask.

Meeting Presentation and Sponsoring Company:

We thank K2 for providing a presenter for our March meeting to show K2 BlackPearl release 4.5's new features.

The K2 platform is for delivering process-driven applications that improve business efficiency. Visual tools make it easy for anyone to assemble reusable objects into applications that use workflow and line-of-business information.

K2-based solutions are deployed by a growing number of the global Fortune 100. K2 is a division of SourceCode Technology Holdings, Inc. based in Redmond, Washington, and has offices all over the world

For more information, contact Joe Bocardo at joeb@k2.com.

Meeting Presenter:

Our meeting presenter information will be available in the next meeting announcement.

For Virtual Attendees:

Note: please keep your phone on mute until you are ready to speak.

Audio Information

Telephone conferencing

Choose one of the following:

  • Start Live Meeting client, and then in Voice & Video pane under Join Audio options, click Call Me. The conferencing service will call you at the number you specify. (Recommended)

Use the information below to connect:

Toll: +1 (719) 867-1571
Participant code: 914421
First Time Users: To save time before the meeting, check your system to make sure it is ready to use Microsoft Office Live Meeting.

Copy and paste the required information:

Location: https://www.livemeeting.com/cc/scna
Meeting ID: 34PCR2
Entry Code: s53+9^P

Troubleshooting

Unable to join the meeting? Follow these steps:

Copy this address and paste it into your web browser: https://www.livemeeting.com/cc/scna/join?id=CH5CQQ&role=attend&pw=k3T@z(h

If you would like to provide refreshments at an upcoming meeting or present at an upcoming meeting, please contact me.

Let me know if you have any questions prior to the meeting.

The next meeting announcement will be sent out next Tuesday.

Thanks,

Have a great day!