Search code examples
c#mef

Objects disappearing once constructor finishes


I suspect there is a term for this behavior that, if I knew it, I could google it and learn what I need. However, I don't know it.

Here is my constructor:

[Export(typeof(MainWindowViewModel))]
public class MainWindowViewModel : ObservableObject
{
    private readonly IProductService _productService;
    private readonly IProfileService _profileService;
    public IEnumerable<ProductViewModel> Products { get; private set; }
    private ProductViewModel _productSelected;

    [ImportingConstructor]
    public MainWindowViewModel(IProductService productService, IProfileService profileService, ILoggingService logger)
    {
        Products = _productService.InstalledProducts.Select(p => new ProductViewModel(p, _profileService, _logger));
        SelectTheProductInDirectoryRunningFrom();

        _productSelected.Load();
    }

    protected virtual void SelectTheProductInDirectoryRunningFrom()
    {
        string currentDir = Directory.GetCurrentDirectory();

        if (_productSelected != null && _productSelected.InstalledPath != null &&
                !_productSelected.InstalledPath.Contains(currentDir))
        {
            _productSelected =
                Products.Where(p => currentDir.Contains(p.InstalledPath)).Select(p => p).DefaultIfEmpty(
                    _productSelected).SingleOrDefault();
        }
    }

It seems pretty self-explanatory. It builds a collection of ProductViewModels, finds the relevant one, and calls Load() on it. ProductViewModel.Load() contains this code:

public class ProductViewModel : ObservableObject
{
    private readonly IProfileService _profileService;
    private readonly ILoggingService _logger;
    private ObservableCollection<ProfileViewModel> _profiles;
    private ConfigProfile _defaultConfig;
    private ProfileViewModel _currentProfile;
    public ListCollectionView Profiles { get; set; }

public bool Load(string errorMessage, bool critical)
{
    List<ProfileViewModel> profileVms = new List<ProfileViewModel> { _currentProfile };

    profileVms.AddRange(
        _profileService.GetSavedProfiles(_data.ProductType).Select(
            p =>
            {
                p.FilePath = current.FilePath;
                return new ProfileViewModel(p, _defaultConfig, _profileService) { IsChanged = false };
            }));
    _profiles = new ObservableCollection<ProfileViewModel>(profileVms);

    Profiles = new ListCollectionView(_profiles);
    Profiles.SortDescriptions.Add(new SortDescription("ProfileTypeValue", ListSortDirection.Ascending));
    Profiles.SortDescriptions.Add(new SortDescription("ProfileName", ListSortDirection.Ascending));
    Profiles.CurrentChanged += (sender, args) =>
    {
        ((ProfileViewModel)Profiles.CurrentItem).Initialize();
        _currentProfile = Profiles.CurrentItem as ProfileViewModel;
    };

    return true;
}

When I step through this code in the visual studio debugger, everything executes and both _profiles and Profiles are assigned to correctly. However, when execution returns from the MainWindowViewModel constructor, _profiles and Profiles are both null.

enter image description here

I press F11 twice to arrive here:

enter image description here

What could be going wrong? Are my objects somehow passing out of scope? I thought maybe it had something to do with value vs reference but I can't find anything. Thanks!


Solution

  • Assigning to an object from a WhereSelectEnumerableIterator creates a new object so the original list wasn't being updated.

    _productSelected =
        Products.Where(p => currentDir.Contains(p.InstalledPath)).Select(p => p).DefaultIfEmpty(
            _productSelected).SingleOrDefault();
    

    I added a .ToList() onto this call

    Products = _productService.InstalledProducts.Select(p => new ProductViewModel(p, _profileService, _logger));
    

    to force the IEnumerable into a List which does not create a new object and does update the existing one. Thanks @Igor.