Matthieu Mezil
L’objet, c’est beau
Le développement orienté objet peut être apparenté, osons le mot, à une forme d’art. Cependant, avant de devenir un « artiste » de l’objet, il y a plusieurs notions à maîtriser. A travers cet article, nous allons en aborder quelques-unes.
Par Matthieu Mezil publié le 27/04/2009 à 07:10, lu 2235 fois, 10 pages
 7 | MEF
MEF
Avec MEF, plus besoin de l’interface IStringsTransformInstanciator (en théorie) mais surtout, plus besoin de la classe Transforms.
Pourquoi, dans la pratique, ais-je encore besoin de mon Abstract Factory ?
Pour avoir plusieurs instances par traitement. Imaginons en effet que je souhaite combiner deux numérotations de lignes. L’utilisation la même instance de NumberPerStringTransform entraîne le résultat suivant :
002 : 001 : ma première ligne
004 : 002 : ma deuxième ligne
etc.
Nous allons donc garder l’abstract factory.
Pour utiliser MEF, il faut commencer par télécharger la dll de MEF sur codeplex.
Ensuite, il faut ajouter la dll dans les références de notre projet UI et des différents projets BLL.
Il faut enfin décorer les classes de traitements d’attributs en précisant à MEF quel sera le type vu côté consommateur de traitements (l’UI dans notre cas).

[Export(typeof(IStringsTransformInstanciator))]

public class InvertCharsPerStringTransformInstanciator : IStringsTransformInstanciator

{

    internal InvertCharsPerStringTransformInstanciator()

    {

    }

 

    public IStringsTransform CreateInstance()

    {

        return new InvertCharsPerStringTransform();

    }

 

    public override string ToString()

    {

        return TransformResources.InvertCharsPerStringTransform;

    }

}

 

[Export(typeof(IStringsTransformInstanciator))]

public class NumberPerStringTransformInstanciator : IStringsTransformInstanciator

{

    internal NumberPerStringTransformInstanciator()

    {

    }

 

    public IStringsTransform CreateInstance()

    {

        return new NumberPerStringTransform();

    }

 

    public override string ToString()

    {

        return TransformResources.NumberPerStringTransform;

    }

}

Maintenant, nous allons définir un nouvel assembly dans lequel nous allons ajouter un nouveau traitement : UpperTransform.

public class UpperTransform : PerStringTransformBase

{

    protected override string Do(string line)

    {

        return line.ToUpper();

    }

 

    public override string ToString()

    {

        return TransformResources.Upper;

    }

}

 

[Export(typeof(IStringsTransformInstanciator))]

public class UpperTransformInstanciator : IStringsTransformInstanciator

{

    public IStringsTransform CreateInstance()

    {

        return new UpperTransform();

    }

 

    public override string ToString()

    {

        return TransformResources.Upper;

    }

}

Cet assembly n’est pas référencé par l’UI et pourtant, nous allons l’utiliser dans l’UI comme ceci. Tout d’abord nous allons définir un répertoire dans lequel seront stockés toutes les dll contenant nos traitements. Pour l’exemple, nous choisirons le répertoire de l’exe. Ensuite, il faudra copier les dll dans ce répertoire. Une petite astuce pour la phase de développement consiste à utiliser les Post-build events définis dans Visual Studio :
copy $(TargetFileName) ..\..\..\FileTransformUI\bin\Debug\
Ensuite, il suffit d’utiliser MEF pour lui faire découvrir dynamiquement les traitements disponibles :

[Import]

IEnumerable<IStringsTransform> _transforms;

 

private void FileTransformForm_Load(object sender, EventArgs e)

{

    var transformsContainer = new CompositionContainer(new DirectoryCatalog("."));

    var batch = new CompositionBatch();

    batch.AddPart(this);

    transformsContainer.Compose(batch);

    traitementCombo.DataSource = _transforms;

}

A la place du ComboBox nous allons utiliser deux ListBox pour que l’utilisateur puisse combiner ses traitements à l’infini.
Ensuite, nous allons modifier la classe FileTransform pour que l’argument transformDone de type Action<bool> devienne un Action<bool, string> :

public static class FileTransform

{

    private static Dictionary<string, BackgroundWorker> _transformsRunning =

        new Dictionary<string, BackgroundWorker>();

    private static object _lockObject = new object();

 

    public static void Transform(string inFilePath, string outFilePath,

        IEnumerable<IStringsTransformInstanciator> transforms, Action<string> showError,

        Func<string, bool> showErrorWithRetry, Action<bool, string> transformDone)

    {

        var transform = new CompositeStringsTransform(transforms);

        if (!(File.Exists(inFilePath) || _transformsRunning.ContainsKey(inFilePath)))

            FileNotFoundShowError(inFilePath, showError);

        else if (!Directory.Exists(Path.GetDirectoryName(outFilePath)))

            DirectoryNotFoundShowError(outFilePath, showError);

        else

        {

            var bg = new BackgroundWorker();

            bg.DoWork += (s, e) =>

            {

                e.Result = false;

                while (true)

                {

                    try

                    {

                        FileUtil.WriteLines(outFilePath, transform.Do(FileUtil.GetLines(inFilePath)));

                        e.Result = true;

                    }

                    catch (FileNotFoundException)

                    {

                        FileNotFoundShowError(inFilePath, showError);

                    }

                    catch (DirectoryNotFoundException)

                    {

                        DirectoryNotFoundShowError(outFilePath, showError);

                    }

                    catch (IOException)

                    {

                        if (showErrorWithRetry(string.Format(ErrorResources.IOException, outFilePath)))

                            continue;

                    }

                    catch (Exception ex)

                    {

                        showError(string.Format(ErrorResources.FileNotFoundException,

                            string.Concat(ex.GetType(), " : ", ex.Message)));

                    }

                    break;

                }

                lock (_lockObject)

                {

                    _transformsRunning.Remove(outFilePath);

                }

            };

            bg.RunWorkerCompleted += (s, e) =>

            {

                if (transformDone != null)

                    transformDone((bool)e.Result, transform.ToString());

            };

 

            StartBGAndCache(inFilePath, outFilePath, bg);

        }

    }

 

    private static void StartBGAndCache(string inFilePath, string outFilePath, BackgroundWorker bg)

    {

        lock (_lockObject)

        {

            RunWorkerCompletedEventHandler runWorkerCompletedTryAgain = null;

            Action<BackgroundWorker> tryAgain = (bgTryAgain) =>

            {

                StartBGAndCache(inFilePath, outFilePath, bg);

                bgTryAgain.RunWorkerCompleted -= runWorkerCompletedTryAgain;

            };

            if (_transformsRunning.ContainsKey(inFilePath))

            {

                var bgTryAgain = _transformsRunning[inFilePath];

                runWorkerCompletedTryAgain = (s, e) => tryAgain(bgTryAgain);

                bgTryAgain.RunWorkerCompleted += runWorkerCompletedTryAgain;

            }

            else if (_transformsRunning.ContainsKey(outFilePath))

            {

                var bgTryAgain = _transformsRunning[outFilePath];

                runWorkerCompletedTryAgain = (s, e) => tryAgain(bgTryAgain);

                bgTryAgain.RunWorkerCompleted += runWorkerCompletedTryAgain;

            }

            else

            {

                _transformsRunning.Add(outFilePath, bg);

                bg.RunWorkerAsync();

            }

        }

    }

 

    private static void DirectoryNotFoundShowError(string directoryPath, Action<string> showError)

    {

        showError(string.Format(ErrorResources.DirectoryNotFoundException, Path.GetDirectoryName(directoryPath)));

    }

 

    private static void FileNotFoundShowError(string filePath, Action<string> showError)

    {

        showError(string.Format(ErrorResources.FileNotFoundException, filePath));

    }

}

Maintenant, côté UI, le code sera le suivant :

private void runBtn_Click(object sender, EventArgs e)

{

    FileTransform.Transform(inFilePathTB.Text, outFilePathTB.Text, transformsLB.Items.Cast<IStringsTransform>(),

        message => MessageBox.Show(message, Resources.ErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error),

        message => MessageBox.Show(message, Resources.ErrorTitle, MessageBoxButtons.RetryCancel, MessageBoxIcon.Error)

            == DialogResult.Retry,

        (result, transformName) =>

        {

            if (result)

                transformsDoneLB.Items.Add(

                    string.Concat(transformName, " ", Resources.Done, ". ",

                    inFilePathTB.Text, " -> ", outFilePathTB.Text));

        });

}

 
» Démarrer une discussion
 
Discussion démarée par killscores le 28/04/2009 à 17:06, 3 commentaire(s).