避免使用Enums进行抽象和依赖注入,在概念

时间:2017-11-23 01:17:09

标签: c# wpf enums

所以现在我正在尝试设计一个新的租用程序,该程序授予对活动目录组的访问权限,生成包含其信息和位置的文档。

现在我正在使用枚举执行此操作,并使用switch语句设置ViewModel上的详细信息,如下所示:

                case CaneRidgeSettings.Departments.SCSC:
                Model.ScannerFolder = @"scan1\Supply Chain Service Center\" + Model.UserId;
                Model.ExtensionRanges = "list station 8000 to-ext 8349";
                Model.AdministrativeAssistant = Loader.SCSCAdminAssistant;
                Model.DuoCode = "Franklin TN - 8175";
                Model.PrinterSelectedIndex = (int)CaneRidgeSettings.PrinterGroups.Cane_Ridge_5th_Floor_West;
                return await find.FindNextComputer("800SCSC");

我对此设计的问题是,如果我在此建筑物中添加更多部门,我必须手动更新此开关。所以我尝试了一些这样的东西,比如字典,但它似乎没有很好地绑定到一个组合框(即使在实现我自己的INotifyCollectionChanged时)。

所以我创建了一个包含这些信息的接口,为了简单和长度,我们只想说接口这样做:

  public interface IDepartmentInfo
{
    string DepartmentName { get; }
    List<string> ActiveDirectoryGroups { get; }
    string AdministrativeAssistant { get; }
    string Floor { get; }
}

然后我创建了一个实现此接口的新类

public class SCSC : IDepartmentInfo
{
    public string DepartmentName { get; } = "Shared Services";
    public List<string> ActiveDirectoryGroups { get; } = new List<string>() {"Example_AD_GRP","Domain_Users"};
    public string AdministrativeAssistant { get; } = "Lisa_Smith@outlook.com";
    public string Floor { get; } = "5th Floor East";

    public override string ToString() => DepartmentName;
}

然后,在我的主要建筑类中,我有一个可观察的集合,需要一个IDepartmentInfo并初始化这些部门

   public class CaneRidgeBuilding : IBuilding
{
    public ObservableCollection<IDepartmentInfo> Departments { get; set; } = new ObservableCollection<IDepartmentInfo>() {new SCSC(), new ARS()};

    public override string ToString()
    {
        return "CaneRidge";
    }
}

在我的视图模型上,我实现了一些属性,主要是BuildingSelectedIndex和DepartmentSelectedIndex。

我还有一个IDepartmentInfo属性,通知它何时被更改,因为它在我的UI上被数据绑定到多个标签。

public class MainWindowViewModel : BindableBase
{

    public ObservableCollection<IBuilding> Buildings { get; set; } = new ObservableCollection<IBuilding>() { new CaneRidgeBuilding() };
    private ObservableCollection<IDepartmentInfo> _departmentInfos =  new ObservableCollection<IDepartmentInfo>();
    public ObservableCollection<IDepartmentInfo> DepartmentInfos
    {
        get { return _departmentInfos; }
        set { SetProperty(ref _departmentInfos, value); }
    }

    private int _buildingIndex = -1;
    public int BuildingIndex
    {
        get { return _buildingIndex; }
        set
        {
            SetProperty(ref _buildingIndex, value);
            SetDepartments();
        }
    }

    private void SetDepartments()
    {
        if (BuildingIndex != -1)
            DepartmentInfos = Buildings[BuildingIndex].Departments;
    }


    private int _departmentIndex = -1;
    public int DepartmentIndex
    {
        get { return _departmentIndex; }
        set
        {
            SetProperty(ref _departmentIndex, value);
            LoadDepartmentSettings();
        }
    }


    private IDepartmentInfo _departmentInformation;
    public IDepartmentInfo DepartmentInformation
    {
        get { return _departmentInformation; }
        set { SetProperty(ref _departmentInformation, value); }
    }

    private void LoadDepartmentSettings()
    {
        if (DepartmentIndex != -1)
            DepartmentInformation = DepartmentInfos[DepartmentIndex];
    }

    private string _title = "Prism Application";
    public string Title
    {
        get { return _title; }
        set { SetProperty(ref _title, value); }
    }

    public MainWindowViewModel()
    {

    }
}

它完全按我想要的方式工作,但是我现在遇到的问题是如何处理依赖注入?如果我有10个部门实现IDepartmentInfo,我怎么能将它传递给一个可观察的集合呢?

因为我介绍一个新建筑的那一刻,如果我告诉Unity解决所有IDepartmentInfos,那么即使它不属于CaneRidge,我也会得到每一个部门。

如果我将部门拆分到每个建筑物,那么我会遇到一些问题,我无法轻松地将部门加载到ViewModel中,因为它需要一个IDepartmentInfo集合。如果我将它限制在一种类型的集合中,那么它将无法工作。

我是不是太复杂了?

2 个答案:

答案 0 :(得分:0)

我能够弄清楚如何注入不同的建筑物和部门,可能不是最好的方式

编辑:更新它以使用反射来减少维护

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        Container.RegisterTypes(AllClasses.FromLoadedAssemblies()
           .Where(type => typeof(IDepartment).IsAssignableFrom(type)), WithMappings.FromAllInterfaces, WithName.TypeName, WithLifetime.None);

        ObservableCollection<IBuilding> Buildings = new ObservableCollection<IBuilding>()
        {
            Container.Resolve<Building1>(new ParameterOverride("departments",GetDepartmentCollection("Building1"))),
            Container.Resolve<Building2>(new ParameterOverride("departments",GetDepartmentCollection("Building2")))

        };

        Container.RegisterInstance(typeof(ObservableCollection<IBuilding>), Buildings,
            new ExternallyControlledLifetimeManager());



    }


    private ObservableCollection<IDepartment> GetDepartmentCollection(string buildingName)
    {
        var departments = new List<IDepartment>();
        foreach (var registration in Container.Registrations.Where( s => s.MappedToType.Namespace.Contains(buildingName)))
        {
            departments.Add((IDepartment)Container.Resolve(registration.MappedToType));
        }
        return new ObservableCollection<IDepartment>(departments);
    }

现在我可以完全消除枚举,并且可以在将来扩展而不会破坏任何代码或要求我更改任何内容。

答案 1 :(得分:0)

这是一个想法。

自定义属性

引入BuilingAttribute,以便每个IDepartmentInfo实施可以声明它所属的建筑物的Type(如果一个部门可以属于多个建筑物,则允许多个,我知道它可以'吨)。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class BuildingAttribute : Attribute
{
    public Type BuildingType { get; private set; }

    public BuildingAttribute(Type buildingType)
    {
        this.BuildingType = buildingType;
    }
}

DepartmentInfo Collection Factory

一个知道如何为每个建筑DepartmentInfo创建Type集合的界面。

public interface IDepartmentInfoCollectionFactory
{
    void RegisterDepartment<T>(Func<IDepartmentInfo> departmentCreator) where T : class, IBuilding;

    ObservableCollection<IDepartmentInfo> GetDepartments<T>() where T : class, IBuilding;
}

实施(将注册为单身人士)。

public class DepartmentInfoCollectionFactory : IDepartmentInfoCollectionFactory
{
    private readonly Dictionary<Type, List<Func<IDepartmentInfo>>> departmentCreators =
        new Dictionary<Type, List<Func<IDepartmentInfo>>>();

    void IDepartmentInfoCollectionFactory.RegisterDepartment<T>(Func<IDepartmentInfo> departmentCreator)
    {
        Type buildingType = typeof(T);

        if (!this.departmentCreators.ContainsKey(buildingType))
            this.departmentCreators.Add(buildingType, new List<Func<IDepartmentInfo>>());

        if (!this.departmentCreators[buildingType].Contains(departmentCreator))
            this.departmentCreators[buildingType].Add(departmentCreator);
    }

    ObservableCollection<IDepartmentInfo> IDepartmentInfoCollectionFactory.GetDepartments<T>()
    {
        Type buildingType = typeof(T);

        if (!this.departmentCreators.ContainsKey(buildingType))
            throw new InvalidOperationException(
                string.Format("No departments have been registered for {0}.", buildingType.ToString()));

        ObservableCollection<IDepartmentInfo> departmentInfos = new ObservableCollection<IDepartmentInfo>();

        foreach(Func<IDepartmentInfo> creator in this.departmentCreators[buildingType])
        {
            departmentInfos.Add(creator());
        }

        return departmentInfos;
    }
}

配置工厂,因此它知道如何创建IDepartmentInfo集合。

protected override void ConfigureContainer()
{
    Container.RegisterType<IDepartmentInfoCollectionFactory, DepartmentInfoCollectionFactory>(
            new ContainerControlledLifetimeManager());

    this.ConfigureDepartmentInfoCollectionFactory(Container.Resolve<IDepartmentInfoCollectionFactory>());
}

private void ConfigureDepartmentInfoCollectionFactory(IDepartmentInfoCollectionFactory factory)
{
    // Types implementing IDepartmentInfo
    var deptInfoTypes = AppDomain.CurrentDomain
                                    .GetAssemblies()
                                    .SelectMany(s => s.GetTypes())
                                    .Where(t => typeof(IDepartmentInfo).IsAssignableFrom(t)  && !t.IsInterface);

    foreach(Type type in deptInfoTypes)
    {
        // Get collection of BuildingAttribute for the type
        var buildingAttributes = type.GetCustomAttributes(typeof(BuildingAttribute), false)
                                        .OfType<BuildingAttribute>();

        if (buildingAttributes.Count() < 1)
            throw new InvalidOperationException(
                string.Format("The type {0} didn't declare BuildingArgument.", type.ToString()));

        var buildingType = buildingAttributes.First().BuildingType;

        if (buildingType == null || !buildingType.GetInterfaces().Contains(typeof(IBuilding)))
            throw new InvalidOperationException(
                string.Format("{0}: BuildingType is not an IBuilding.", type.ToString()));

        var registerMethod = typeof(IDepartmentInfoCollectionFactory).GetMethod("RegisterDepartment")
                                                                .MakeGenericMethod(new Type[] { buildingType });

        registerMethod.Invoke(factory, new object[]
        {
            new Func<IDepartmentInfo>(() => (IDepartmentInfo)Container.Resolve(type))
        });
    }
}

注入工厂。

public class FooBuilding : IBuilding
{
    private IDepartmentInfoCollectionFactory factory;
    private readonly ObservableCollection<IDepartmentInfo> departmentInfos;

    public string Name { get; } = "FooBuilding";

    public ObservableCollection<IDepartmentInfo> DepartmentInfos
    {
        get { return this.departmentInfos; }
    }

    public FooBuilding(IDepartmentInfoCollectionFactory factory)
    {
        this.factory = factory;
        this.departmentInfos = factory.GetDepartments<FooBuilding>();
    }
}

添加新部门

它不需要任何编辑,只需使用属性创建新类。

[Building(typeof(FooBuilding))]
public class BarDepartment : IDepartmentInfo
{
    public string Name { get; } = "Bar department";
}