【问题标题】:Create an instance of DirectoryEntry for use in test创建用于测试的 DirectoryEntry 实例
【发布时间】:2011-05-11 14:45:46
【问题描述】:

我正在尝试创建一个 DirectoryEntry 实例,以便我可以使用它来测试一些将通过 DirectoryEntry 传递的代码。然而,尽管进行了很多尝试,我还是找不到一种方法来实例化 DE 并将其初始化为 PropertyCollection。

我有以下代码,它是从 another answer on SO 获取和修改的,它执行相同的过程,但用于 SearchResult 对象。似乎 Add 方法已被完全禁用,我找不到在 PropertyCollection 上调用构造函数来传递某些属性的方法。

using System.Collections;
using System.DirectoryServices;
using System.Globalization;
using System.Reflection;
using System.Runtime.Serialization;

public static class DirectoryEntryFactory
{
    const BindingFlags nonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
    const BindingFlags publicInstance = BindingFlags.Public | BindingFlags.Instance;

    public static DirectoryEntry Construct<T>(T anonInstance)
    {
        var e = GetUninitializedObject<DirectoryEntry>();

        SetPropertiesField(e);

        var dictionary = (IDictionary)e.Properties;
        var type = typeof(T);
        var propertyInfos = type.GetProperties(publicInstance);

        foreach (var propertyInfo in propertyInfos)
        {
            var value = propertyInfo.GetValue(anonInstance, null);
            var valueCollection = GetUninitializedObject<PropertyValueCollection>();
            var innerList = GetInnerList(valueCollection);
            innerList.Add(value);

            var lowerKey = propertyInfo.Name.ToLower(CultureInfo.InvariantCulture);

            // These both throw exceptions saying you can't add to a PropertyCollection
            //(typeof(PropertyCollection)).InvokeMember("System.Collections.IDictionary.Add", nonPublicInstance | BindingFlags.InvokeMethod, null, dictionary, new object[] { propertyInfo.Name, value });
            //dictionary.Add(lowerKey, propertyCollection);
        }

        return e;
    }

    private static ArrayList GetInnerList(object propertyCollection)
    {
        var propertyInfo = typeof(PropertyValueCollection).GetProperty("InnerList", nonPublicInstance);
        return (ArrayList)propertyInfo.GetValue(propertyCollection, null);
    }

    private static void SetPropertiesField(DirectoryEntry e)
    {
        var propertiesField = typeof(DirectoryEntry).GetField("propertyCollection", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        propertiesField.SetValue(e, GetUninitializedObject<PropertyCollection>());
    }

    private static T GetUninitializedObject<T>()
    {
        return (T)FormatterServices.GetUninitializedObject(typeof(T));
    }
}

使用目的是

DirectoryEntry e = DirectoryEntryFactory.Construct(new { attr1 = "Hello", attr2 = "World"});

我希望我错过了一些东西,因为我对在愤怒中使用反射还很陌生。

【问题讨论】:

  • Adam,您真的设法通过有效的 Properties 实现来实现这一点吗?我有同样的问题here

标签: c# reflection active-directory


【解决方案1】:

我不熟悉 DirectoryEntry 本身,但我非常喜欢用于测试的适配器模式。不得不做这样的事情很烦人,但是代码本身很简单,并且可以将类放在项目文件夹中以将它们隐藏在您的主项目之外。

例如,我有一个 FileInfoAdapter 和 DirectoryInfoAdapter 来包装那些接触文件系统的类。

文件信息适配器:

public class FileInfoAdapter : IFileInfo
{
    private readonly FileSystemInfo _fi;

    public FileInfoAdapter(string fileName)
        : this(new FileInfo(fileName))
    { }

    public FileInfoAdapter(FileSystemInfo fi)
    {
        _fi = fi;
    }

    public string Name { get { return _fi.Name; } }
    public string FullName { get { return _fi.FullName; } }
    public bool Exists { get { return _fi.Exists; } }
}

【讨论】:

  • 我曾考虑包装对象,但希望有另一个答案。我想我现在可能会放弃并把它包装起来,而不是等待一个完美的解决方案——但知道是否有办法正确地做到这一点会很有趣。
  • @Adam - 对于不实现接口的库代码,我认为适配器没有任何问题。我经常使用这种模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-15
  • 2023-03-09
  • 1970-01-01
  • 2015-07-26
  • 2016-04-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多