Как установить двоичный атрибут при использовании класса расширения AccountManagement?

Я использую пользовательский класс для предоставления некоторых пользовательских схем в Active Directory. Я храню двоичный двоичный объект, в соответствии с требованиями проекта эти данные должны храниться в AD, я не могу использовать внешнее хранилище (я бы, если бы мог).

Когда я создаю пользователя, он прекрасно сохраняет блоб. Я также могу получить блоб обратно и получить все свои данные. Проблема в том, если мне нужно обновить значение, и я получаю ошибки

Небольшой пример программы:

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() операция

System.DirectoryServices.AccountManagement.PrincipalOperationException не обработано. HResult=-2146233087 Сообщение = указанный атрибут или значение службы каталогов уже существует.

  Source=System.DirectoryServices.AccountManagement
  ErrorCode=-2147016691
  StackTrace:
       //Snip
  InnerException: System.DirectoryServices.DirectoryServicesCOMException
       HResult=-2147016691 Сообщение = указанный атрибут или значение службы каталогов уже существует.

       Source=System.DirectoryServices
       ErrorCode=-2147016691
       ExtendedError=8321
       ExtendedErrorMessage=00002081: AtrErr: DSID-030F154F, #1:
    0: 00002081: DSID-030F154F, проблема 1006 (ATT_OR_VALUE_EXISTS), резервное копирование данных 0 0, резервное копирование данных 0 (0), резервное копирование данных 0 (0), резервное копирование данных 0 0 (0) / Snip InnerException: 

Если я использую метод 2, я получаю исключение от this.ExtensionSet звонок из set.BlobData вызов.

System.ArgumentException не обработан
  HResult=-2147024809
  Message= Коллекции, элементы которых являются другой коллекцией, не могут быть установлены ExtensionClasses.
  Источник = System.DirectoryServices.AccountManagement
  Трассировки стека:
      // Снип
  InnerException: 


В итоге: я могу установить значение, если оно в данный момент не установлено, но если я хочу перезаписать существующее значение, я получаю сообщение об ошибке.

1 ответ

Решение

Я нашел обходной путь, установив значение равным нулю, во-первых, оно больше не выдает исключение.

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.
}

Я оставляю вопрос открытым, чтобы посмотреть, сможет ли кто-нибудь ответить, если есть "правильный" способ сделать это.


Нашел 2-е обходное решение, которое не требует принудительного сохранения.

[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() вызов.


ЛУЧШЕЕ РЕШЕНИЕ

Я обнаружил, что вторая работа требует, чтобы объект продолжал работать, поэтому я выбрал лучший из двух подходов. Это работает, когда вы еще не сохранили объект, когда объект имеет значение null и когда объект уже имеет значение.

[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;
    }
}
Другие вопросы по тегам