Showing posts with label silverlight. Show all posts
Showing posts with label silverlight. Show all posts

Wednesday, May 4, 2011

How to Handle the WCF Error on Silverlight

By David Espino.

When sending a call to a WCF service from a Silverlight application and the service returns an error, the Silverlight client backs up only a very generic error:

The remote server returns an error: NotFound

This message does not help to know the exact error neither for the user nor the developer. Even more, it is necessary for more security to make a division of the error information: one message for the user and one detailed message for the system administrator or developer.

The first part of the error dedicated to the application administrator consists on writing the exception that the code generates on a detailed way. There are a lot of tools that allows tomake this log of errors (Log for Net, Microsoft Enterprise Library and some other). For this example, we need to provide an easy log in to the server application log where our service would be found. This method captures two parameters: One “Entity” that indicates the business entity where the error occurred (for example the Countru, State, Employee, etc) and an action, which indicates the operation that could not be performed (Save, Select, Delete, etc). With these two parameters we can deliver a friendly message to be sent to the user.

public class ExceptionLogger

{

public static string LogAndReturnError(Exception ex, string entidad, string accion)

{

StringBuilder cadenaError = new StringBuilder();

cadenaError.Append("Ocurrió un error en el servicio de la aplicación: \n\n");

cadenaError.Append("Error: ");

cadenaError.Append(ex.Message);

cadenaError.Append("\n\nStackTrace: \n\n");

cadenaError.Append(ex.StackTrace);

EventLog.WriteEntry("NombreAplicación", cadenaError.ToString(), EventLogEntryType.Error);

cadenaError = new StringBuilder();

cadenaError.Append(string.Format("Ocurrió un error al intentar {0} {1}. Contacte al administrador del sistema.", accion, entidad));

return cadenaError.ToString();

}

}

The next step would be to generate an entity as a DataContract for the object that stores errors that occur during the service.

[DataContract]

public class ServiceError

{

[DataMember]

public string Type { get; set; }

[DataMember]

public string Message { get; set; }

}

The next thing to do is to make our service operations to return us an error object into the interface layer. We will use an output parameter in our methods of service and use the kind of login error.

public void DoWork(string Parameter1, out ServiceError errorService)

{

errorService = null;

try

{

//Operaciones a realizar

}

catch (Exception ex)

{

errorService = new ServiceError

{

Message = ExceptionLogger.LogAndReturnError(ex, "Acción", "Entidad"),

Type = "Aplicación"

};

}

Finally we need a Silverlight application to consume our service. For this, we need to add a Silverlight application and then a reference of our service. Once we call our service we are going to get the following:

MyServiceClient client = new MyServiceClient ();

client.DoWorkCompleted += new EventHandler<DoWorkCompletedEventArgs>(client_DoWorkCompleted);

client.DoWorkAsync("Hola Mundo");

void client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)

{

//Error propio de WCF

if (e.Error == null)

{

//Nuestro Parámetro de Error!!!

if (e.errorService == null)

{

if (e.Result != null)

{

//Manipular resultado

}

}

}

}

This is a very easy way to implement error management on Silverlight.

Synchronous and Asynchronous Validations using the Silverlight 4.0 and the MVVM pattern

By David Espino.

In every application that we develop, it is always an excellent practice to validate the entry data of an opt-in form. In common web applications, Visual Studio provides several mechanisms and controls to perform such validations. In the case of Silverlight 4.0 we can find some existing interfaces that allow us to do such validations when developing applications using the MVVM pattern.

Introduction to the MVVM pattern.

This design pattern allows applications to divide them into separate layers and provides additional benefits such as implicit validation, ease of including unit tests and an increase in the reuse of components.

The pattern defines three main parts, the Model, the View and the View-Model. The image below briefly explains the objectives of each of the layers.

1

For further information of this pattern visit the following link:

http://weblogs.asp.net/dwahlin/archive/2009/12/08/getting-started-with-the-mvvm-pattern-in-silverlight-applications.aspx

Synchronous and Asynchronous Validations

There are validations that can take place when the user captures the information (or fails to capture) in length, structure or valid characters of the information captured. These validations can be done synchronously. However, when trying to validate whether data is duplicated in the database, it is necessary Silverlight to link to a repository of data through a service handler or WebClient. These validations are executed after the user captures the information and after running the service function asynchronously.

Procedure: To implement synchronous validations we are going to use IdataErrorInfo interface and INotifyDataErrorInfo for asynchronous validations.

This will require several elements:

1. ValidationHandler class will be working with notifications when an error occurs. This class contains a dictionary called BrokenRules which will contain the errors that are occurring in the model.

public class ValidationHandler

{

public Dictionary<string, string> BrokenRules { get; set; }

public event EventHandler IsValidated;

public void OnIsValidatedChanged()

{

if (IsValidated != null)

{

this.IsValidated(this, new EventArgs());

}

}

public ValidationHandler()

{

BrokenRules = new Dictionary<string, string>();

}

public string this[string property]

{

get

{

return this.BrokenRules[property];

}

}

public bool BrokenRuleExists(string property)

{

return BrokenRules.ContainsKey(property);

}

public bool ValidateRule(string property, string message, Func<bool> ruleCheck)

{

if (!ruleCheck())

{

if (!BrokenRuleExists(property))

{

this.BrokenRules.Add(property, message);

}

else

{

this.BrokenRules[property] = message;

}

this.OnIsValidatedChanged();

return false;

}

else

{

RemoveBrokenRule(property);

this.OnIsValidatedChanged();

return true;

}

}

public void RemoveBrokenRule(string property)

{

if (this.BrokenRules.ContainsKey(property))

{

this.BrokenRules.Remove(property);

}

}

}

A base class for View-Model of the application that implements the INotifyPropertyChanged Interface

public class ViewModelBase : INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

protected void RaisePropertyChanged(string propertyName)

{

PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)

{

handler(this, new PropertyChangedEventArgs(propertyName));

}

}

}

A service that will do an Asynchronous validation of an object called "Person" whose business logic would be on finding a data repository if the captured Id is duplicated or not. For purposes of this example, this logic is omitted. Do not forget the policy files required by Silverlight to consume the service.

public class PersonServiceObject : IPersonService

{

public string GetData(int value)

{

return string.Format("You entered: {0}", value);

}

public bool GetDataUsingDataContract(PersonType composite)

{

return false;

}

}

[ServiceContract]

public interface IPersonService

{

[OperationContract]

string GetData(int value);

[OperationContract]

bool GetDataUsingDataContract(PersonType composite);

// TODO: Add your service operations here

}

[DataContract]

public class PersonType

{

int id = 0;

string nombre = "Hello ";

[DataMember]

public int Id

{

get { return id; }

set { id = value; }

}

[DataMember]

public string Nombre

{

get { return nombre; }

set { nombre = value; }

}

}

Next, we need to add a referral to the service in our Silverlight application to use the service. We do it from "Add Service reference" which is in the Project Explorer.

2

Now, with the reference of the service we can create our model to be validated.

public class PersonViewModel: ViewModelBase, IDataErrorInfo, INotifyDataErrorInfo

{

private string nombre;

private int id;

private ValidationHandler validator = new ValidationHandler();

private PersonService.PersonServiceClient personProxy = new PersonService.PersonServiceClient();

public int Id

{

get { return id; }

set {

id = value;

RaisePropertyChanged("Id");

bool valid = validator.ValidateRule("Id", "Id del cliente no válido ", () => (id > 100));

personProxy.GetDataUsingDataContractAsync(new PersonService.PersonType { Id = value, Nombre = this.Nombre });

}

}

public string Nombre

{

get { return nombre; }

set {

nombre = value;

RaisePropertyChanged("Nombre");

}

}

public PersonViewModel()

{

if (!DesignerProperties.IsInDesignTool)

{

this.personProxy.GetDataUsingDataContractCompleted += new EventHandlerGetDataUsingDataContractCompletedEventArgs>(personProxy_GetDataUsingDataContractCompleted);

}

}

void personProxy_GetDataUsingDataContractCompleted(object sender, PersonService.GetDataUsingDataContractCompletedEventArgs e)

{

if (e.Error == null)

{

bool valid = validator.ValidateRule("Id", "Id del cliente duplicado. ", () => (e.Result == true));

OnErrorsChanged("Id");

}

else

{

MessageBox.Show("No fue posible obtener la validación en el servicio.");

}

}

#region IDataErrorInfo Members

public string Error

{

get { return null; }

}

public string this[string columnName]

{

get

{

if (this.validator.BrokenRuleExists(columnName))

{

return this.validator[columnName];

}

return null;

}

}

#endregion

#region INotifyDataErrorInfo Members

public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

public System.Collections.IEnumerable GetErrors(string propertyName)

{

IEnumerable<string> errors = null;

if (propertyName == null)

{

errors = validator.BrokenRules.Select(dict => dict.Value);

}

else

{

if (validator.BrokenRuleExists(propertyName))

{

errors = validator.BrokenRules.Select(dict => dict.Value);

}

}

return errors;

}

public bool HasErrors

{

get { return validator.BrokenRules.Keys.Count > 0; }

}

protected void OnErrorsChanged(string propertyName)

{

if (ErrorsChanged != null)

ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));

}

#endregion

}

The line bool valid = validator.ValidateRule("Id", "Id del cliente no válido ", () => (id > 100));

Will cause when you assign the property, it is going to be confirmed that the data captured is greater than 100, otherwise an error will add to the dictionary of errors. At the end a call is going to be made to the service for validating asynchronously using the method:

personProxy.GetDataUsingDataContractAsync(new PersonService.PersonType { Id = value, Nombre = this.Nombre });

Upon returning from the service, the validation will be done again with the result of the service and invokes a method that fires the event ErrorsChanged, typical of INotifyDataErrorInfo interface to notify whether or not there was an asynchronous error.

Once you have the model we must now add this model as a model for a hearing to test our validation:

<UserControl

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:i="http://schemas.microsoft.com/expression/2010/interactivity"

xmlns:ic="http://schemas.microsoft.com/expression/2010/interactions"

xmlns:local="clr-namespace:AplicacionPrueba"

mc:Ignorable="d"

x:Class="AplicacionPrueba.MainView"

d:DesignWidth="640" d:DesignHeight="480">

<UserControl.Resources>

<local:PersonViewModel x:Key="MainViewModelDataSource" />

UserControl.Resources>

<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MainViewModelDataSource}}" d:DataContext="{d:DesignData /SampleData/MainViewModelSampleData.xaml}">

<StackPanel>

<TextBlock Text="ID:">TextBlock>

<TextBox x:Name="txtNombre" Text="{Binding Path=Id, Mode=TwoWay, ValidatesOnDataErrors=True}" >TextBox>

StackPanel>

Grid>

UserControl>

At that view we will add our View-Model as a resource to be used, in addition to make a Bind to the control in which we are going to capture the Id for the change of ownership. If we try to be less than 100, the control will display an error:

3

If we try to proof a value greater than 100, it will trigger the asynchronous validation, and after a while you will receive an error of duplicity. In this example it will always mark the error because the method of service is not doing the validation, but in fact is always returning false as the value of validation.

4