使用基于类型(或上一个ViewModel)的现有委托工厂获取实例

本文关键字:实例 工厂 获取 上一个 于类型 类型 ViewModel | 更新日期: 2023-09-27 17:59:16

基于此页面,我们创建了一个包含三个步骤的向导。一切都很好,但我们对链接中给出的代码有一个问题,那就是它如何创建下一步实例(从链接粘贴的副本):

protected override IScreen DetermineNextItemToActivate(IList<IScreen> list, int lastIndex)
{
    var theScreenThatJustClosed = list[lastIndex] as BaseViewModel;
    var state = theScreenThatJustClosed.WorkflowState;
    var nextScreenType = TransitionMap.GetNextScreenType(theScreenThatJustClosed);
    var nextScreen = Activator.CreateInstance(nextScreenType, state);
    return nextScreen as IScreen;
}

目前,在我们的项目中是这样的:

protected override IWizardScreen DetermineNextItemToActivate(IList<IWizardScreen> list, int lastIndex)
{
    var theScreenThatJustClosed = list[lastIndex];
    if (theScreenThatJustClosed == null) throw new Exception("Expecting a screen here!");
    if (theScreenThatJustClosed.NextTransition == WizardTransition.Done)
    {
        TryClose(); // Close the entire Wizard
    }
    var state = theScreenThatJustClosed.WizardAggregateState;
    var nextScreenType = _map.GetNextScreenType(theScreenThatJustClosed);
    if (nextScreenType == null) return null;
    // TODO: CreateInstance requires all constructors for each WizardStep, even if they aren't needed. This should be different!
    var nextScreen = Activator.CreateInstance(nextScreenType, state, _applicationService, _wfdRegisterInstellingLookUp,
        _adresService, _userService, _documentStore, _windowManager, _fileStore, _fileUploadService, _dialogService,
        _eventAggregator, _aanstellingViewModelFactory);
    return nextScreen as IWizardScreen;
}

正如你所看到的,我们在一些步骤中需要相当多的参数。在步骤1中,我们只需要两个,但由于Activator.CreateInstance(nextScreenType, state, ...);,我们仍然需要通过所有这些。

我想要的是使用delegate Factory。我们在项目中的更多地方使用它们,并让AutoFac处理其余的参数。对于三个步骤中的每一个,我们只需要一个使用statedelegate Factory

因为这三个都使用相同的delegate Factory,只有state,所以我把这个工厂放在了它们的基类中:

public delegate WizardBaseViewModel<TViewModel> Factory(AggregateState state);

我想如何更改DetermineNextItemToActivate方法:

protected override IWizardScreen DetermineNextItemToActivate(IList<IWizardScreen> list, int lastIndex)
{
    var theScreenThatJustClosed = list[lastIndex];
    if (theScreenThatJustClosed == null) throw new Exception("Expecting a screen here!");
    if (theScreenThatJustClosed.NextTransition == WizardTransition.Done)
    {
        TryClose(); // Close the entire Wizard
    }
    return _map.GetNextScreenFactoryInstance(state);
}

但现在我一直在制作GetNextScreenFactoryInstance方法:

public IWizardScreen GetNextScreenFactoryInstance(IWizardScreen screenThatClosed)
{
    var state = screenThatClosed.WizardAggregateState;
    // This is where I'm stuck. How do I get the instance using the Factory, when I only know the previous ViewModel
    // ** Half-Pseudocode
    var nextType = GetNextScreenType(screenThatClosed);
    var viewModelFactory = get delegate factory based on type?;
    var invokedInstance = viewModelFactory.Invoke(state);
    // **
    return invokedInstance as IWizardScreen;
}

您可以随意更改GetNextScreenFactoryInstance。只要我能在地图中的上一个步骤的基础上获得下一个步骤ViewModel。


注意:其他相关代码,可以在链接中找到,但我也会把它发布在这里,把它放在一起:

WizardTransitionMap(唯一的变化是它不再是Singleton,所以我们可以实例化自己的地图):

public class WizardTransitionMap : Dictionary<Type, Dictionary<WizardTransition, Type>>
{
    public void Add<TIdentity, TResponse>(WizardTransition transition) 
        where TIdentity : IScreen 
        where TResponse : IScreen
    {
        if (!ContainsKey(typeof(TIdentity)))
        {
            Add(typeof(TIdentity), new Dictionary<WizardTransition, Type> { { transition, typeof(TResponse) } });
        }
        else
        {
            this[typeof(TIdentity)].Add(transition, typeof(TResponse));
        }
    }
    public Type GetNextScreenType(IWizardScreen screenThatClosed)
    {
        var identity = screenThatClosed.GetType();
        var transition = screenThatClosed.NextTransition;
        if (!transition.HasValue) return null;
        if (!ContainsKey(identity))
        {
            throw new InvalidOperationException(String.Format("There are no states transitions defined for state {0}", identity));
        }
        if (!this[identity].ContainsKey(transition.Value))
        {
            throw new InvalidOperationException(String.Format("There is no response setup for transition {0} from screen {1}", transition, identity));
        }
        return this[identity][transition.Value];
    }
}

我们的InitializeMap方法:

protected override void InitializeMap()
{
    _map = new WizardTransitionMap();
    _map.Add<ScreenOneViewModel, ScreenTwoViewModel>(WizardTransition.Next);
    _map.Add<ScreenTwoViewModel, ScreenOneViewModel>(WizardTransition.Previous);
    _map.Add<ScreenTwoViewModel, ScreenThreeViewModel>(WizardTransition.Next);
    _map.Add<ScreenThreeViewModel, ScreenTwoViewModel>(WizardTransition.Previous);
    _map.Add<ScreenThreeViewModel, ScreenThreeViewModel>(WizardTransition.Done);
}

使用基于类型(或上一个ViewModel)的现有委托工厂获取实例

我们已经更改了代码:

WizardTransitionMap现在接受代理。此外,我们现在不再根据WizardTransition枚举值(Next、Previous等)检索类型,而是根据下一个类型检索Factory调用(因此内部Dictionary被颠倒)。因此,这是我们当前的WizardTransitionMap:

using System;
using System.Collections.Generic;
namespace NatWa.MidOffice.CustomControls.Wizard
{
    public class WizardTransitionMap : Dictionary<Type, Dictionary<Type, Delegate>>
    {
        public void Add<TCurrentScreenType, TNextScreenType>(Delegate delegateFactory)
        {
            if (!ContainsKey(typeof(TCurrentScreenType)))
            {
                Add(typeof(TCurrentScreenType), new Dictionary<Type, Delegate> { { typeof(TNextScreenType), delegateFactory } });
            }
            else
            {
                this[typeof(TCurrentScreenType)].Add(typeof(TNextScreenType), delegateFactory);
            }
        }
        public IWizardScreen GetNextScreen(IWizardScreen screenThatClosed)
        {
            var identity = screenThatClosed.GetType();
            var state = screenThatClosed.State;
            var transition = screenThatClosed.NextScreenType;
            if (!ContainsKey(identity))
            {
                throw new InvalidOperationException(String.Format("There are no states transitions defined for state {0}", identity));
            }
            if (!this[identity].ContainsKey(transition))
            {
                throw new InvalidOperationException(String.Format("There is no response setup for transition {0} from screen {1}", transition, identity));
            }
            if (this[identity][transition] == null)
                return null;
            return (IWizardScreen)this[identity][transition].DynamicInvoke(state);
        }
    }
}

我们的InitializeMap现在改为:

protected override void InitializeMap()
{
    _map = new WizardTransitionMap();
    _map.Add<ScreenOneViewModel, ScreenTwoViewModel>(_screenTwoFactory);
    _map.Add<ScreenTwoViewModel, ScreenOneViewModel>(_screenOneFactory);
    _map.Add<ScreenTwoViewModel, ScreenThreeViewModel>(_screenThreeFactory);
    _map.Add<ScreenThreeViewModel, ScreenTwoViewModel>(_screenTwoFactory);
    _map.Add<ScreenThreeViewModel, ScreenThreeViewModel>(null);
}

以及我们的DetemineTexttemToActivate方法:

protected override IWizardScreen DetermineNextItemToActivate(IList<IWizardScreen> list, int previousIndex)
{
    var theScreenThatJustClosed = list[previousIndex];
    if (theScreenThatJustClosed == null) throw new Exception("Expecting a screen here!");
    var nextScreen = _map.GetNextScreen(theScreenThatJustClosed);
    if (nextScreen == null)
    {
        TryClose();
        return ActiveItem; // Can't return null here, because Caliburn's Conductor will automatically get into this method again with a retry
    }
    return nextScreen;
}

我们还删除了整个WizardBaseViewModel,只让每个步骤ViewModel实现IWizardScreen:

public interface IWizardScreen : IScreen
{
    AggregateState State { get; }
    Type NextScreenType { get; }
    void Next();
    void Previous();
}

在我们的ScreenOneViewModel中使用以下实现:

public AggregateState State { get { return _state; }  }
public Type NextScreenType { get; private set; }
public void Next()
{
    if (!IsValid()) return;
    NextScreenType = typeof(ScreenTwoViewModel);
    TryClose();
}
public void Previous()
{
    throw new NotImplementedException(); // Isn't needed in first screen, because we have no previous
}

以及我们的ScreenThreeViewModel中的以下实现:

public AggregateState State { get { return _state; } }
public Type NextScreenType { get; private set; }
public void Next()
{
    NextScreenType = typeof(ScreenThreeViewModel); // Own type, because we have no next
    TryClose();
}
public void Previous()
{
    NextScreenType = typeof(ScreenTwoViewModel);
    TryClose();
}

每个步骤视图模型都有自己的delegate Factory,就像ScreenTwoViewModel:

public delegate ScreenTwoViewModel Factory(AggregateState state);