3

カスタムクラスを使用して、ActiveDirectoryでカスタムスキーマを公開しています。私はバイナリブロブを保存しています。プロジェクトの要件に従って、このデータはADに保存する必要があり、外部ストアを使用することはできません(可能であれば)。

ユーザーを作成すると、blobが正常に保存されます。また、blobを正常に取得して、すべてのデータを取得することもできます。問題は、値を更新する必要があり、エラーが発生する場合です

小さなサンプルプログラム:

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
  スタックトレース:
       //をちょきちょきと切る
  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、Att 82818fec(vwBlobData)

       スタックトレース:
            //をちょきちょきと切る
       InnerException:

this.ExtensionSetメソッド2を使用すると、呼び出しからの呼び出しから例外が発生しset.BlobDataます。

System.ArgumentExceptionが処理されませんでした
  HResult =-2147024809
  Message =要素が別のコレクションであるコレクションは、ExtensionClassesでは設定できません。
  Source = System.DirectoryServices.AccountManagement
  スタックトレース:
      //をちょきちょきと切る
  InnerException:


要約:現在設定されていない場合は値を設定できますが、既存の値を上書きしたい場合はエラーが発生します。

4

1 に答える 1

3

最初に値をnullに設定することで、回避策を見つけました。これ以上、例外はスローされません。

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()


最良の解決策

2番目の回避策では、オブジェクトを永続的に動作させる必要があることがわかったので、両方の世界のアプローチを最大限に活用しました。これは、オブジェクトをまだ永続化していない場合、オブジェクトが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;
    }
}
于 2013-01-28T16:55:27.417 に答える