How to set a binary attribute when using a AccountManagement Extension Class?(使用 AccountManagement 扩展类时如何设置二进制属性?)
问题描述
我正在使用 自定义类以在 Active Directory 中公开一些自定义架构.我正在存储一个二进制 blob,根据项目要求,此数据必须存储在 AD 中,我不能使用外部存储(如果可以的话,我会这样做).
当我创建用户时,它会很好地存储 blob.我也可以很好地检索 blob 并获取我的所有数据.问题是我是否需要更新值并且出现错误
小示例程序:
使用系统;使用 System.DirectoryServices.AccountManagement;命名空间 SandboxConsole40{课程计划{静态无效主(字符串 [] args){使用(var context = new PrincipalContext(ContextType.Domain)){使用 (var clear = ExamplePrincipal.FindByIdentity(context, "example")){如果(清除!= null)清除.删除();}使用 (var create = new ExamplePrincipal(context, "example", "Password1", false)){创建.保存();}使用 (var set = ExamplePrincipal.FindByIdentity(context, "example")){set.BlobData = 新字节 [] { 0xDE, 0xAD, 0xBE, 0xEF };//这与方法2失败.设置.保存();}使用 (var lookup = ExamplePrincipal.FindByIdentity(context, "example")){Console.WriteLine(BitConverter.ToString(lookup.BlobData));}使用 (var update = ExamplePrincipal.FindByIdentity(context, "example")){update.BlobData = 新字节[] { 0x12, 0x34, 0x56, 0x67 };更新.保存();//此保存失败,方法 1.}}Console.WriteLine("完成");Console.ReadLine();}[目录对象类(用户")][DirectoryRdnPrefix("CN")]类 ExamplePrincipal : UserPrincipal{public ExamplePrincipal(PrincipalContext context) : base(context) { }公共 ExamplePrincipal(PrincipalContext 上下文,字符串 samAccountName,字符串密码,启用 bool):基础(上下文,samAccountName,密码,启用){}public static new ExamplePrincipal FindByIdentity(PrincipalContext context, string identityValue){return (ExamplePrincipal)FindByIdentityWithType(context, typeof(ExamplePrincipal), identityValue);}[DirectoryProperty("vwBlobData")]公共字节[] BlobData{得到{如果(ExtensionGet(vwBlobData").长度!= 1)返回空;return (byte[])ExtensionGet("vwBlobData")[0];}放{//方法一this.ExtensionSet("vwBlobData", value );//方法二//this.ExtensionSet("vwBlobData", new object[] { value});}}}}}如果我使用方法 1,我会在
<前>System.DirectoryServices.AccountManagement.PrincipalOperationException 未处理HResult=-2146233087Message=指定的目录服务属性或值已经存在.来源=System.DirectoryServices.AccountManagement错误代码=-2147016691堆栈跟踪://剪断内部异常:System.DirectoryServices.DirectoryServicesCOMExceptionHResult=-2147016691Message=指定的目录服务属性或值已经存在.源 = System.DirectoryServices错误代码=-2147016691扩展错误=8321ExtendedErrorMessage=00002081:AtrErr:DSID-030F154F,#1:0: 00002081: DSID-030F154F,问题 1006 (ATT_OR_VALUE_EXISTS),数据 0,Att 82818fec (vwBlobData)堆栈跟踪://剪断内部异常:update.Save()操作中得到以下异常如果我使用方法 2,我会从
<前>System.ArgumentException 未处理HResult=-2147024809Message=其元素是另一个集合的集合不能由 ExtensionClasses 设置.来源=System.DirectoryServices.AccountManagement堆栈跟踪://剪断内部异常:set.BlobData调用的this.ExtensionSet调用中得到一个异常.
<小时>
总结:如果当前未设置该值,我可以设置该值,但如果我想覆盖现有值,则会出现错误.
我找到了一个解决方法,首先将值设置为 null,它不再抛出异常.
using (var update = ExamplePrincipal.FindByIdentity(context, "example")){更新.BlobData = null;更新.保存();update.BlobData = 新字节[] { 0x12, 0x34, 0x56, 0x67 };更新.保存();//方法 1 不再失败.}我将这个问题留待一段时间,看看是否有其他人可以回答是否有适当"的方法来做到这一点.
<小时>找到了不需要强制保存的第二个解决方法.
[DirectoryProperty("vwBlobData")]公共字节[] BlobData{得到{如果(ExtensionGet(vwBlobData").长度!= 1)返回空;return (byte[])ExtensionGet("vwBlobData")[0];}放{((DirectoryEntry)this.GetUnderlyingObject()).Properties["vwBlobData"].Value = value;}}通过直接转换到底层对象,您可以直接设置值.我使用 ILSpy 检查并处理 AccountManagement 包装器处理底层对象,因此 GetUnderlyingObject() 调用不需要 Dispose().
最佳解决方案
我发现第 2 个解决方法要求对象持续工作,因此我采用了两全其美的方法.这适用于尚未持久化对象、对象为 null 以及对象已有值的情况.
[DirectoryProperty("vwBlobData")]公共字节[] BlobData{得到{如果(ExtensionGet(vwBlobData").长度!= 1)返回空;return (byte[])ExtensionGet("vwBlobData")[0];}放{if(ExtensionGet("vwBlobData").Length == 0)this.ExtensionSet("vwBlobData", 数据);别的((DirectoryEntry)this.GetUnderlyingObject()).Properties["vwBlobData"].Value = 数据;}}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).
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
Small example program:
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});
}
}
}
}
}
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:
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;
}
}
By casting directly to the underlying object you can set the value directly. I checked using ILSpy and disposing the AccountManagement wrapper disposes the underlying object, so no Dispose() is required for the GetUnderlyingObject() call.
BEST SOLUTION
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;
}
}
这篇关于使用 AccountManagement 扩展类时如何设置二进制属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:使用 AccountManagement 扩展类时如何设置二进制属性
基础教程推荐
- 在 VS2010 中的 Post Build 事件中将 bin 文件复制到物 2022-01-01
- 首先创建代码,多对多,关联表中的附加字段 2022-01-01
- 经典 Asp 中的 ResolveUrl/Url.Content 等效项 2022-01-01
- 如何动态获取文本框中datagridview列的总和 2022-01-01
- 从 VS 2017 .NET Core 项目的发布目录中排除文件 2022-01-01
- 错误“此流不支持搜索操作"在 C# 中 2022-01-01
- 全局 ASAX - 获取服务器名称 2022-01-01
- JSON.NET 中基于属性的类型解析 2022-01-01
- 将事件 TextChanged 分配给表单中的所有文本框 2022-01-01
- 是否可以在 asp classic 和 asp.net 之间共享会话状态 2022-01-01
