José F. Romaniello

Las aventuras y desventuras de un codificador.

Part I: Introducing NHiberate and WPF: The ChinookMediaManager
Part II: Nhibernate and WPF: The core
Part III: Nhibernate and WPF: Models concept
Part IV: Nhibernate and WPF: ViewModels and Views

In this post I will show you an easy way to handle validations in WPF.

In System.ComponentModel we have an interesting interface named IDataErrorInfo.
This interface has two members:

  • IDataErrorInfo.Item: Gets the error message for the property with the given name.
  • IDataErrorInfo.Error: Gets an error message indicating what is wrong with this object.

The interesting side of this interface is that if we implement this in our domain classes, the presentation layer will automatically resolved certain things.

Side note: This interface was made with DataTables in mind.

Validation Framework

I really like NHibernateValidator, in the same way that I like NHibernate. But, in uNhAddIns we have a main principle: “those two are just options”, so you can change whenever you want to use anything else (Validation Application Block, Validation Framework, Castle Validators, xVal).

For that matter, Fabio Maulo developed a simple interface named IEntityValidator, this is part now of the uNhAddIns.Adapters project. If you want to use Nhibernate Validator the EntityValidator is already implemented here. If you want to use another validator framework please implement the interface and send us the patch ;-).

How To

Implementing IDataErrorInfo directly in domain classes is a waste of time and also is too much invasive, because will end with a dependency in the IEntityValidator. Since we already know how to build injectable behavior we can address this problem in the same way.

I build a new DynamicProxy IInterceptor you could see the implementation here.

The configuration of the Album entity, now looks as follows:

container.Register(Component.For<Album>()
                                    .NhibernateEntity()
                                    .AddDataErrorInfoBehavior()
                                    .AddNotificableBehavior()
                                    .LifeStyle.Transient);

 

The configuration of Nhibernate.Validator is very easy and I don’t want to repeat myself.
Then you need to register an IEntityValidator as follows:

container.Register(Component.For<IEntityValidator>()
                            .ImplementedBy<EntityValidator>());

You have three way to write validations with NHibernate Validator: Xml, Fluent and Attributes. This is aleady very well explained in this post.

In the VIEW the only thing that we need to enable is the ValidateOnDataErrors attribute, this is an example textbox:

<TextBox Text="{Binding Path=Album.Title, ValidatesOnDataErrors=true}" />

I’ve a shared a resource for views, as follows:

<Style TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip"
            Value="{Binding RelativeSource={RelativeSource Self}, 
                   Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>
This means: “textbox should display validation errors in tooltip”.

This is all. What?, you don’t believe me.

See the behavior in action

Oh, and I almost forgot, this behavior should also work in ASP.NET Mvc.

| More

I wanted to briefly mention something in case you haven’t noticed. In my last post I show you a working application, and I didn’t mention anything about NHibernate.

Where are our models and repositories?

Here:

image  image

The repository definition is here.

Do you see the complex session management?
Do you see the complex transactions management?
Do you see the overhead?
Do you see the custom implementation of INotifyPropertyChanged, ICollectionChanged and so on?

This is the power of CpBT and other tools in unhaddins.

Sorry, this is not a joke,

| More

In this post I will introduce some concepts about the presentation layer of the Chinook Media Manager example.

Prerequisites

If you are new to the MVVM pattern I will strongly recommend you these blogs post:

Introduction

I will quote Josh Smith:

The single most important aspect of WPF that makes MVVM a great pattern to use is the data binding infrastructure. By binding properties of a view to a ViewModel, you get loose coupling between the two and entirely remove the need for writing code in a ViewModel that directly updates a view.

This is the more important thing that you need to remember. Unlike the MVP pattern the viewmodel never updates the UI, the ui is automatically updated by the databinding infrastructure. Also you has to keep in mind that all in the MVVM is about databinding, even events.

I chose to separate Views and ViewModels in differents assembly, although I saw these two together in a bunch of samples. For the other hand the interfaces of the ViewModels are defined in the Views assembly.

The “Album Manager” use case

If we recall my first post the UI was this:

albums

I will separate this use case in two views and viewmodels:

albumsred

The main View is called AlbumManagerView and the view inside the red border is called “EditAlbumView”. The reason for why I’m doing this is clear: “I need somehow to separate the problem into smaller parts”. And as I say in the “Models concept” post a use-case can expand multiples views.

To complicate things a bit, the user told us that would like to be able to simultaneously edit multiple albums. So, I will use the workspace sample of Josh Smith.

The AlbumManager

When I start to define a View, the first thing is the ViewModel interface, in this case:

public interface IAlbumManagerViewModel : INotifyPropertyChanged
{
    /// <summary>
    /// Setup the view, load the albums collection.
    /// </summary>
    /// <param name="artist"></param>
    void SetUp(Artist artist);

    /// <summary>
    /// Expose a bindable collection of albums.
    /// </summary>
    IEnumerable<Album> Albums { get; }

    /// <summary>
    /// Get or Set the selected album.
    /// </summary>
    Album SelectedAlbum { get; set; }

    /// <summary>
    /// Open an edition workspace for editing the selected album.
    /// </summary>
    ICommand EditSelectedAlbumCommand { get; }

    /// <summary>
    /// Commit all the changes.
    /// </summary>
    ICommand SaveAllCommand { get; }

    /// <summary>
    /// Discard all the changes.
    /// </summary>
    ICommand CancelAllCommand { get; }

    /// <summary>
    /// WorkSpace open.
    /// </summary>
    ObservableCollection<IEditAlbumViewModel> AlbumEditWorkspaces { get; }
}

In the introduction I said that MVVM use databinding even for events .
So, what is an ICommand?  ICommand interface has three members: CanExecuteChanged, CanExecute and Execute. The object that is bound to this command (aka command source), disable itself if the command cann’t be executed. You can bind KeyGestures, MouseActions, buttons and so on. More on this topic here.

The second step is to start writing a test for the concrete implementation of the viewmodel. 

[TestFixture]
public class AlbumManagerViewModelTest
{
    [Test]
    public void setup_viewmodel_should_work()
    {
        var albumManagerModel = new Mock<IAlbumManagerModel>();

        var artist = new Artist { Name = "John" };
        var albumList = new List<Album> { new Album() { Artist = artist } };

        albumManagerModel.Setup(am => am.GetAlbumsByArtist(artist))
                         .Returns(albumList)
                         .AtMostOnce();


        var albumManagerVm = new AlbumManagerViewModel(albumManagerModel.Object, 
                                                       viewInsantiator.Object);

        var eventWasRaised = false;

        albumManagerVm.PropertyChanged +=
            (sender, args) =>
            {
                //property changed should be raised AFTER the property change.
                if ("Albums".Equals(args.PropertyName))
                {
                    albumManagerVm.Albums.Should().Be.SameInstanceAs(albumList);
                    eventWasRaised = true;    
                }
            };

        albumManagerVm.SetUp(artist);
        eventWasRaised.Should().Be.True();
        albumManagerModel.VerifyAll();

    }

When I setup an “AlbumManagerViewModel”, it should call GetAlbumByArtist of my model, and put the result in the “Albums” property. Also the viewmodel should raise the PropertyChanged for the album property, AFTER the change in the property.

The implementation is very simple:

public void SetUp(Artist artist)
{
    Albums = _albumManagerModel.GetAlbumsByArtist(artist);
}
private IEnumerable<Album> _albums;
public IEnumerable<Album> Albums
{
    get { return _albums; }
    private set
    {
        _albums = value;
        OnPropertyChanged("Albums");
    }
}

To not getting bored with the code the full implementation of the EditSelectedAlbumCommand is here.

I will highlight interesting parts of this code:

public ICommand EditSelectedAlbumCommand
{
    get
    {
        if (_editSelectedAlbumCommand == null)
            _editSelectedAlbumCommand = new RelayCommand(
                o => EditSelectedAlbum(),
                o => SelectedAlbum != null &&!AlbumEditWorkspaces.Any(ae => ae.Album == SelectedAlbum));
        return _editSelectedAlbumCommand;
    }
}

This is the EditSelectedAlbumCommand. In the instantiation of the command you see two lambdas. The first function is the code that should be called when the commandsource call Execute. The second is the CanExecute function, as you can see it returns false when:

  • There isn’t a selected album.
  • There is another workspace editing the album.

You can find the RelayCommand in the source.

The EditSelectedAlbum method looks as follows:

private void EditSelectedAlbum()
{
    //Resolve a new instance of EditAlbumViewModel
    var newWp = _viewFactory.ResolveViewModel<IEditAlbumViewModel>();
    //Setup the new viewmodel.
    newWp.SetUp(SelectedAlbum, _albumManagerModel);
    //subscribe to close request.
    newWp.RequestClose += EditAlbumRequestClose;
    //add the new viewmodel to the workspace collection.
    AlbumEditWorkspaces.Add(newWp);
    //set the new viewmodel as the active wp.
    SetActiveWorkspace(newWp);
}
Now, the view for this viewmodel is very easy:
<StackPanel>
    <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" >
        <Button Command="{Binding EditSelectedAlbumCommand}" >Edit</Button>
        <Button>Close</Button>
    </StackPanel>
    <ListBox Name="AlbumsList" 
         ItemsSource="{Binding Albums}" 
         DisplayMemberPath="Title"
         SelectedItem="{Binding SelectedAlbum}"
         DockPanel.Dock="Top" Height="302">
    </ListBox>
    <Button Command="{Binding SaveAllCommand}" >Save All</Button>
    <Button Command="{Binding CancelAllCommand}" >Cancel All</Button>
</StackPanel>

The “tabs” container is described in this peace of code:

<Border Style="{StaticResource MainBorderStyle}">
    <HeaderedContentControl 
      Content="{Binding Path=AlbumEditWorkspaces}"
      ContentTemplate="{StaticResource WorkspacesTemplate}"
      Header="Workspaces"
      Style="{StaticResource MainHCCStyle}"
      />
</Border>

The EditAlbum

I define the interface for the IEditAlbumViewModel as follows:

public interface IEditAlbumViewModel : INotifyPropertyChanged
{
    void SetUp(Album album, IAlbumManagerModel albumManagerModel);

    /// <summary>
    /// The album being edited.
    /// </summary>
    Album Album { get; }
   
    /// <summary>
    /// The title of the tab.
    /// </summary>
    string DisplayName { get; }

    /// <summary>
    /// The command to close the tab.
    /// </summary>
    ICommand CloseCommand { get; }

    /// <summary>
    /// The command to save the album.
    /// </summary>
    ICommand SaveCommand { get; }

    /// <summary>
    /// The command to add a new track to the album.
    /// </summary>
    ICommand AddNewTrackCommand { get; }

    /// <summary>
    /// The command to delete the selected track.
    /// </summary>
    ICommand DeleteSelectedTrackCommand { get; }

    Track SelectedTrack { get; set; }

    event EventHandler RequestClose;
}

The implementation is very straightforward, you can see here. One thing to mention is that I configure nhibernate to resolve all my collections with INotifyCollectionChanged, so I can add a new Track in my viewmodel and the datagrid bound to the tracks collection will be notified about that change.

What’s next?

We have several troubles with this application:

  • Error handling. Is simple, this is what the user see. We MUST handle unexpected exceptions, just think what would happen if we shutdown the database server.
  • Asynchronous calls. For now we are freezing the UI thread with database and business layer operations. We need to build responsive UI’s
  • Validation. I will add support to the NHibernate Validator.
  • Conflict resolution for concurrency problems.

Do you want to see the app working?

Here you can see the full concept of "Conversation per Business Transaction" of Fabio Maulo working.

The full source code project is here.

Special thanks to all the people mentioned in this post.

| More

Part I: Introducing Nhibernate and WPF
Part II: Chinook Media Manager: The Core

Introduction

For this post I will use the concept of Fabio Maulo called “Conversation-per-BussinesTransaction”. If you have not read, what are you waiting for?

Definition

This definition is extracted from Fabio’s post and is very important that you understood it:

When you read something about NHibernate-session-management is because we want abstract/”aspect” NH-session’s stuff from the code are using it. In general, or I hope so, you are using the session in something like a DAO/Repository and, in a use-case, you are calling more than one DAO/Repository. Open a new session in each DAO/Repository is an anti-pattern called session-per-call (or very closer to it). What we are searching is something to manage the session in an high-layer but without wire that layer with NHibernate. In a WEB environment it is pretty easy because we can manage the session using an implementation of a IHttpModule, or in the HttpApplication, “intercepting” the request (two patterns mentioned here). In a winForm, or WPF, aplication it is not so clear where the abstraction have place. In this post I will use an “artifact” named Model to represent the “abstraction-point” (in my mind the Model is the business-object that are using DAOs/Repository and domain-entities); in an MVC based application, probably, the abstraction have place in the Controller.

Implementation

We start defining an interface for our Use-case Model class as follows:

public interface IAlbumManagerModel
{

    /// <summary>
    /// Search all albums from a given artist.
    /// </summary>
    /// <param name="artist"></param>
    /// <returns></returns>
    IEnumerable<Album> GetAlbumsByArtist(Artist artist);

    /// <summary>
    /// Persist an album.
    /// </summary>
    /// <param name="album"></param>
    void SaveAlbum(Album album);

    /// <summary>
    /// Revert changes of a given album to his current persisted state.
    /// </summary>
    /// <param name="album"></param>
    void CancelAlbum(Album album);

    /// <summary>
    /// Flush all changes to the database.
    /// </summary>
    void SaveAll();

    /// <summary>
    /// Cancel all changes.
    /// </summary>
    void CancelAll();
}

As I say in my previous post this interface is defined in the “ChinookMediaManager.Domain”.
The concrete implementation is defined in ChinookMediaManager.DomainImpl as follows:

[PersistenceConversational(MethodsIncludeMode = MethodsIncludeMode.Implicit)]
public class AlbumManagerModel : IAlbumManagerModel
{

    private readonly IAlbumRepository albumRepository;

    public AlbumManagerModel(IAlbumRepository albumRepository)
    {
        this.albumRepository = albumRepository;
    }

    /// <summary>
    /// Search all the albums from a given artist.
    /// </summary>
    /// <param name="artist"></param>
    /// <returns></returns>
    public IEnumerable<Album> GetAlbumsByArtist(Artist artist)
    {
        return albumRepository.GetByArtist(artist);
    }

    /// <summary>
    /// Persist an album.
    /// </summary>
    /// <param name="album"></param>
    public void SaveAlbum(Album album)
    {
        albumRepository.MakePersistent(album);
    }

    /// <summary>
    /// Revert changes of a given album to his original state.
    /// </summary>
    /// <param name="album"></param>
    public void CancelAlbum(Album album)
    {
        albumRepository.Refresh(album);
    }

    /// <summary>
    /// Flush all changes to the database.
    /// </summary>
    [PersistenceConversation(ConversationEndMode = EndMode.End)]
    public void SaveAll()
    {}

    /// <summary>
    /// Cancel all changes.
    /// </summary>
    [PersistenceConversation(ConversationEndMode = EndMode.Abort)]
    public void CancelAll()
    {
    }
}


Well, this is the non-invasive AOP way of doing with CpBT from unhaddins.
The PersistenceCovnersational attribute tell us two things:
- All methods are involved in the conversation if not explicitly excluded.
– All methods ends with “Continue” if not explicitly changed.
SaveAll end the conversation with EndMode.End, this means that the UoW will be flushed.
For the other hand CancelAll end with Abort, this means that all changes are evicted and the conversation is not flushed.

Testing the model

Testing is very simple, all you need to do is to mock your repositories as follows:

[Test]
public void can_cancel_album()
{
    var repository = new Mock<IAlbumRepository>();
    var album = new Album();
    repository.Setup(rep => rep.Refresh(It.IsAny<Album>()))
              .AtMostOnce();

    var albumManagerModel = new AlbumManagerModel(repository.Object);

    albumManagerModel.CancelAlbum(album);

    repository.Verify(rep => rep.Refresh(album));
}

In this test I’m using the Moq library.
The other thing that we need to test is the conversation configuration, my first approach was to test the attributes, but owned by the fact that we can configure the CpBT in many ways (such as XML), this is my test:

[TestFixtureSetUp]
public void FixtureSetUp()
{
    conversationalMetaInfoStore.Add(typeof (AlbumManagerModel));
    metaInfo = conversationalMetaInfoStore.GetMetadataFor(typeof(AlbumManagerModel));
}

[Test]
public void model_represents_conversation()
{
    metaInfo.Should().Not.Be.Null();
    metaInfo.Setting.DefaultEndMode.Should().Be.EqualTo(EndMode.Continue);
    metaInfo.Setting.MethodsIncludeMode.Should().Be.EqualTo(MethodsIncludeMode.Implicit);
}

[Test]
public void cancel_all_abort_the_conversation()
{
    var method = Strong.Instance<AlbumManagerModel>
        .Method(am => am.CancelAll());

    var conversationInfo = metaInfo.GetConversationInfoFor(method);

    conversationInfo.ConversationEndMode
                    .Should().Be.EqualTo(EndMode.Abort);
}

[Test]
public void save_all_end_the_conversation()
{
    var method = Strong.Instance<AlbumManagerModel>
        .Method(am => am.SaveAll());

    var conversationInfo = metaInfo.GetConversationInfoFor(method);

    conversationInfo.ConversationEndMode
                    .Should().Be.EqualTo(EndMode.End);
}

I'm using a strongly typed way to get the MethodInfo extracted from this example.

| More


Part I: Introducing NHiberate and WPF: The ChinookMediaManager

Introduction


This is my second post about the Chinook Media Manager, an example application for NHibernate and WPF. In this post I will outline some concepts behind the architecture that I’ve chosen.

Note

Probably at some points you will feel that this is over-architected, YAGNI and so on. Yes, you are right I’m not trying to bind a grid to a collection of objects there are tons of samples on the net about that subject.

The four musketeers

The core of the application is composed of four components:

  • ChinookMediaManager.Data: Contains the definition of repositories interfaces.
  • ChinookMediaManager.DataImpl: Contains the implementation of these repositories plus other nhibernate things that we will need to make work the implementation of repositories (like mappings).
  • ChinookMediaManager.Domain: Contains the domain classes plus model interfaces (we will focus on this concept latter)
  • ChinookMediaManager.DomainImpl: Contains the implementation of the models.

My base repository definition is as follows:

public interface IRepository<T> : IQueryable<T>
{
    T Get(object id);
    T Load(object id);
    T MakePersistent(T entity);
    void Refresh(T entity);
    void MakeTransient(T entity);
}

This means that a repository is a IQueryable<T> by itself and we can build queries in a more natural way.
I have seen a lot of repositories implementations with methods Save, Update and Delete, there is nothing wrong with it, just that I think that MakePersistent (Save) and MakeTransient (Delete) better represents that functionality (I have stolen this terminology from a Gustavo Ringel’s example).
You could find the implementation here.

Don’t touch “the domain”

Working with WPF sometimes you will need that your entities implement INotifyPropertyChanged or IEditableObject for data binding. Although the implementation of those interfaces is simple, if you implement directly in your domain you will tie your domain to a presentation concern and also will end writing a lot of code.

So, I wrote an addin for nhibernate (unhaddins.wpf) that will help you to inject those behaviors without touching anything in your domain model. 

We only have to add the entity to the container as follows:

container.Register(Component.For<Album>()
    .NhibernateEntity()
    .AddNotificableBehavior()
    .LifeStyle.Transient);

If you need to add editable object behavior (read this and this) you simple put “.AddEditableBehavior()”.
If we want a transient instance we will request it to the container (or an object factory). In unhaddins we have got an artifact that allows nhibernate to instantiate entities from the container, read this post from Fabio Maulo.

For INotifyCollectionChanged, unhaddins.wpf has a Collection Type Factory, we need to configure NHibernate as follows:

nhConfiguration.Properties[Environment.CollectionTypeFactoryClass] =
  typeof (WpfCollectionTypeFactory).AssemblyQualifiedName;

With this simple step all bag, sets and list (more coming up) of objects retrieved from NHibernate will implement INotifyCollectionChanged.

It goes without saying that this configuration is not in the domain.

As you can see, I don’t have to put presentations concerns inside “the four musketeers”.

In the next post I will talk about models and CpBT.

| More

As you can note in my previous posts I’ve talked a lot about NHibernate and WPF. So I've decided to write an ongoing series of blog posts about the integration of these two technologies.

Motivation

The motivation behind this series of posts is very simple most of the articles that you can read about NHibernate are for web technologies such as ASP.Net or ASP.NET MVC. Despite the fact that NHibernate is an ORM and has nothing to do with the presentation layer there are certain consideration that you have to take into account to build a good architecture.

Sample Domain

For this series of post I have chosen to use a small subset of the Chinook database with few modifications. I have changed all the Identities primary keys to HiLo. You can find both the original and the slightly modified version here (with class, mappings and read only tests).

This is the full database schema:

However we will focus in this subset of the domain:

ClassDiagram1

 

A word about session management

Most of the examples of NHibernate for web applications uses a pattern called “Session-per-Request”, this means that the session and the transaction has the same lifetime that the web request. This pattern is very easy to work with and very easy to understood but is not suitable for desktop applications.
There are two antipatterns called “session-per-application” and “session-per-call” that you need to avoid.
The pattern that I will use for this series is called “conversation per bussines transactions” and is very well described by his inventor Fabio Maulo. I’ve learned a lot with Fabio and Gustavo Ringel  about CpBT.

A word about data binding

In this series of post we will make intensive use of WPF data binding capabilities. Is for that reason that I’ve written uNHAddIns.WPF that uses NHibernate extensions points to bring us few behaviors that we need for data binding.

The interface that we have to solve

albums

The user need this very complex GUI. The user-story is as follows:

  • The user can have multiple instances of this window open for editing albums of different artists.
  • The user can add albums.
  • The user can add tracks to the new albums and preexistent albums.
  • None of the information is persisted until the user press “Save All”.
  • If the user press close the application will ask to the user if he really want to discard all changes.


This is all for now, in the next post I will talk about the architecture that I will use.

| More

Después de mucho tiempo sin escribir en español, quería hacer un post sobre algo que estuvimos viendo en la VAN de altnet hispano de ayer.
Mas allá de no gustarme muchas cosas sobre CSLA.Net de lo que voy hablar en este post es muy particular. Lo que voy a comentar es sobre el manejo de propiedades que es como sigue:

private static PropertyInfo<string> LastNameProperty = 
  RegisterProperty<string>(p=>p.LastName);
private string _lastName = LastNameProperty.DefaultValue;
public string LastName
{
  get
  {
    return GetProperty(LastNameProperty, _lastName);
  }
  set
  {
    SetProperty(LastNameProperty, ref _lastName, value);
  }
}

Cada vez que veo algo así, me pregunto que es lo que “gano” con hacer eso? Y créanme que lo he visto en otro lugar. La respuesta suele ser diferente, a veces la función “SetProperty” en la clase base suele hacer cosas como:

  • Disparar el evento de la interfaz INotifyProperyChanged.
  • Validar algo.
  • Establecer en algún lugar que la instancia esta Dirty, es decir con cambios pendientes de persistir.
  • También para implementar el comportamiento de IEditableObject.
  • Logging.
  • Validar acceso a alguna propiedad.

Y no mucho mas que eso. No es que todas estas cosas no sean necesarias, lo que me saca de las casillas es la forma de declarar una propiedad. Tiene “MUCHO” para hacer tan “POCO”. A tal punto que definir una propiedad de mi BussinesObject puede ser un verdadero dolor de cabeza y necesite un generador de código. Por supuesto, se comento en la VAN que si a alguien no le gusta esto, lo puede hacer “manual” que son muchas mas líneas de código.
Todas las cosas que mencione en la lista anterior ya han sido estudiadas y pueden ser realizadas perfectamente mediante AOP. La solución, como dijo Fabio Maulo ayer es utilizar PostSharp o DynamicProxy. Estos frameworks permiten implementar el concepto de AOP, de dos maneras muy diferentes, por un lado DynamicProxy a partir de una clase genera dinámicamente otra que hereda (en algunos casos no) de la primera y ejecuta los interceptors. Por otro lado PostSharp inyecta código post compilación. Mientras que PostSharp es muy potente, DynamicProxy es bastante flexible y fácil de entender para alguien que se inicie en AOP.
Como conozco poco de PostSharp, voy a explicar un poco DP. DynamicProxy de Castle (como Spring.AOP de Spring y creo que en la misma línea esta LinFu) son muy, pero muy sencillos de interpretar.
Cuando uno crea un proxy lo que se tiene que imaginar es que “una pila de interceptores con un orden especifico va a interceptar cada llamada a función de la clase que se esta proxy-ando”.´

Algo así:
first_thumb[20] 

La interfaz IInterceptor tiene solamente un método:

public void Intercept(IInvocation invocation)

IInvocation es un objeto muy jugoso. Tiene muchas propiedades entre las que se destacan:

  • Arguments: un array con los parámetros que se llamo al método. Recordemos que las propiedades al final son métodos.
  • Method: es un objeto MethodInfo propio del framework, del cual podemos sacar cosas interesantísimas como el nombre del método, los attributos, custom attributes, etc.
  • Proxy: la instancia del objeto proxy.
  • InvocationTarget: el destino final de la invocación.
  • ReturnValue: Esta es una de las mas importantes…. Este es el valor que va a devolver la función. Puedo cambiarlo, inspeccionarlo, guardarlo, hacer lo que quiera.

Luego tiene un método que se llama “Proceed”, este método lo que hace es proceder con la invocación, esto significa pasar la llamada al siguiente interceptor o finalmente al destino de la invocación.

Escribiendo un IInterceptor para NotifyPropertyChanged. Lo primero que hace falta es un buen test ;-)

[TestFixture]
public class Test1
{
    [Test]
    public void notify_on_change()
    {
        var proxyGen = new ProxyGenerator();
        bool eventWasRaised = false;
        var persona = (Persona)proxyGen.CreateClassProxy(typeof (Persona),
                               new []{typeof(INotifyPropertyChanged)},
                               new IInterceptor[] {new PropertyChangeInterceptor()});

        ((INotifyPropertyChanged) persona).PropertyChanged += 
            (sender, args) =>
            {
                args.PropertyName.Should().Be.EqualTo("Nombre");
                eventWasRaised = true;
            };

        persona.Nombre = "José";

        eventWasRaised.Should().Be.True();

    }
}

public class Persona
{
    public virtual int Id { get; set; }
    public virtual string Nombre { get; set; }
    public virtual string Apellido { get; set; }
}

Notar como esta declarada la clase “Persona”, solo lo que necesito. Por la forma en que esta instanciada no se preocupen mucho por que se puede hacer unos extensions methods, y dejarla dentro del contenedor, el contenedor resolverá el Proxy. Generalmente Castle, Spring y LinFu permiten lo mismo, el generador de proxy se integra con el container.

Explicando el test, lo que estoy haciendo es crear una instancia proxy-ada, subscribirme al evento propertychanged y modificar la propiedad Nombre. Lo que verifico es que el evento haya sido disparado y que en los args venga el nombre de la property que modifique.

Como hago que este test pase?

public class PropertyChangeInterceptor : IInterceptor
{
    private PropertyChangedEventHandler _handler;

    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
        string methodName = invocation.Method.Name;

        if (methodName == "add_PropertyChanged")
        {
            _handler = (PropertyChangedEventHandler)
                       Delegate.Combine(_handler, (Delegate) invocation.Arguments[0]);
            return;
        }
        if (methodName == "remove_PropertyChanged")
        {
            _handler = (PropertyChangedEventHandler)
                       Delegate.Remove(_handler, (Delegate) invocation.Arguments[0]);
            return;
        }

        invocation.Proceed();

        if (invocation.MethodInvocationTarget.Name.StartsWith("set_"))
        {
            if (_handler != null)
              _handler(invocation.Proxy, new PropertyChangedEventArgs(methodName.Substring(4)));
            }
        }
    }

    #endregion
}

Lo que hago en esta clase es primero interceptar la subscripción y dessubscripción al evento “PropertyChanged”. Notar que a esas invocaciones no les doy proseguir, puesto que el destino de la invocación ni si quiera implementa INotifyPropertyChanged (en el test la interfaz fue agregada al proxy como una interface adicional). Para todas las demás llamadas las prosigo. La parte fundamental ocurre después de proseguir la invocación, si el método empezaba con “set_” asumo que el código que llama esta seteando una propiedad, por lo tanto disparo el evento, pasándole como argumentos el nombre del método sin los primeros 4 caracteres (sin el “set_”).

De esta forma dejo librado a su imaginación las cosas que pueden hacer con los interceptors. Aunque muchas de las que vienen con la CSLA.Net ya existen de esta forma o de otras formas mejores.

Creo que el código para hacer el bussines object verdaderamente se disminuye.

Espero que el artículo haya sido de vuestro agrado y espero sus comentarios.

| More