0
votes

i have a problem to cast ViewModel Class with abstract class from external assembly.

i have 2 dlls and one executable:

one Abstract class into dll Cockpit.RUN.Common.dll, this Dll is referenced: in the other project of dll Cockpit.RUN.ViewModels and in the project of executable Cockpit.RUN.Test, but Cocpit.RUN.ViewModels is not referenced in the project of executable, because its loaded dynamically

The abstrac class: (dll)

using Caliburn.Micro;

namespace Cockpit.RUN.Common
{
    public abstract class PluginModel : PropertyChangedBase
    {
        public abstract double Width { get; set; }
        public abstract double Height { get; set; }
        public abstract double Left { get; set; }
        public abstract double Top { get; set; }

        private double zoomfactorfrompluginmodel;
        public double ZoomFactorFromPluginModel
        {
            get => zoomfactorfrompluginmodel;
            set
            {
                zoomfactorfrompluginmodel = value;
                NotifyOfPropertyChange(() => ZoomFactorFromPluginModel);
            }
        }
        public string NameUC;
    }
}

second dll containing viewmodels Cockpit.RUN.ViewModels.dll i have lot of plugins differents in this dll: Push_ViewModel, Switch_ViewModel....

i derive each class with the abstract class for example for the Push_ViewModel:

using Cockpit.RUN.Common;
using System.Windows;
using System.Windows.Input;

namespace Cockpit.RUN.ViewModels
{
    public class PushButton_ViewModel : PluginModel
    {

        public PushButton_ViewModel(params object[] settings)
        {
            NameUC = (string)settings[2];
            Layout = new LayoutPropertyViewModel();
        }

        public override double Left
        {
            get => Layout.UCLeft;
            set => Layout.UCLeft = value;
        }
        public override double Top
        {
            get => Layout.UCTop;
            set => Layout.UCTop = value;
        }
        public override double Width
        {
            get => Layout.Width;
            set => Layout.Width = value;
        }
        public override double Height
        {
            get => Layout.Height;
            set => Layout.Height = value;
        }                         
    }
}

one thing again, this plugin dll is loaded dynamically during the bootstrapper

    protected override IEnumerable<Assembly> SelectAssemblies()   
    {                                                                                                                                  
        var assemblies = new List<Assembly>();
        assemblies.AddRange(base.SelectAssemblies());
        assemblies.Add(Assembly.LoadFile(@"J:\ProjetC#\Cockpit-master\Cockpit.RUN.Test\bin\Debug\Cockpit.RUN.ViewModels.dll")); 
        assemblies.Add(Assembly.LoadFile(@"J:\ProjetC#\Cockpit-master\Cockpit.RUN.Test\bin\Debug\Cockpit.RUN.Views.dll")); 
        return assemblies;   
    }  

so the problem is when i want to cast each ViewModels with the abstract base PulginModel, my result is null:

        using Cockpit.RUN.Common;
                    :
                    :
        public BindableCollection<PluginModel> MyCockpitPlugins { get; set; }

                    :
                    :

        model = "Cockpit.RUN.ViewModels.PushButton_ViewModel, Cockpit.RUN.ViewModels";
        var typeClass = Type.GetType(model); // its ok 
        var viewmodel = resolutionRoot.TryGet(typeClass, param); // Its ok i have an instance of PushButton_ViewModel

        var v = viewmodel as PluginModel; //-> not ok its null
        var w = (PluginModel)viewmodel; //-> not ok its error

        // So i cant display my  list of views associated 
        MyCockpitPlugins.Add(v);

Do I have done something wrong?

assembly loaded at break debug before casting:

ScreenPrint of processes

and the result of typeof and GetType:

typeof(PluginModel) gives {Name = "PluginModel" FullName = "Cockpit.RUN.Common.PluginModel"}

viewmodel.GetType().BaseType gives {Name = "PluginModel" FullName = "Cockpit.RUN.ViewModels.PluginModel"}
1
How do you make sure that the assembly, where the class PluginModel is, is the same for the executable as well as the one referenced by the "Cockpit.RUN.ViewModels.dll"? Check with stackoverflow.com/questions/458362/… which assemblies are loaded and in which version. It might be possible that the PluginModel class used by your "Cockpit.RUN.ViewModels.dll" file isn't/wasn't the same as the PluginModel reference in your executable. Compare viewmodel.GetType().BaseType against typeof(PluginModel); to see if they reference the same class. - Progman
@progman i have just one class definition of PluginModel, and this dll is referenced in other projects of the solution. and i have checked loaded assemblies i dont see problem... - Frenchy
Use interfaces? - ikwillem
@progman i have edited the question and show the result of TypeOf and GetType() - Frenchy
@Frenchy Can you check with "Make object ID" in the debug session or by other means that the two types returned are in fact the actual same System.Type object and not just different type objects, which reference two different type instance of a class named "PluginModel" by random? Also check additional debug information in the debug session about these two types to verify that they reference the same class from the same assembly in the same version and build time. Please include the full error message/exception you get when you try to cast your object with var w = (PluginModel)viewmodel;. - Progman

1 Answers

1
votes

The interfaces (dll):

public interface IPlugin
{
    ICallbacks Callbacks { get; set; }

    string NameUC { get; set; }

    double Width { get; set; }
    double Height { get; set; }
    double Left { get; set; }
    double Top { get; set; }

    double ZoomFactorFromPluginModel { get; set; }
}
public interface ICallbacks
{
    void SomeCallback();
}

Your implementation:

public class PushButton_ViewModel : PluginModel, IPlugin
{
    // How was this going to work? Who makes the instance?
    //public PushButton_ViewModel(params object[] settings)
    //{
    //    NameUC = (string)settings[2];
    //    Layout = new LayoutPropertyViewModel();
    //}

    public ICallbacks Callbacks { get; set; }

    public override double Left
    {
        get => Layout.UCLeft;
        set => Layout.UCLeft = value;
    }
    public override double Top
    {
        get => Layout.UCTop;
        set => Layout.UCTop = value;
    }
    public override double Width
    {
        get => Layout.Width;
        set => Layout.Width = value;
    }
    public override double Height
    {
        get => Layout.Height;
        set => Layout.Height = value;
    }                         
}

Then initialize it

 Type t = Type.GetType("PushButton_ViewModel");
 object obj = FormatterServices.GetUninitializedObject(t);
 IPlugin instance = obj as IPlugin;
 instance.NameUC = "the name";
 instance.Callbacks = new CallbacksModel();