Moritz
Moritz

Reputation: 389

Implement autofac in boostrapper (caliburn.micro) correctly

I would like to re-code my first little C# project using Caliburn.Micro and the bootstrapper stuff. During my first approach I used Autofac but now I'm stuck. Without Caliburn I was able to retrieve my employees from a db file like this (mind the scope.resolve stuff -- thats my problem right now):

public ObservableCollection<EmployeeEntity> Employees
{
    get
    {
        var container = ContainerConfig.Configure();
        using (var scope = container.BeginLifetimeScope())
        {
            var test = scope.Resolve<IEmployeeRepository>();
            _Employees = test.GetAll().ToObservable();
        }
        return _Employees;
    }
    set
    {
        _Employees = value;
    }
}

Now I switched to Caliburn.Micro incl. bootstrapper which looks like this (I would like to keep the LifeTimeScope stuff)

protected override void Configure()
{
    var builder = new ContainerBuilder();

    builder.RegisterType<WindowManager>()
        .AsImplementedInterfaces()
        .SingleInstance();

    builder.RegisterType<EventAggregator>()
        .AsImplementedInterfaces()
        .SingleInstance();

    builder.RegisterType<CalendarViewModel>()
        .SingleInstance();

    builder.RegisterType<EmployeeViewModel>()
        .SingleInstance();

    builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>()
        .InstancePerLifetimeScope();

    Container = builder.Build();
}

On application start I open "CalendarViewModel" and get the corresponding view. My view contains a menu which opens a second view "EmployeeViewModel" on click with the following implementation:

private readonly IWindowManager _windowManager;
public CalendarViewModel(IWindowManager WindowManager)
{
    _windowManager = WindowManager;
}
public void OpenEmployeeView() => _windowManager.ShowWindow(new EmployeeViewModel());

Now my problem is how to change and include the code from the beginning (Autofac only) to a working one using the bootstrapper and the implementation of "IEmployeeRepository" with "Lifetimescope".

What I have tried:

I tried to include "IEmployeeRepository" like I did with the WindowManager:

private ObservableCollection<EmployeeEntity> _Employees;
private readonly IEmployeeRepository _emp;
public EmployeeViewModel(IEmployeeRepository Emp)
{
    _emp = Emp;
}
public ObservableCollection<EmployeeEntity> Employees
{
    get
    {
        _Employees = _emp.GetAll().ToObservable();
        return _Employees;
    }
    set
    {
        _Employees = value;
    }
}

but (as you might already know) I get an error

CS7036 There is no argument given that corresponds to the required formal parameter

So how do I implement Autofac within Bootstrapper correctly? Quite sure that my way isn't the proper way to do it :/

Quick summary: (bootstrapper) --application start--> (CalendarViewModel) --menu item--> (EmployeeViewModel)

UPDATE 29.10.2019: The line

public EmployeeViewModel(IEmployeeRepository Emp)
{
    _emp = Emp;
}

throws an error. I guess because my OpenCommand

public void OpenEmployeeView() => _windowManager.ShowWindow(new EmployeeViewModel());

does not pass any IEmployeeRepository to my EmployeeViewModel.

UPDATE 29.10.2019 No 2: new error message

System.InvalidOperationException: "A parameterless default constructor or one matching signature (System.Int64 Id, System.String FirstName) is required for Calendar.Database.Entities.EmployeeEntity materialization"

is thrown within SqlRepository.cs (GetAll return row throws the error)

protected SqlRepository(IDbContext dbContext)
{
    DbContext = dbContext;
}

public IList<TEntity> GetAll()
{
    return DbContext.Connection.GetAll<TEntity>().ToList();
}

What I did: Bootstrapper.cs

builder.RegisterType<EmployeeViewModel>()
.SingleInstance();
builder.RegisterType<DbContext>().As<IDbContext>()
.InstancePerLifetimeScope();
builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>()
.InstancePerLifetimeScope();

CalendarViewModel.cs

public void OpenEmployeeView() => _windowManager.ShowWindow(IoC.Get<EmployeeViewModel>());

EmployeeViewModel.cs

private readonly IEmployeeRepository _emp;
public EmployeeViewModel(IEmployeeRepository Emp)
{
    _emp = Emp;
}
public ObservableCollection<EmployeeEntity> Employees
{
    get
    {
        _Employees = _emp.GetAll().ToObservable();
        return _Employees;
    }
    set
    {
        _Employees = value;
    }
}

(I could post a link to my Visual Studio Project … but don't know if it is allowed here)

Upvotes: 0

Views: 716

Answers (1)

Pavel Anikhouski
Pavel Anikhouski

Reputation: 23268

You can try to use

var employeeViewModel = IoC.Get<EmployeeViewModel>();
…
public void OpenEmployeeView() => _windowManager.ShowWindow(employeeViewModel);

to get instance of EmployeeViewModel with injected constructor parameters.

Another option is inject the EmployeeViewModel to CalendarViewModel, if it fits your arhitecture design

Upvotes: 1

Related Questions