1
votes

I'm working on adding a Windsor IoC container to an existing WinForms application that uses an MVP UI design pattern. I'm trying to determine a good approach to resgistering a datacontext that depends on a connection string supplied at runtime. The problem is that I cannot create a datacontext until the user selects a database, i.e. a 'connection string' after the application has loaded. Granted only one datacontext is generally used, but sometimes a user need to switch to a different database, i.e. creating a differnet datacontext. This leads to additional runtime dependencies as well.

public interface IProductsView
{
    event EventHandler<ProductSelectedEventArgs> ProductSelectedEvent;
    event EventHandler<StringEventArgs> ProductStatusEvent;
    void ClearProductList();
    void DisplayProductList(IList<Product> products);
    Control Control { get; }
    IProductsPresenter Presenter { get; set; }
}

public class ProductsPresenter : IProductsPresenter
{
    public IProductsView View { get; set; }
    private IProductRepository Repository { get; set; }

    public ProductsPresenter(IProductsView view, IProductRepository repository)
    {
        View = view;
        View.Presenter = this;
        Repository = repository;
    }

    public void ProductSelected(IList<Product> products)
    {
        throw new NotImplementedException();
    }

    public void ShowProductList(string name)
    {
        IList<Product> productList;

        if (string.IsNullOrEmpty(name))
            productList = Repository.GetProducts();
        else
            productList = Repository.GetProductsByName(name);

        View.DisplayProductList(productList);
    }
}

public class ProductDao : IDisposable, IProductRepository
{
    private MeasurementDataContext dataContext;

    public ProductDao(MeasurementDataContext context)
    {
        dataContext = context;
    }

    public List<Product> GetProducts()
    {
        return dataContext.Products.Select(p => Mapper.Map(p)).ToList().OrderBy(x => x.Name).ToList();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
            if (dataContext != null)
            {
                dataContext.Dispose();
                dataContext = null;
            }
    }

    ~ProductDao()
    {
        this.Dispose(false);
    }
}

So this means that the Presenter in my View is null until the IProductRepository is created, which in turn depends on creating a MeasurementDataContext. I have these component regisitered in a IWindsorInstaller like so:

container.Register(Component.For<IProductsView>()
            .ImplementedBy<ViewProductsControl>());
        container.Register(Component.For<IProductsPresenter>()
            .ImplementedBy<ProductsPresenter>());

Do I need to use Named and DependsOn which supply a unique name and connectionString argument for each datacontext?

What I currently do to register the data context at runtime after the user has selected a database

kernel.Register(Component.For<MeasurementDataContext>()
               .UsingFactoryMethod(() => new MeasurementDataContext(conn)));

and then `Resolve' my Views and set their Presenters. I know this is not good design, but it's a brute force way of resolving my dependcies.

Thanks

UPDATE:

I changed the way I registered my datacontext's in the installer to the following:

container.Register(Component.For<DataContext>().ImplementedBy<MeasurementDataContext>().Named("Localhost").DependsOn(new { connectionString = conn }));

and then modified my model's constructor to:

public ProductDao(DataContext context)
    {
        dataContext = context as MeasurementDataContext;
    }

All components will resolve with the right key:

kernel.Resolve<DataContext>(cbo.SelectedItem.ToString());
1
Unrelated to your questions, but there's no reason to implement the full-blow finalizer version of the Dispose pattern since you are only disposing managed resources. If your finalizer executes it will not do anything (diposing = false in your Dispose() method yields nothing); it would just be an unnecessary performance penalty. - roken

1 Answers

1
votes

What about injecting a wrapper class to hold the connection string and have the datacontext objects use that? Something along these lines:

public class ConnectionStringProvider : IConnectionStringProvider
{
    private string _value;

    public event EventHandler ConnectionStringChanged;

    public string ConnectionString
    {
        get { return _value; }
        set
        {
            _value = value;

            var del = ValueChanged;
            if (del != null)
                del(this, EventArgs.Empty);
        }
    }
}

Register this with and singleton lifestyle. This way your application can set or update the connection string on a single object and everyone who depends on it will be notified of the change.