Как установить двоичный атрибут при использовании класса расширения 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;
}
}