Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic pipeline where the input/output types are different for each filter

I am trying to implement a Filter/Pipeline pattern so that I can take an input, process it through a number of filters and get an output at the end.

I can do this easily when the input type and final output types are the same and each filter uses the same types too. However, I want to input one type and get another type out.

e.g. Take a csvfile by its filename, load it into individual string, parse them, validate and output as xml. psuedo code example:

input = filename
filter = load csv file <filename, list<string>>
filter = parse csv <list<string>, list<businessobject>>
filter = validate objects <list<businessobject>, list<businessobject>> *... return type same as input type in this case.*
filter = create xml <list<businessobject>, XDocument>
filter = validate XDoc <XDocument, XDocument>
output = XDocument

Here is what I have so far:

IFilter, FilterBase, FilterImplementation
IPipeline, Pipeline
IBusinessObject, BusinessObject, BusinessObjectImplementation

My intention was to be able to have a List of IFilter<T,U> where T and U are IBusinessObject

However, I get a "Cannot Convert from BusinessObjectImplementation to IBusinessObject" when trying to add an IFilter<IBusinessObject, IBusinessObject> to the list.

Apols for all the code...its the very last part that won't compile


public interface IFilter<T, U>
        where T : IBusinessObject
        where U : IBusinessObject
    {
        U Execute(T input);
    }

    public abstract class FilterBase<T, U> : IFilter<T, U>
        where T : IBusinessObject
        where U : IBusinessObject, new()
    {
        protected abstract U Process(T input);

        public U Execute(T input)
        {
            return Process(input);
        }

    }

    public class FilterCsvFileLoader<T, U> : FilterBase<T, U>, IFilter<T, U>
        where T : FilenameObject, IBusinessObject
        where U : CSVFile, IBusinessObject, new()
    {
        public FilterCsvFileLoader()
        { }

        protected override U Process(T input)
        {
            U result = new CSVFile(input) as U;
            return result;
        }
    }

public interface IPipeline
    {
        IBusinessObject Execute(IBusinessObject input);

        IPipeline Register(IFilter<IBusinessObject, IBusinessObject> filter);
    }

    public class Pipeline : IPipeline
    {
        private List<IFilter<IBusinessObject, IBusinessObject>> _filters = new List<IFilter<IBusinessObject, IBusinessObject>>();

        public IBusinessObject Execute(IBusinessObject input)
        {
            var result = input;
            foreach (var filter in _filters)
            {
                result = filter.Execute(result);
            }
            return result;
        }

        public IPipeline Register(IFilter<IBusinessObject, IBusinessObject> filter)
        {
            _filters.Add(filter);
            return this;
        }
    }

public interface IBusinessObject
    {
        bool Validate();
        List<string> ValidationErrors { get; }
    }

    public class BusinessObject : IBusinessObject
    {
        private List<BusinessRule> _businessRules = new List<BusinessRule>();

        private List<string> _validationErrors = new List<string>();

        public List<string> ValidationErrors
        {
            get { return _validationErrors; }
        }
        protected void AddRule(BusinessRule rule)
        {
            _businessRules.Add(rule);
        }

        public bool Validate()
        {
            bool isValid = true;

            _validationErrors.Clear();

            foreach (BusinessRule rule in _businessRules)
            {
                if (!rule.Validate(this))
                {
                    isValid = false;
                    _validationErrors.Add(rule.ErrorMessage);
                }
            }
            return isValid;
        }
    }

    public class FilenameObject : BusinessObject, IBusinessObject
    {
        string _filename;

        public string Filename
        {
            get { return _filename; }
        }

        public FilenameObject(string filename)
        {
            _filename = filename;
        }
    }

    public class CSVFile : BusinessObject, IBusinessObject
    {
        private string _filename;
        private string[] _splitChar = new string[] { "," };

        public List<List<string>> Lines { get; set; }

        public CSVFile()
        { }

        public CSVFile(FilenameObject filename)
            : this()
        {
            _filename = filename.Filename;
            Lines = new List<List<string>>();
        }

        private void ImportFile()
        {
            FileInfo fi = new FileInfo(_filename);
            using (StreamReader sr = new StreamReader(fi.Open(FileMode.Open, FileAccess.Read, FileShare.None)))
            {
                String readline;
                while ((readline = sr.ReadLine()) != null)
                {
                    var line = (from l in readline.Split(_splitChar, StringSplitOptions.None)
                                select l.Trim()).ToList();
                    Lines.Add(line);
                }
            }
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            var pipeline = new Pipeline()
            .Register(new FilterCsvFileLoader<FilenameObject, CSVFile>());
        }
    }

The exception is on the .Register line above

Error 2 Argument 1: cannot convert from 'BusinessLogic.FilterCsvFileLoader<BusinessObjects.FilenameObject,BusinessObjects.CSVFile>' to 'BusinessLogic.IFilter<BusinessObjects.IBusinessObject,BusinessObjects.IBusinessObject>'
C:\Users\davidc\Documents\Visual Studio 2010\Projects\MPMeFeed\TestConsole\Program.cs 15 23 TestConsole

like image 346
BlueChippy Avatar asked Nov 29 '11 09:11

BlueChippy


1 Answers

The problem you have here is that FilterCsvFileLoader<FilenameObject, CSVFile> implements IFilter<FilenameObject, CSVFile> which ISN'T a derived type of IFilter<IBusinessObject, IBusinessObject> even though FilenameObject & CSVFile are derived from IBusinessObject.

This is a common mistake to make.

Try something like this instead:

public interface IFilter
{
    IBusinessObject Execute(IBusinessObject input);
}

public interface IFilter<T, U> : IFilter
    where T : IBusinessObject
    where U : IBusinessObject
{
    U Execute(T input);
}

public abstract class FilterBase<T, U> : IFilter<T, U>
    where T : IBusinessObject
    where U : IBusinessObject, new()
{
    protected abstract U Process(T input);

    IBusinessObject IFilter.Execute(IBusinessObject input)
    {
        return this.Execute((T)input);
    }

    public U Execute(T input)
    {
        return Process(input);
    }
}

public interface IPipeline
{
    IBusinessObject Execute(IBusinessObject input);

    IPipeline Register<T, U>(IFilter<T, U> filter)
        where T : IBusinessObject
        where U : IBusinessObject;
}

public class Pipeline : IPipeline
{
    private List<IFilter> _filters = new List<IFilter>();

    public IBusinessObject Execute(IBusinessObject input)
    {
        var result = input;
        foreach (var filter in _filters)
        {
            result = filter.Execute(result);
        }
        return result;
    }

    public IPipeline Register<T, U>(IFilter<T, U> filter)
        where T : IBusinessObject
        where U : IBusinessObject
    {
        _filters.Add(filter);
        return this;
    }
}
like image 192
Enigmativity Avatar answered Sep 18 '22 18:09

Enigmativity