如何使用AccountManagement扩展类时设置一个二进制属性?如何使用、属性、AccountManagement

2023-09-07 14:42:19 作者:三岁就很酷

我使用的是自定义类暴露在Active Directory 一些自定义模式。我存储二进制数据,每个项目要求这些数据必须存储在AD,我不能使用外挂(我想如果我可以)。

I am using a custom class to expose some custom schema in Active Directory. I am storing a binary blob, per the project requirements this data must be stored in the AD, I can not use a external store (I would if I could).

当我创建的用户它存储了一滴精。我还可以检索BLOB背出来也没关系,让我的所有数据。问题是,如果我需要更新的价值,我收到错误

When I create the user it stores the blob fine. I also can retrieve the blob back out fine too and get all my data. The issue is if I need to update the value and I am getting errors

小例子程序:

using System;
using System.DirectoryServices.AccountManagement;

namespace SandboxConsole40
{
    class Program
    {
        static void Main(string[] args)
        {
            using(var context = new PrincipalContext(ContextType.Domain))
            {
                using (var clear = ExamplePrincipal.FindByIdentity(context, "example"))
                {
                    if (clear != null)
                        clear.Delete();
                }

                using (var create = new ExamplePrincipal(context, "example", "Password1", false))
                {
                    create.Save();
                }

                using (var set = ExamplePrincipal.FindByIdentity(context, "example"))
                {
                    set.BlobData = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF }; //This fails with method 2.
                    set.Save();
                }

                using (var lookup = ExamplePrincipal.FindByIdentity(context, "example"))
                {
                    Console.WriteLine(BitConverter.ToString(lookup.BlobData));
                }

                using (var update = ExamplePrincipal.FindByIdentity(context, "example"))
                {
                    update.BlobData = new byte[] { 0x12, 0x34, 0x56, 0x67 };
                    update.Save(); //This save fails with method 1.
                }
            }

            Console.WriteLine("Done");
            Console.ReadLine();
        }

        [DirectoryObjectClass("user")]
        [DirectoryRdnPrefix("CN")]
        class ExamplePrincipal : UserPrincipal
        {
            public ExamplePrincipal(PrincipalContext context) : base(context) { }

            public ExamplePrincipal(PrincipalContext context, string samAccountName, string password, bool enabled)
                : base(context, samAccountName, password, enabled) { }

            public static new ExamplePrincipal FindByIdentity(PrincipalContext context, string identityValue)
            {
                return (ExamplePrincipal)FindByIdentityWithType(context, typeof(ExamplePrincipal), identityValue);
            }

            [DirectoryProperty("vwBlobData")]
            public byte[] BlobData
            {
                get
                {
                    if (ExtensionGet("vwBlobData").Length != 1)
                        return null;

                    return (byte[])ExtensionGet("vwBlobData")[0];
                }
                set
                {
                    //method 1
                    this.ExtensionSet("vwBlobData",  value );

                    //method 2
                    //this.ExtensionSet("vwBlobData", new object[] { value});
                }
            }
        }
    }
}

如果我使用方法1我上 update.Save()运行下面的异常

If I use method 1 I get the following exception on the update.Save() operation


System.DirectoryServices.AccountManagement.PrincipalOperationException was unhandled
  HResult=-2146233087
  Message=The specified directory service attribute or value already exists.

  Source=System.DirectoryServices.AccountManagement
  ErrorCode=-2147016691
  StackTrace:
       //Snip
  InnerException: System.DirectoryServices.DirectoryServicesCOMException
       HResult=-2147016691
       Message=The specified directory service attribute or value already exists.

       Source=System.DirectoryServices
       ErrorCode=-2147016691
       ExtendedError=8321
       ExtendedErrorMessage=00002081: AtrErr: DSID-030F154F, #1:
    0: 00002081: DSID-030F154F, problem 1006 (ATT_OR_VALUE_EXISTS), data 0, Att 82818fec (vwBlobData)

       StackTrace:
            //Snip
       InnerException: 

如果我使用方法2我得到一个例外,在从 this.ExtensionSet 呼叫 set.BlobData 调用。

If I use method 2 I get a exception on from the this.ExtensionSet call from the set.BlobData call.


System.ArgumentException was unhandled
  HResult=-2147024809
  Message=Collections whose elements are another collection cannot be set by ExtensionClasses.
  Source=System.DirectoryServices.AccountManagement
  StackTrace:
      //Snip
  InnerException: 

总结:我可以设置的值,如果当前没有设置,但如果我想覆盖现有的值,我得到一个错误

In summary: I can set the value if it is currently not set, but if I want to overwrite a existing value I am getting a error.

推荐答案

我发现周围的工作,将其值设置为第一个空就没有再抛出异常。

I found a work around, by setting the value to null first it no-longer throws the exception.

using (var update = ExamplePrincipal.FindByIdentity(context, "example"))
{
    update.BlobData = null;
    update.Save();
    update.BlobData = new byte[] { 0x12, 0x34, 0x56, 0x67 };
    update.Save(); //No longer fails with method 1.
}

我要离开这个问题打开了一下,看看有没有人可以回答,如果有一个正确的方式做到这一点。

I am leaving the question open for a bit to see if anyone else can answer if there is a "proper" way to do this.

找了第二个变通,不需要强制保存。

Found a 2nd work around that does not require forcing a save.

[DirectoryProperty("vwBlobData")]
public byte[] BlobData
{
    get
    {
        if (ExtensionGet("vwBlobData").Length != 1)
            return null;

        return (byte[])ExtensionGet("vwBlobData")[0];
    }
    set
    {
        ((DirectoryEntry)this.GetUnderlyingObject())
                             .Properties["vwBlobData"].Value = value;
    }
}

通过直接浇铸到底层的对象,你可以直接设置的值。我检查使用ILSpy和处理AccountManagement包装处置的基本对象,所以没有的Dispose()是必需的 GetUnderlyingObject()调用。

最佳解决方案

我发现了第二个工作大约需要持久化工作对象的,所以我做了一个两全其美的办法。这工作时,你还没有持久化对象,当对象为空,而当对象有一个值了。

I found out the 2nd work around required the object to be persisted to work, so I made a best of both worlds approach. This works when you have not yet persisted the object, when the object is null, and when the object has a value already.

[DirectoryProperty("vwBlobData")]
public byte[] BlobData
{
    get
    {
        if (ExtensionGet("vwBlobData").Length != 1)
            return null;

        return (byte[])ExtensionGet("vwBlobData")[0];
    }
    set
    {
        if(ExtensionGet("vwBlobData").Length == 0)
            this.ExtensionSet("vwBlobData", data); 
        else
            ((DirectoryEntry)this.GetUnderlyingObject())
                                 .Properties["vwBlobData"].Value = data;
    }
}