<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0">
  <channel>
    <title>Directory Programming .NET</title>
    <description>Active Directory and ADAM programming support for .NET developers</description>
    <link>http://directoryprogramming.net/roller/default.aspx</link>
    <docs>http://backend.userland.com/rss</docs>
    <generator>Community Server v2.0 (http://www.communityserver.org)</generator>
    <item>
      <title>Working with Objects in SSDS Part 3</title>
      <description>&lt;p&gt;
Here is my last installment in this series of working with objects in SQL Server Data
Services.&amp;nbsp; For background, readers should read the following:
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/SerializationInSSDS.aspx"&gt;Serialization in SSDS&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/WorkingWithObjectsInSSDSPart1.aspx"&gt;Working with Objects
in SSDS Part 1&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/WorkingWithObjectsInSSDSPart2.aspx"&gt;Working with Objects
in SSDS Part 2&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
Last time, we concluded with a class called &lt;strong&gt;SsdsEntity&amp;lt;T&amp;gt;&lt;/strong&gt; that
became an all-purpose wrapper or veneer around our CLR objects.&amp;nbsp; This made it
simple to take our existing classes and serialize them as entities in SSDS.
&lt;/p&gt;
&lt;p&gt;
In this post, I want to discuss how the querying in the REST library works.&amp;nbsp;
First a simple example:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;var ctx = &lt;span&gt;new&lt;/span&gt; SsdsContext( &lt;span&gt;"authority=http://dunnry.data.beta.mssds.com/v1/;username=dunnry;password=secret"&lt;/span&gt; );
var container = ctx.OpenContainer(&lt;span&gt;"foo"&lt;/span&gt;); var
foo = &lt;span&gt;new&lt;/span&gt; Foo { IsPublic = &lt;span&gt;false&lt;/span&gt;,
Name = &lt;span&gt;"MyFoo"&lt;/span&gt;, Size = 12 }; &lt;span&gt;//insert
it with unique id guid string&lt;/span&gt; container.Insert(foo, Guid.NewGuid().ToString()); &lt;span&gt;//now
query for it&lt;/span&gt; var results = container.Query&amp;lt;Foo&amp;gt;(e =&amp;gt; e.Entity.IsPublic
== &lt;span&gt;false&lt;/span&gt; &amp;amp;&amp;amp; e.Entity.Size &amp;gt; 2); &lt;span&gt;//Query&amp;lt;T&amp;gt;
returns IEnumerable&amp;lt;SsdsEntity&amp;lt;T&amp;gt;&amp;gt;, so foreach over it&lt;/span&gt; &lt;span&gt;foreach&lt;/span&gt; (var
item &lt;span&gt;in&lt;/span&gt; results) { Console.WriteLine(item.Entity.Name);
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
I glossed over it in my previous posts with this library, but I have a class called &lt;strong&gt;SsdsContext&lt;/strong&gt; that
acts as my credential store and factory to create &lt;strong&gt;SsdsContainer&lt;/strong&gt; objects
where I perform my operations.&amp;nbsp; Here, I have opened a container called 'foo',
which would relate to the URI (&lt;a href="http://dunnry.data.beta.mssds.com/v1/foo"&gt;http://dunnry.data.beta.mssds.com/v1/foo&lt;/a&gt;)
according to the authority name I passed on the &lt;strong&gt;SsdsContext&lt;/strong&gt; constructor
arguments.
&lt;/p&gt;
&lt;p&gt;
I created an instance of my &lt;strong&gt;Foo&lt;/strong&gt; class (see &lt;a href="http://dunnry.com/blog/WorkingWithObjectsInSSDSPart1.aspx"&gt;this
post&lt;/a&gt; if you want to see what a &lt;strong&gt;Foo&lt;/strong&gt; looks like) and inserted it.&amp;nbsp;
We know that under the covers we have an &lt;strong&gt;XmlSerializer&lt;/strong&gt; doing the
work to serialize that to the proper POX wire format.&amp;nbsp; So far, so good.&amp;nbsp;
Now, I want to retrieve that same entity back from SSDS. The key line here is the
table.Query&amp;lt;T&amp;gt;() call.&amp;nbsp; It accepts a &lt;strong&gt;Expression&amp;lt;Func&amp;lt;SsdsEntity&amp;lt;T&amp;gt;,
bool&amp;gt;&amp;gt;&lt;/strong&gt; argument that represents a strongly typed query.
&lt;/p&gt;
&lt;p&gt;
For the uninitiated, the &lt;strong&gt;Expression&amp;lt;TDelegate&amp;gt;&lt;/strong&gt; is a way to
represent lambda expressions in an abstract syntax tree.&amp;nbsp; We can think of them
as a way to model what the expression does without generating the bits of code necessary
to actually do it.&amp;nbsp; We can inspect the &lt;strong&gt;Expression&lt;/strong&gt; and create
new ones based on it until finally we can call Compile and actually convert the representation
of the lambda into something that can execute.
&lt;/p&gt;
&lt;p&gt;
The &lt;strong&gt;Func&amp;lt;SsdsEntity&amp;lt;T&amp;gt;, bool&amp;gt;&lt;/strong&gt; represents a delegate that
accepts a &lt;strong&gt;SsdsEntity&amp;lt;T&amp;gt;&lt;/strong&gt; as an argument and returns a boolean.&amp;nbsp;
This effectively represents the WHERE clause in the SSDS LINQ query syntax.&amp;nbsp;
Since &lt;strong&gt;SsdsEntity&amp;lt;T&amp;gt;&lt;/strong&gt; contains an actual type T in the &lt;strong&gt;Entity&lt;/strong&gt; property,
you can query directly against it in a strongly typed fashion!
&lt;/p&gt;
&lt;p&gt;
What about those flexible properties that I added to support flexible attributes outside
of our T?&amp;nbsp; I mentioned that I wanted to keep the &lt;strong&gt;PropertyBucket&lt;/strong&gt; (a &lt;strong&gt;Dictionary&amp;lt;string,
object&amp;gt;&lt;/strong&gt;) property public for querying.&amp;nbsp; In order to use the flexible
properties that you add, you simply use it in a weakly typed manner:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;var results = container.Query&amp;lt;Foo&amp;gt;(e =&amp;gt; e.PropertyBucket[&lt;span&gt;"MyFlexProp"&lt;/span&gt;]
&amp;gt; 10);&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
As you can see, any boolean expression that you can think of in the string-based SSDS
LINQ query syntax can now be expressed in a strongly-typed manner using the &lt;strong&gt;Func&amp;lt;SsdsEntity&amp;lt;T&amp;gt;,
bool&amp;gt;&lt;/strong&gt; lambda syntax.
&lt;/p&gt;
&lt;h3&gt;How it works
&lt;/h3&gt;
&lt;p&gt;
Since I have the expression tree of what your query looks like in strongly-typed terms,
it is a simple matter to take that and convert it to the SSDS LINQ query syntax that
looks like "from e in entities where [....] select e" that is appended to the query
string in the REST interface.&amp;nbsp; I should say it is a simple matter because &lt;a href="http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx"&gt;Matt
Warren&lt;/a&gt; did a lot of the heavy lifting for us and provided the abstract expression
visitor (&lt;strong&gt;ExpressionVisitor&lt;/strong&gt;) as well as the expression visitor that
partially evaluates the tree to evaluate constants (&lt;strong&gt;SubTreeEvaluator&lt;/strong&gt;).&amp;nbsp;
This last part is important because it allows us to write this:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;int&lt;/span&gt; i
= 10; &lt;span&gt;string&lt;/span&gt; name = &lt;span&gt;"MyFoo"&lt;/span&gt;;
var results = container.Query&amp;lt;Foo&amp;gt;(e =&amp;gt; e.Entity.Name == name &amp;amp;&amp;amp;
e.Entity.Size &amp;gt; i);&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Without the partial tree evaluation, you would not be able to express the right hand
side of the equation.&amp;nbsp; All I had to do was implement an expression visitor that
correctly evaluated the lambda expression and converted it to the LINQ syntax that
SSDS expects (&lt;strong&gt;SsdsExpressionVisitor&lt;/strong&gt;).&amp;nbsp; It would be a trivial
matter to actually implement the &lt;strong&gt;IQueryProvider&lt;/strong&gt; and &lt;strong&gt;IQueryable&lt;/strong&gt; interfaces
to make the whole thing work inside LINQ to Objects.
&lt;/p&gt;
&lt;p&gt;
Originally, I did supply the &lt;strong&gt;IQueryProvider&lt;/strong&gt; for this implementation
but after consideration I have decided that using methods from the &lt;strong&gt;SsdsContainer&lt;/strong&gt; class
instead of the standard LINQ syntax is the best way to proceed.&amp;nbsp; Mainly, this
has to do with the fact that I want to make it more explicit to the developer what
will happen under the covers rather than using the standard &lt;strong&gt;Where()&lt;/strong&gt; extension
method.
&lt;/p&gt;
&lt;h3&gt;Querying data
&lt;/h3&gt;
&lt;p&gt;
The main interaction to return data is via the &lt;strong&gt;Query&amp;lt;T&amp;gt;&lt;/strong&gt; method.&amp;nbsp;
This method is smart enough to add the Kind into the query for you based on the T
supplied.&amp;nbsp; So, if you write something like:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;var results = container.Query&amp;lt;Foo&amp;gt;(e =&amp;gt; e.Entity.Size &amp;gt; 2);&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
This is actually translated to "&lt;em&gt;from e in entities where e["Size"] &amp;gt; 2 &amp;amp;&amp;amp;
e.Kind == "Foo" select e&lt;/em&gt;".&amp;nbsp; The addition of the kind is important because
we want to limit the results as much as possible.&amp;nbsp; If there happened to be many
kinds in the container that had the flexible property "Size", it would actually return
those as well in the wire response.
&lt;/p&gt;
&lt;p&gt;
Of course, what about if you want that to happen?&amp;nbsp; What if you want to return
other kinds that have the "Size" property?&amp;nbsp; To do this, I have introduced a class
called &lt;strong&gt;SsdsEntityBucket&lt;/strong&gt;.&amp;nbsp; It is exactly what it sounds like.&amp;nbsp;
To use it, you simply specify a query that uses additional types with either the &lt;strong&gt;Query&amp;lt;T,U,V&amp;gt;&lt;/strong&gt; or &lt;strong&gt;Query&amp;lt;T,U&amp;gt;&lt;/strong&gt; methods.&amp;nbsp;
Here is an example:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;var foo = &lt;span&gt;new&lt;/span&gt; Foo
{ IsPublic = &lt;span&gt;true&lt;/span&gt;, MyCheese = &lt;span&gt;new&lt;/span&gt; Cheese
{ LastModified = DateTime.Now, Name = &lt;span&gt;"MyCheese"&lt;/span&gt; },
Name = &lt;span&gt;"FooMaster"&lt;/span&gt;, Size = 10 }; container.Insert(foo,
foo.Name); container.Insert(foo.MyCheese, foo.MyCheese.Name); &lt;span&gt;//query
for bucket...&lt;/span&gt; var bucket = container.Query&amp;lt;Foo, Cheese&amp;gt;( (f, c) =&amp;gt;
f.Entity.Name == &lt;span&gt;"FooMaster"&lt;/span&gt; || c.Entity.Name
== &lt;span&gt;"MyCheese"&lt;/span&gt; ); var f1 = bucket.GetEntities&amp;lt;Foo&amp;gt;().Single();
var c1 = bucket.GetEntities&amp;lt;Cheese&amp;gt;().Single();&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
The calls to &lt;strong&gt;GetEntities&amp;lt;T&amp;gt;&lt;/strong&gt; returns &lt;strong&gt;IEnumerable&amp;lt;SsdsEntity&amp;lt;T&amp;gt;&amp;gt;&lt;/strong&gt; again.&amp;nbsp;
However, this was done in a single call to SSDS instead of multiple calls per T.
&lt;/p&gt;
&lt;h3&gt;Paging
&lt;/h3&gt;
&lt;p&gt;
As I mentioned earlier, I wanted the developer to understand what they were doing
when they called each method, so I decided to make paging explicit.&amp;nbsp; If I had
potentially millions of entities in SSDS, it would be a bad mistake to allow a developer
to issue a simple query that seamlessly paged the items back - especially if the query
was something like &lt;em&gt;e =&amp;gt; e.Id != ""&lt;/em&gt;.&amp;nbsp; Here is how I handled paging:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;var container = ctx.OpenContainer(&lt;span&gt;"paging"&lt;/span&gt;);
List&amp;lt;Foo&amp;gt; items = &lt;span&gt;new&lt;/span&gt; List&amp;lt;Foo&amp;gt;(); &lt;span&gt;int&lt;/span&gt; i
= 1; container.PagedQuery&amp;lt;Foo&amp;gt;( e =&amp;gt; e.Entity.Size != 0, c =&amp;gt; { Console.WriteLine(&lt;span&gt;"Got
Page {0}"&lt;/span&gt;, i++); items.AddRange(c.Select(s =&amp;gt; s.Entity)); } ); Console.WriteLine(items.Count);&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
The &lt;strong&gt;PagedQuery&amp;lt;T&amp;gt;&lt;/strong&gt; method takes two arguments.&amp;nbsp; One is
the standard &lt;strong&gt;Expression&amp;lt;Func&amp;lt;SsdsEntity&amp;lt;T&amp;gt;, bool&amp;gt;&amp;gt;&lt;/strong&gt; that
you use to specify the WHERE clause for SSDS, and the other is &lt;strong&gt;Action&amp;lt;IEnumerable&amp;lt;SsdsEntity&amp;lt;T&amp;gt;&amp;gt;&amp;gt;&lt;/strong&gt; which
represents a delegate that takes an &lt;strong&gt;IEnumerable&amp;lt;SsdsEntity&amp;lt;T&amp;gt;&amp;gt;&lt;/strong&gt; and
has a void return.&amp;nbsp; This is a delegate you provide that does something with the
500 entities returned per page (it gets called once per page).&amp;nbsp; Here, I am just
adding them into a &lt;strong&gt;List&amp;lt;T&amp;gt;&lt;/strong&gt;, but I could easily be doing anything
else here.&amp;nbsp; Under the covers, this is adding the paging term dynamically into
the expression tree that is evaluated.
&lt;/p&gt;
&lt;h3&gt;What's next
&lt;/h3&gt;
&lt;p&gt;
This is a good head start on using the REST API with SSDS today.&amp;nbsp; However, there
are a number of optimizations that could be made to the model: additional overloads,
perhaps some extension methods for common operations, etc. 
&lt;/p&gt;
&lt;p&gt;
As new features are added, I will endeavor to update this as well (blob support comes
to mind here).&amp;nbsp; Additionally, I have a few optimizations planned around concurrency
for CRUD operations.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
I have published this out to Code Gallery and I welcome feedback and bug fixes.&amp;nbsp; &lt;a href="http://code.msdn.microsoft.com/ssdsrest"&gt;Linked
here&lt;/a&gt;.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=b37072b9-1fe8-4785-9df3-00861ab346f7" /&gt;</description>
      <link>http://dunnry.com/blog/WorkingWithObjectsInSSDSPart3.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,b37072b9-1fe8-4785-9df3-00861ab346f7.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,b37072b9-1fe8-4785-9df3-00861ab346f7.aspx</guid>
      <pubDate>Wed, 02 Jul 2008 18:43:26 GMT</pubDate>
    </item>
    <item>
      <title>Working with Objects in SSDS Part 2</title>
      <description>&lt;p&gt;
This is the second post in my series on working with SQL Server Data Service (SSDS)
and objects.&amp;nbsp; For background, you should read my post on &lt;a href="http://dunnry.com/blog/SerializationInSSDS.aspx"&gt;Serializing
Objects in SSDS&lt;/a&gt; and the &lt;a href="http://dunnry.com/blog/WorkingWithObjectsInSSDSPart1.aspx"&gt;first
post&lt;/a&gt; in this series.
&lt;/p&gt;
&lt;p&gt;
Last time I showed how to create a general purpose serializer for SSDS using the standard &lt;strong&gt;XmlSerializer&lt;/strong&gt; class
in .NET.&amp;nbsp; I created a shell entity or a 'thin veneer' for objects called &lt;strong&gt;SsdsEntity&amp;lt;T&amp;gt;&lt;/strong&gt;,
where T was any POCO (plain old C#/CLR object).&amp;nbsp; This allowed me to abstract
away the metadata properties required for SSDS without changing my actual POCO object
(which, I noted was lame to do).
&lt;/p&gt;
&lt;p&gt;
If we decide that we will use SSDS to interact with POCO T, an interesting situation
arises.&amp;nbsp; Namely, once we have defined T, we have in fact defined a schema - albeit
one only enforced in code you write and not by the SSDS service itself.&amp;nbsp; One
of the advantages of using something like SSDS is that you have a lot of flexibility
in storing entities&amp;nbsp; (hence the term 'flexible entity') without conforming to
schema.&amp;nbsp; Since, I want to support this flexibility, it means I need to think
of a way to support not only the schema implied by T, but also additional and arbitrary
properties that a user might consider.
&lt;/p&gt;
&lt;p&gt;
Some may wonder why we need this flexibility:&amp;nbsp; after all, why not just change
T to support whatever we like?&amp;nbsp; The issue comes up most often with code you do
not control.&amp;nbsp; If you already have an existing codebase with objects that you
would like to store in SSDS, it might not be practical or even possible to change
the T to add additional schema.
&lt;/p&gt;
&lt;p&gt;
Even if you completely control the codebase, expressing relationships between CLR
objects and expressing relationships between things in your data are two different
ideas - sometimes this problem has been termed 'impedance mismatch'.
&lt;/p&gt;
&lt;p&gt;
In the CLR, if two objects are related, they are often part of a collection, or they
refer to an instance on another object.&amp;nbsp; This is easy to express in the CLR (e.g. &lt;strong&gt;Instance.ChildrenCollection["key"]&lt;/strong&gt;).&amp;nbsp;
In your typical datasource, this same relationship is done using foreign keys to refer
to other entities.
&lt;/p&gt;
&lt;p&gt;
Consider the following classes:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; Employee
{ &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; EmployeeId
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Name
{ get; set; } &lt;span&gt;public&lt;/span&gt; DateTime HireDate { get;
set; } &lt;span&gt;public&lt;/span&gt; Employee Manager { get; set; } &lt;span&gt;public&lt;/span&gt; Project[]
Projects { get; set; } } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; Project
{ &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; ProjectId
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Name
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; BillCode
{ get; set; } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Here we see that the &lt;strong&gt;Employee&lt;/strong&gt; class refers to itself as well as contains
a collection of related projects (&lt;strong&gt;Project&lt;/strong&gt; class) that the employee
works on.&amp;nbsp; SSDS only supports simple scalar types and no arrays or nested objects
today, so we cannot directly express this in SSDS.&amp;nbsp; However, we can decompose
this class and store the bits separately and then reassemble later.&amp;nbsp; First, let's
see what that looks like and then we can see how it was done:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;var projects = &lt;span&gt;new&lt;/span&gt; Project[]
{ &lt;span&gt;new&lt;/span&gt; Project { BillCode = &lt;span&gt;"123"&lt;/span&gt;,
Name = &lt;span&gt;"TPS Slave"&lt;/span&gt;, ProjectId = &lt;span&gt;"PID01"&lt;/span&gt;}, &lt;span&gt;new&lt;/span&gt; Project
{ BillCode = &lt;span&gt;"124"&lt;/span&gt;, Name = &lt;span&gt;"Programmer"&lt;/span&gt;,
ProjectId = &lt;span&gt;"PID02"&lt;/span&gt; } }; var bill = &lt;span&gt;new&lt;/span&gt; Employee
{ EmployeeId = &lt;span&gt;"EMP01"&lt;/span&gt;, HireDate = DateTime.Now.AddMonths(-1),
Manager = &lt;span&gt;null&lt;/span&gt;, Name = &lt;span&gt;"Bill
Lumbergh"&lt;/span&gt;, Projects = &lt;span&gt;new&lt;/span&gt; Project[] {}
}; var peter = &lt;span&gt;new&lt;/span&gt; Employee { EmployeeId = &lt;span&gt;"EMP02"&lt;/span&gt;,
HireDate = DateTime.Now, Manager = bill, Name = &lt;span&gt;"Peter
Gibbons"&lt;/span&gt;, Projects = projects }; var cloudpeter = &lt;span&gt;new&lt;/span&gt; SsdsEntity&amp;lt;Employee&amp;gt;
{ Entity = peter, Id = peter.EmployeeId }; var cloudbill = &lt;span&gt;new&lt;/span&gt; SsdsEntity&amp;lt;Employee&amp;gt;
{ Entity = bill, Id = bill.EmployeeId }; &lt;span&gt;//here is how
we add flexible props&lt;/span&gt; cloudpeter.Add&amp;lt;&lt;span&gt;string&lt;/span&gt;&amp;gt;(&lt;span&gt;"ManagerId"&lt;/span&gt;,
peter.Manager.EmployeeId); var table = _context.OpenContainer(&lt;span&gt;"initech"&lt;/span&gt;);
table.Insert(cloudpeter); table.Insert(cloudbill); var cloudprojects = peter.Projects
.Select(s =&amp;gt; &lt;span&gt;new&lt;/span&gt; SsdsEntity&amp;lt;Project&amp;gt;
{ Entity = s, Id = Guid.NewGuid().ToString() }); &lt;span&gt;//add
some metadata to track the project to employee&lt;/span&gt; &lt;span&gt;foreach&lt;/span&gt; (var
proj &lt;span&gt;in&lt;/span&gt; cloudprojects) { proj.Add&amp;lt;&lt;span&gt;string&lt;/span&gt;&amp;gt;(&lt;span&gt;"RelatedEmployee"&lt;/span&gt;,
peter.EmployeeId); table.Insert(proj); }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
All this code does is create two employees and two projects and set the relationships
between them.&amp;nbsp; Using the &lt;strong&gt;Add&amp;lt;K&amp;gt;&lt;/strong&gt; method, I can insert any
primitive type to go along for the ride with the POCO.&amp;nbsp; If we query the container
now, this is what we see:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:EntitySet&lt;/span&gt; &lt;span&gt;xmlns:s&lt;/span&gt;&lt;span&gt;="http://schemas.microsoft.com/sitka/2008/03/"&lt;/span&gt; &lt;span&gt;xmlns:xsi&lt;/span&gt;&lt;span&gt;="http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span&gt;xmlns:x&lt;/span&gt;&lt;span&gt;="http://www.w3.org/2001/XMLSchema"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Project&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;2ffd7a92-2a3b-4cd8-a5f7-55f40c3ba2b0&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;1&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ProjectId&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;PID01&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ProjectId&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;TPS
Slave&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;BillCode&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;123&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;BillCode&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;RelatedEmployee&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;EMP02&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;RelatedEmployee&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Project&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Project&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;892dbb1e-ba47-4c87-80e6-64fbb46da935&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;1&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ProjectId&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;PID02&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ProjectId&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;Programmer&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;BillCode&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;124&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;BillCode&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;RelatedEmployee&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;EMP02&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;RelatedEmployee&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Project&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Employee&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;EMP01&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;1&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;EmployeeId&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;EMP01&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;EmployeeId&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;Bill
Lumbergh&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;HireDate&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:dateTime"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;2008-05-25T23:59:49&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;HireDate&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Employee&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Employee&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;EMP02&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;1&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;EmployeeId&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;EMP02&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;EmployeeId&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;Peter
Gibbons&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;HireDate&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:dateTime"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;2008-06-25T23:59:49&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;HireDate&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ManagerId&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;EMP01&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ManagerId&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Employee&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:EntitySet&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
As you can see, I have stored extra data in my 'flexible' entity with the &lt;strong&gt;ManagerId&lt;/strong&gt; property
(on one entity) and &lt;strong&gt;RelatedEmployee&lt;/strong&gt; property on the &lt;strong&gt;Project&lt;/strong&gt; kinds.&amp;nbsp;
This allows me to figure out later what objects are related to each other since we
can't model the CLR objects relationships directly.&amp;nbsp; Let's see how this was done.
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; SsdsEntity&amp;lt;T&amp;gt; &lt;span&gt;where&lt;/span&gt; T: &lt;span&gt;class&lt;/span&gt; {
Dictionary&amp;lt;&lt;span&gt;string&lt;/span&gt;, &lt;span&gt;object&lt;/span&gt;&amp;gt;
_propertyBucket = &lt;span&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span&gt;string&lt;/span&gt;, &lt;span&gt;object&lt;/span&gt;&amp;gt;(); &lt;span&gt;public&lt;/span&gt; SsdsEntity()
{ } [XmlIgnore] &lt;span&gt;public&lt;/span&gt; Dictionary&amp;lt;&lt;span&gt;string&lt;/span&gt;, &lt;span&gt;object&lt;/span&gt;&amp;gt;
PropertyBucket { get { &lt;span&gt;return&lt;/span&gt; _propertyBucket;
} } [XmlAnyElement] &lt;span&gt;public&lt;/span&gt; XElement[] Attributes
{ get { &lt;span&gt;//using XElement is much easier than XmlElement
to build&lt;/span&gt; &lt;span&gt;//take all properties on object instance
and build XElement&lt;/span&gt; var props = from prop &lt;span&gt;in&lt;/span&gt; &lt;span&gt;typeof&lt;/span&gt;(T).GetProperties()
let val = prop.GetValue(&lt;span&gt;this&lt;/span&gt;.Entity, &lt;span&gt;null&lt;/span&gt;) &lt;span&gt;where&lt;/span&gt; prop.GetSetMethod()
!= &lt;span&gt;null&lt;/span&gt; &amp;amp;&amp;amp; allowableTypes.Contains(prop.PropertyType)
&amp;amp;&amp;amp; val != &lt;span&gt;null&lt;/span&gt; select &lt;span&gt;new&lt;/span&gt; XElement(prop.Name, &lt;span&gt;new&lt;/span&gt; XAttribute(Constants.xsi
+ &lt;span&gt;"type"&lt;/span&gt;, XsdTypeResolver.Solve(prop.PropertyType)),
EncodeValue(val) ); &lt;span&gt;//Then stuff in any extra stuff you
want&lt;/span&gt; var extra = _propertyBucket.Select( e =&amp;gt; &lt;span&gt;new&lt;/span&gt; XElement(e.Key, &lt;span&gt;new&lt;/span&gt; XAttribute(Constants.xsi
+ &lt;span&gt;"type"&lt;/span&gt;, XsdTypeResolver.Solve(e.Value.GetType())),
EncodeValue(e.Value) ) ); &lt;span&gt;return&lt;/span&gt; props.Union(extra).ToArray();
} set { &lt;span&gt;//wrap the XElement[] with the name of the type&lt;/span&gt; var
xml = &lt;span&gt;new&lt;/span&gt; XElement(&lt;span&gt;typeof&lt;/span&gt;(T).Name, &lt;span&gt;value&lt;/span&gt;);
var xs = &lt;span&gt;new&lt;/span&gt; XmlSerializer(&lt;span&gt;typeof&lt;/span&gt;(T)); &lt;span&gt;//xml.CreateReader()
cannot be used as it won't support base64 content&lt;/span&gt; XmlTextReader reader = &lt;span&gt;new&lt;/span&gt; XmlTextReader(
xml.ToString(), XmlNodeType.Document, &lt;span&gt;null&lt;/span&gt; ); &lt;span&gt;this&lt;/span&gt;.Entity
= (T)xs.Deserialize(reader); &lt;span&gt;//now deserialize the other
stuff left over into the property bucket...&lt;/span&gt; var stuff = from v &lt;span&gt;in&lt;/span&gt; &lt;span&gt;value&lt;/span&gt;.AsEnumerable()
let props = &lt;span&gt;typeof&lt;/span&gt;(T).GetProperties().Select(s
=&amp;gt; s.Name) &lt;span&gt;where&lt;/span&gt; !props.Contains(v.Name.ToString())
select v; &lt;span&gt;foreach&lt;/span&gt; (var item &lt;span&gt;in&lt;/span&gt; stuff)
{ _propertyBucket.Add( item.Name.ToString(), DecodeValue( item.Attribute(Constants.xsi
+ &lt;span&gt;"type"&lt;/span&gt;).Value, item.Value) ); } } } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Add&amp;lt;K&amp;gt;(&lt;span&gt;string&lt;/span&gt; key,
K &lt;span&gt;value&lt;/span&gt;) { &lt;span&gt;if&lt;/span&gt; (!allowableTypes.Contains(&lt;span&gt;typeof&lt;/span&gt;(K))) &lt;span&gt;throw&lt;/span&gt; &lt;span&gt;new&lt;/span&gt; ArgumentException(
String.Format( &lt;span&gt;"Type {0} not supported in SsdsEntity"&lt;/span&gt;, &lt;span&gt;typeof&lt;/span&gt;(K).Name)
); &lt;span&gt;if&lt;/span&gt; (!_propertyBucket.ContainsKey(key)) { _propertyBucket.Add(key, &lt;span&gt;value&lt;/span&gt;);
} &lt;span&gt;else&lt;/span&gt; { &lt;span&gt;//replace
the value&lt;/span&gt; _propertyBucket.Remove(key); _propertyBucket.Add(key, &lt;span&gt;value&lt;/span&gt;);
} } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
I have omitted the parts of &lt;strong&gt;SsdsEntity&amp;lt;T&amp;gt;&lt;/strong&gt; from the first post
that didn't change.&amp;nbsp; The only other addition you don't see here is a helper method
called &lt;strong&gt;DecodeValue&lt;/strong&gt;, which as you might guess, interprets the string
value in XML and attempts to cast it to a CLR type based on the xsi:type that comes
back.
&lt;/p&gt;
&lt;p&gt;
All we did here was add a &lt;strong&gt;Dictionary&amp;lt;string, object&amp;gt;&lt;/strong&gt; property
called &lt;strong&gt;PropertyBucket&lt;/strong&gt; that holds our extra stuff we want to associate
with our T instance.&amp;nbsp; Then in the getter and setter for the &lt;strong&gt;XElement[]&lt;/strong&gt; property
called &lt;strong&gt;Attributes&lt;/strong&gt;, we are adding them into our array of &lt;strong&gt;XElement&lt;/strong&gt; as
well as pulling them back out on deserialization and stuffing them back into the Dictionary.&amp;nbsp;
With this simple addition, we have fixed our in flexibility (or lack thereof) problem.&amp;nbsp;
We are still limited to the simple scalar types, but as you can see you can work around
this in a lot of cases by decomposing the objects down enough to be able to recreate
them later.
&lt;/p&gt;
&lt;p&gt;
The &lt;strong&gt;Add&amp;lt;K&amp;gt;&lt;/strong&gt; method is a convenience only as we could operate
directly against the Dictionary.&amp;nbsp; I also could have chosen to keep the Dictionary
property bucket private and not expose it.&amp;nbsp; That would have worked just fine
for serialization, but I wanted to also be able to query it later.
&lt;/p&gt;
&lt;p&gt;
In my last post, I said I would introduce a library where all this code is coming
from, but I didn't realize at the time how long this post would be and that I still
need to cover querying.&amp;nbsp; So... next time, I will finish up this series by explaining
how the strongly typed query model works and how all these pieces fit together to
recompose the data back into objects (and release the library).
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=bdbc0cbd-e845-49e9-9e43-52a079bda687" /&gt;</description>
      <link>http://dunnry.com/blog/WorkingWithObjectsInSSDSPart2.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,bdbc0cbd-e845-49e9-9e43-52a079bda687.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,bdbc0cbd-e845-49e9-9e43-52a079bda687.aspx</guid>
      <pubDate>Thu, 26 Jun 2008 15:03:54 GMT</pubDate>
    </item>
    <item>
      <title>Working with Objects in SSDS Part 1</title>
      <description>&lt;p&gt;
&lt;a href="http://dunnry.com/blog/SerializationInSSDS.aspx"&gt;Last time&lt;/a&gt; we talked
about SQL Server Data Services and serializing objects, we discussed how easy it was
to use the &lt;strong&gt;XmlSerializer&lt;/strong&gt; to deserialize objects using the REST interface.&amp;nbsp;
The problem was that when we serialized objects using the &lt;strong&gt;XmlSerializer&lt;/strong&gt;,
it left out the xsi type declarations that we needed.&amp;nbsp; I gave two possible solutions
to this problem - one that used the &lt;strong&gt;XmlSerializer&lt;/strong&gt; and 'fixed' the
output after the fact, and the other built the XML that we needed using XLINQ and
Reflection.
&lt;/p&gt;
&lt;p&gt;
Today, I am going to talk about a third technique that I have been using lately that
I like better.&amp;nbsp; It uses some of the previous techniques and leverages a few tricks
with &lt;strong&gt;XmlSerializer&lt;/strong&gt; to get what I want.&amp;nbsp; First, let's start with
a POCO (plain ol' C# object) class that we would like to use with SSDS.
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; Foo
{ &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Name
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;int&lt;/span&gt; Size
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;bool&lt;/span&gt; IsPublic
{ get; set; } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
In it's correctly serialized form, it looks like this on the wire:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Foo&lt;/span&gt; &lt;span&gt;xmlns:s&lt;/span&gt;&lt;span&gt;="http://schemas.microsoft.com/sitka/2008/03/"&lt;/span&gt; &lt;span&gt;xmlns:xsi&lt;/span&gt;&lt;span&gt;="http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span&gt;xmlns:x&lt;/span&gt;&lt;span&gt;="http://www.w3.org/2001/XMLSchema"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;someid&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Id&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;1&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;s:Version&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:string"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;My
Foo&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Name&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Size&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:decimal"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;10&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Size&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;IsPublic&lt;/span&gt; &lt;span&gt;xsi:type&lt;/span&gt;&lt;span&gt;="x:boolean"&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;false&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;IsPublic&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt; &lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Foo&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
You'll notice that we have the additional system metadata attributes "Id" and "Version"
in the markup.&amp;nbsp; We can account for the metadata attributes by doing something
cheesy like deriving from a base class:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;abstract&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; Cheese
{ &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Id
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;int&lt;/span&gt; Version
{ get; set; } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
However this is very unnatural as our classes would all have to derive from our "Cheese"
abstract base class (ABC).
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; Foo
: Cheese { &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Name
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;int&lt;/span&gt; Size
{ get; set; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;bool&lt;/span&gt; IsPublic
{ get; set; } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Developers familiar with remoting in .NET should be cringing right now as they remember
the hassles associated with deriving from &lt;strong&gt;MarshalByRefObject&lt;/strong&gt;.&amp;nbsp;
In a world without multiple inheritance, this can be painful.&amp;nbsp; I want a model
where I can use arbitrary POCO objects (&lt;a href="http://haacked.com/archive/2008/06/13/ras-syndrome.aspx"&gt;redundant&lt;/a&gt;,
yes I know) and not be forced to derive from anything or do what I would otherwise
term unnatural acts.
&lt;/p&gt;
&lt;p&gt;
What if instead, we derived a generic entity that could contain any other entity?
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; SsdsEntity&amp;lt;T&amp;gt; &lt;span&gt;where&lt;/span&gt; T: &lt;span&gt;class&lt;/span&gt; { &lt;span&gt;string&lt;/span&gt; _kind; &lt;span&gt;public&lt;/span&gt; SsdsEntity()
{ } [XmlElement(Namespace = &lt;span&gt;@"http://schemas.microsoft.com/sitka/2008/03/"&lt;/span&gt;)] &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Id
{ get; set; } [XmlIgnore] &lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Kind
{ get { &lt;span&gt;if&lt;/span&gt; (String.IsNullOrEmpty(_kind)) { _kind
= &lt;span&gt;typeof&lt;/span&gt;(T).Name; } &lt;span&gt;return&lt;/span&gt; _kind;
} set { _kind = &lt;span&gt;value&lt;/span&gt;; } } [XmlElement(Namespace
= &lt;span&gt;@"http://schemas.microsoft.com/sitka/2008/03/"&lt;/span&gt;)] &lt;span&gt;public&lt;/span&gt; &lt;span&gt;int&lt;/span&gt; Version
{ get; set; } [XmlIgnore] &lt;span&gt;public&lt;/span&gt; T Entity { get;
set; } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
In this case, we have simply wrapped the POCO that we care about in a class that knows
about the specifics of the SSDS wire format (or more accurately could serialize down
to the wire format).
&lt;/p&gt;
&lt;p&gt;
This &lt;strong&gt;SsdsEntity&amp;lt;T&amp;gt;&lt;/strong&gt; is easy to use and provides access to the
strongly typed object via the Entity property.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/content/binary/WindowsLiveWriter/SerializationinSSDSRevisited_BEC9/foomembers_2.png"&gt;&lt;img height="293" alt="foomembers" src="http://dunnry.com/blog/content/binary/WindowsLiveWriter/SerializationinSSDSRevisited_BEC9/foomembers_thumb.png" width="487" border="0"&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
Now, we just have to figure out how to serialize the &lt;strong&gt;SsdsEntity&amp;lt;Foo&amp;gt;&lt;/strong&gt; object
and we know that the metadata attributes are taken care of and our original POCO object
that we care about is included.&amp;nbsp; I call it wrapping POCOs in a thin SSDS veneer.
&lt;/p&gt;
&lt;p&gt;
The trick to this is to add a bucket of XElement objects on the &lt;strong&gt;SsdsEntity&amp;lt;T&amp;gt;&lt;/strong&gt; class
that will hold our public properties on our class T (i.e. 'Foo' class).&amp;nbsp; It looks
something like this:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;[XmlAnyElement]
&lt;span&gt;public&lt;/span&gt; XElement[]
Attributes { get { &lt;span&gt;//using XElement is much easier than
XmlElement to build&lt;/span&gt; &lt;span&gt;//take all properties on object
instance and build XElement&lt;/span&gt; var props = from prop &lt;span&gt;in&lt;/span&gt; &lt;span&gt;typeof&lt;/span&gt;(T).GetProperties()
let val = prop.GetValue(&lt;span&gt;this&lt;/span&gt;.Entity, &lt;span&gt;null&lt;/span&gt;) &lt;span&gt;where&lt;/span&gt; prop.GetSetMethod()
!= &lt;span&gt;null&lt;/span&gt; &amp;amp;&amp;amp; allowableTypes.Contains(prop.PropertyType)
&amp;amp;&amp;amp; val != &lt;span&gt;null&lt;/span&gt; select &lt;span&gt;new&lt;/span&gt; XElement(prop.Name, &lt;span&gt;new&lt;/span&gt; XAttribute(Constants.xsi
+ &lt;span&gt;"type"&lt;/span&gt;, XsdTypeResolver.Solve(prop.PropertyType)),
EncodeValue(val) ); &lt;span&gt;return&lt;/span&gt; props.ToArray(); }
set { &lt;span&gt;//wrap the XElement[] with the name of the type&lt;/span&gt; var
xml = &lt;span&gt;new&lt;/span&gt; XElement(&lt;span&gt;typeof&lt;/span&gt;(T).Name, &lt;span&gt;value&lt;/span&gt;);
var xs = &lt;span&gt;new&lt;/span&gt; XmlSerializer(&lt;span&gt;typeof&lt;/span&gt;(T)); &lt;span&gt;//xml.CreateReader()
cannot be used as it won't support base64 content&lt;/span&gt; XmlTextReader reader = &lt;span&gt;new&lt;/span&gt; XmlTextReader(
xml.ToString(), XmlNodeType.Document, &lt;span&gt;null&lt;/span&gt;); &lt;span&gt;this&lt;/span&gt;.Entity
= (T)xs.Deserialize(reader); } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
In the getter, we use Reflection and pull back a list of all the public properties
on the T object and build an array of &lt;strong&gt;XElement&lt;/strong&gt;.&amp;nbsp; This is the
same technique I used in my first post on serialization.&amp;nbsp; The 'allowableTypes'
object is a &lt;strong&gt;HashSet&amp;lt;Type&amp;gt;&lt;/strong&gt; that we use to figure out which property
types we can support in the service (DateTime, numeric, string, boolean, and byte[]).&amp;nbsp;
When this property serializes, the &lt;strong&gt;XElement&lt;/strong&gt;s are simply added to
the markup.
&lt;/p&gt;
&lt;p&gt;
The &lt;strong&gt;EncodeValue&lt;/strong&gt; method shown is a simple helper method that correctly
encodes string values, boolean, dates, integers, and byte[] values for the attribute.&amp;nbsp;
Finally, we are using a helper method that returns from a &lt;strong&gt;Dictionary&amp;lt;Type,string&amp;gt;&lt;/strong&gt; the
correct xsi type for the required attribute (as determined from the property type).
&lt;/p&gt;
&lt;p&gt;
For deserialization, what happens is that the &lt;strong&gt;[XmlAnyElement]&lt;/strong&gt; attribute
causes all unmapped attributes (in this case, all non-system metadata attributes)
to be collected in a collection of &lt;strong&gt;XElement&lt;/strong&gt;.&amp;nbsp; When we deserialize,
if we simply wrap an enclosing element around this &lt;strong&gt;XElement&lt;/strong&gt; collection,
it is exactly what we need for deserialization of T.&amp;nbsp; This is shown in the setter
implementation.
&lt;/p&gt;
&lt;p&gt;
It might look a little complicated, but now simple serialization will just work via
the &lt;strong&gt;XmlSerializer&lt;/strong&gt;.&amp;nbsp; Here is one such implementation:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; Serialize(SsdsEntity&amp;lt;T&amp;gt;
entity) { &lt;span&gt;//add a bunch of namespaces and override the
default ones too&lt;/span&gt; XmlSerializerNamespaces namespaces = &lt;span&gt;new&lt;/span&gt; XmlSerializerNamespaces();
namespaces.Add(&lt;span&gt;"s"&lt;/span&gt;, Constants.ns.NamespaceName);
namespaces.Add(&lt;span&gt;"x"&lt;/span&gt;, Constants.x.NamespaceName);
namespaces.Add(&lt;span&gt;"xsi"&lt;/span&gt;, Constants.xsi.NamespaceName);
var xs = &lt;span&gt;new&lt;/span&gt; XmlSerializer( entity.GetType(), &lt;span&gt;new&lt;/span&gt; XmlRootAttribute(&lt;span&gt;typeof&lt;/span&gt;(T).Name)
); XmlWriterSettings xws = &lt;span&gt;new&lt;/span&gt; XmlWriterSettings();
xws.Indent = &lt;span&gt;true&lt;/span&gt;; xws.OmitXmlDeclaration = &lt;span&gt;true&lt;/span&gt;; &lt;span&gt;using&lt;/span&gt; (var
ms = &lt;span&gt;new&lt;/span&gt; MemoryStream()) { &lt;span&gt;using&lt;/span&gt; (XmlWriter
writer = XmlWriter.Create(ms, xws)) { xs.Serialize(writer, entity, namespaces); ms.Position
= 0; &lt;span&gt;//reset to beginning&lt;/span&gt; &lt;span&gt;using&lt;/span&gt; (var
sr = &lt;span&gt;new&lt;/span&gt; StreamReader(ms)) { &lt;span&gt;return&lt;/span&gt; sr.ReadToEnd();
} } } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
Deserialization is even easier since we are starting with the XML representation and
don't have to build a Stream in memory.
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; SsdsEntity&amp;lt;T&amp;gt;
Deserialize(XElement node) { var xs = &lt;span&gt;new&lt;/span&gt; XmlSerializer( &lt;span&gt;typeof&lt;/span&gt;(SsdsEntity&amp;lt;T&amp;gt;), &lt;span&gt;new&lt;/span&gt; XmlRootAttribute(&lt;span&gt;typeof&lt;/span&gt;(T).Name)
); &lt;span&gt;//xml.CreateReader() cannot be used as it won't support
base64 content&lt;/span&gt; XmlTextReader reader = &lt;span&gt;new&lt;/span&gt; XmlTextReader(
node.ToString(), XmlNodeType.Document, &lt;span&gt;null&lt;/span&gt;); &lt;span&gt;return&lt;/span&gt; (SsdsEntity&amp;lt;T&amp;gt;)xs.Deserialize(reader);
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
If you notice, I am using an &lt;strong&gt;XmlTextReader&lt;/strong&gt; to pass to the &lt;strong&gt;XmlSerializer&lt;/strong&gt;.&amp;nbsp;
Unfortunately, the &lt;strong&gt;XmlReader&lt;/strong&gt; from XLINQ does not support handling
of base64 content, so this workaround is necessary.
&lt;/p&gt;
&lt;p&gt;
At this point, we have a working serializer/deserializer that can handle arbitrary
POCOs.&amp;nbsp; There are some limitations of course:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;font color="#555555"&gt;We are limited to the same datatypes that SSDS supports.&amp;nbsp;
This also means nested objects and arrays are not directly supported.&lt;/font&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;font color="#555555"&gt;We have lost a little of the 'flexible' in the Flexible Entity
(the E in the &lt;a href="http://dunnry.com/blog/EntitiesContainersAndAuthorities.aspx"&gt;ACE
model&lt;/a&gt;).&amp;nbsp; We now have a rigid schema defined by SSDS metadata and T public
properties and enforced on our objects.&lt;/font&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
In my next post, I will attempt to address some of those limitations and I will introduce
a library that handles most of this for you.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=2021b648-8af2-4702-95f5-c5ec1455c1e1" /&gt;</description>
      <link>http://dunnry.com/blog/WorkingWithObjectsInSSDSPart1.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,2021b648-8af2-4702-95f5-c5ec1455c1e1.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,2021b648-8af2-4702-95f5-c5ec1455c1e1.aspx</guid>
      <pubDate>Tue, 17 Jun 2008 22:17:10 GMT</pubDate>
    </item>
    <item>
      <title>LINQPad - not just for LINQ</title>
      <description>&lt;p&gt;
I officially love &lt;a href="http://www.linqpad.net/"&gt;LINQPad&lt;/a&gt;.&amp;nbsp; Joe Albahari
has done a great job of introducing a light weight tool that is great for learning
and prototyping LINQ queries.&amp;nbsp; From what I gather, Joe and Ben Albahari built
this tool as part of &lt;a href="http://www.amazon.com/exec/obidos/ASIN/0596527578/extemporaneou-20-20"&gt;their
book&lt;/a&gt; offering.&amp;nbsp; It was so useful, it has taken on a life of its own.
&lt;/p&gt;
&lt;p&gt;
It may not be entirely obvious, but it turns out don't have to use LINQPad solely
for LINQ queries.&amp;nbsp; You can actually prototype any type of snippet of code.&amp;nbsp;
I have been using it now instead of &lt;a href="http://www.sliver.com/dotnet/SnippetCompiler/"&gt;SnippetCompiler&lt;/a&gt; (another
great quick snippet tool).
&lt;/p&gt;
&lt;p&gt;
As an example, here is how to use System.DirectoryServices snippets inside of LINQPad:
&lt;/p&gt;
&lt;p&gt;
Hit F4 to bring up the Advanced Query Properties Window
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/content/binary/WindowsLiveWriter/LINQPadnotjustforLINQ_9627/image_2.png"&gt;&lt;img height="364" alt="image" src="http://dunnry.com/blog/content/binary/WindowsLiveWriter/LINQPadnotjustforLINQ_9627/image_thumb.png" width="461" border="0"&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
Add the System.DirectoryServices.dll reference in the Additional References window,
and then add "System.DirectoryServices" in the Additional Namespace Imports window.
&lt;/p&gt;
&lt;p&gt;
Now, just type your code normally and hit F5 when you are done:
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/content/binary/WindowsLiveWriter/LINQPadnotjustforLINQ_9627/image_6.png"&gt;&lt;img height="352" alt="image" src="http://dunnry.com/blog/content/binary/WindowsLiveWriter/LINQPadnotjustforLINQ_9627/image_thumb_2.png" width="457" border="0"&gt;&lt;/a&gt;&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
This is a great little tool to have as you can query databases, build LINQ expressions,
and visually inspect the results that come back pretty easily.&amp;nbsp; Now, as you can
see you can also execute arbitrary code snippets as well.&amp;nbsp; Highly recommended.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=ada84666-9fb7-4142-8764-adff11fd8552" /&gt;</description>
      <link>http://dunnry.com/blog/LINQPadNotJustForLINQ.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,ada84666-9fb7-4142-8764-adff11fd8552.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,ada84666-9fb7-4142-8764-adff11fd8552.aspx</guid>
      <pubDate>Wed, 11 Jun 2008 12:47:50 GMT</pubDate>
    </item>
    <item>
      <title>Paged Asynchronous LDAP Searches Revisited</title>
      <description>&lt;p&gt;
A member in the &lt;a href="http://directoryprogramming.net/forums/9/ShowForum.aspx"&gt;book's
forum&lt;/a&gt; mentioned some code I had originally posted &lt;a href="http://dunnry.com/blog/AsynchronousLDAPSearchingWithSystemDirectoryServicesProtocols.aspx"&gt;here&lt;/a&gt; in
the blog for asynchronous, paged searches in System.DirectoryServices.Protocols (SDS.P).&amp;nbsp;
He questioned whether or not it was thread safe.&amp;nbsp; I honestly don't know - it
might not be as I didn't test it extensively.
&lt;/p&gt;
&lt;p&gt;
Regardless, I had actually moved on from that code and started using anonymous delegates
for callbacks instead of events.&amp;nbsp; I liked this pattern a bit better because it
also got rid of the shared resources.
&lt;/p&gt;
&lt;p&gt;
After reading Stephen Toub's &lt;a href="http://msdn.microsoft.com/en-us/magazine/cc337900.aspx"&gt;article&lt;/a&gt; on
asynchronous stream processing, I learned about the AsyncOperationManager which was
something I was missing in my implementation.&amp;nbsp; I have been doing a lot lately
with .NET 3.5, LINQ, and lambda expressions, so I also decided to rewrite the anonymous
delegates to lambda expressions.&amp;nbsp; That is not as big a change, but it is more
concise.
&lt;/p&gt;
&lt;p&gt;
I actively investigated using &lt;a href="http://tomasp.net/blog/csharp-async.aspx"&gt;async
iterators&lt;/a&gt;, but ultimately I decided closures seemed to be more intuitive for me.&amp;nbsp;
I might revisit this at some time and change my mind.&amp;nbsp; Here is my outcome:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; AsyncSearcher
{ LdapConnection _connect; &lt;span&gt;public&lt;/span&gt; AsyncSearcher(LdapConnection
connection) { &lt;span&gt;this&lt;/span&gt;._connect = connection; &lt;span&gt;this&lt;/span&gt;._connect.AutoBind
= &lt;span&gt;true&lt;/span&gt;; &lt;span&gt;//will bind
on first search&lt;/span&gt; } &lt;span&gt;public&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; BeginPagedSearch( &lt;span&gt;string&lt;/span&gt; baseDN, &lt;span&gt;string&lt;/span&gt; filter, &lt;span&gt;string&lt;/span&gt;[]
attribs, &lt;span&gt;int&lt;/span&gt; pageSize, Action&amp;lt;SearchResponse&amp;gt;
page, Action&amp;lt;Exception&amp;gt; completed ) { &lt;span&gt;if&lt;/span&gt; (page
== &lt;span&gt;null&lt;/span&gt;) &lt;span&gt;throw&lt;/span&gt; &lt;span&gt;new&lt;/span&gt; ArgumentNullException(&lt;span&gt;"page"&lt;/span&gt;);
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(&lt;span&gt;null&lt;/span&gt;);
Action&amp;lt;Exception&amp;gt; done = e =&amp;gt; { &lt;span&gt;if&lt;/span&gt; (completed
!= &lt;span&gt;null&lt;/span&gt;) asyncOp.Post(&lt;span&gt;delegate&lt;/span&gt; {
completed(e); }, &lt;span&gt;null&lt;/span&gt;); }; SearchRequest request
= &lt;span&gt;new&lt;/span&gt; SearchRequest( baseDN, filter, System.DirectoryServices.Protocols.SearchScope.Subtree,
attribs ); PageResultRequestControl prc = &lt;span&gt;new&lt;/span&gt; PageResultRequestControl(pageSize); &lt;span&gt;//add
the paging control&lt;/span&gt; request.Controls.Add(prc); AsyncCallback rc = &lt;span&gt;null&lt;/span&gt;;
rc = readResult =&amp;gt; { &lt;span&gt;try&lt;/span&gt; { var response = (SearchResponse)_connect.EndSendRequest(readResult); &lt;span&gt;//let
current thread handle results&lt;/span&gt; asyncOp.Post(&lt;span&gt;delegate&lt;/span&gt; {
page(response); }, &lt;span&gt;null&lt;/span&gt;); var cookie = response.Controls
.Where(c =&amp;gt; c &lt;span&gt;is&lt;/span&gt; PageResultResponseControl)
.Select(s =&amp;gt; ((PageResultResponseControl)s).Cookie) .Single(); &lt;span&gt;if&lt;/span&gt; (cookie
!= &lt;span&gt;null&lt;/span&gt; &amp;amp;&amp;amp; cookie.Length != 0) { prc.Cookie
= cookie; _connect.BeginSendRequest( request, PartialResultProcessing.NoPartialResultSupport,
rc, &lt;span&gt;null&lt;/span&gt; ); } &lt;span&gt;else&lt;/span&gt; done(&lt;span&gt;null&lt;/span&gt;); &lt;span&gt;//signal
complete&lt;/span&gt; } &lt;span&gt;catch&lt;/span&gt; (Exception ex) { done(ex);
} }; &lt;span&gt;//kick off async&lt;/span&gt; &lt;span&gt;try&lt;/span&gt; {
_connect.BeginSendRequest( request, PartialResultProcessing.NoPartialResultSupport,
rc, &lt;span&gt;null&lt;/span&gt; ); } &lt;span&gt;catch&lt;/span&gt; (Exception
ex) { done(ex); } } }&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
It can be consumed very easily using something like this:
&lt;/p&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;class&lt;/span&gt; Program
{ &lt;span&gt;static&lt;/span&gt; ManualResetEvent _resetEvent = &lt;span&gt;new&lt;/span&gt; ManualResetEvent(&lt;span&gt;false&lt;/span&gt;); &lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Main(&lt;span&gt;string&lt;/span&gt;[]
args) { &lt;span&gt;//set these to your environment&lt;/span&gt; &lt;span&gt;string&lt;/span&gt; servername
= &lt;span&gt;"server.yourdomain.com"&lt;/span&gt;; &lt;span&gt;string&lt;/span&gt; baseDN
= &lt;span&gt;"dc=yourdomain,dc=com"&lt;/span&gt;; &lt;span&gt;using&lt;/span&gt; (LdapConnection
connection = CreateConnection(servername)) { AsyncSearcher searcher = &lt;span&gt;new&lt;/span&gt; AsyncSearcher(connection);
searcher.BeginPagedSearch( baseDN, &lt;span&gt;"(sn=Dunn)"&lt;/span&gt;, &lt;span&gt;null&lt;/span&gt;,
100, f =&amp;gt; &lt;span&gt;//runs per page&lt;/span&gt; { &lt;span&gt;foreach&lt;/span&gt; (var
item &lt;span&gt;in&lt;/span&gt; f.Entries) { var entry = item &lt;span&gt;as&lt;/span&gt; SearchResultEntry; &lt;span&gt;if&lt;/span&gt; (entry
!= &lt;span&gt;null&lt;/span&gt;) { Console.WriteLine(entry.DistinguishedName);
} } }, c =&amp;gt; &lt;span&gt;//runs on error or when done&lt;/span&gt; { &lt;span&gt;if&lt;/span&gt; (c
!= &lt;span&gt;null&lt;/span&gt;) Console.WriteLine(c.ToString()); Console.WriteLine(&lt;span&gt;"Done"&lt;/span&gt;);
_resetEvent.Set(); } ); _resetEvent.WaitOne(); } Console.WriteLine(); Console.WriteLine(&lt;span&gt;"Finished....
Press Enter to Continue."&lt;/span&gt;); Console.ReadLine(); } &lt;span&gt;static&lt;/span&gt; LdapConnection
CreateConnection(&lt;span&gt;string&lt;/span&gt; server) { LdapConnection
connect = &lt;span&gt;new&lt;/span&gt; LdapConnection( &lt;span&gt;new&lt;/span&gt; LdapDirectoryIdentifier(server), &lt;span&gt;null&lt;/span&gt;,
AuthType.Negotiate ); connect.SessionOptions.ProtocolVersion = 3; connect.SessionOptions.ReferralChasing
= ReferralChasingOptions.None; connect.SessionOptions.Sealing = &lt;span&gt;true&lt;/span&gt;;
connect.SessionOptions.Signing = &lt;span&gt;true&lt;/span&gt;; &lt;span&gt;return&lt;/span&gt; connect;
} } &lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
The important thing to note is that because everything is running asynchronously,
it is totally possible for the end delegate to be invoked before the paging delegate
has a chance to finish processing results (depending on how complicated your code
is).&amp;nbsp; You would need to compensate for this yourself.
&lt;/p&gt;
&lt;p&gt;
This client is a console application, so I am using a ManualResetEvent just to prevent
it from closing before finishing.&amp;nbsp; You wouldn't need to do this in a WinForms
or WPF app.
&lt;/p&gt;
&lt;p&gt;
I am sure there are other optimizations you could make to pass in parameters or even
other directory controls.&amp;nbsp; However, the general pattern should apply.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=982fe6ab-e507-4e65-b168-e8c2d3d72a9f" /&gt;</description>
      <link>http://dunnry.com/blog/PagedAsynchronousLDAPSearchesRevisited.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,982fe6ab-e507-4e65-b168-e8c2d3d72a9f.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,982fe6ab-e507-4e65-b168-e8c2d3d72a9f.aspx</guid>
      <pubDate>Thu, 05 Jun 2008 13:56:28 GMT</pubDate>
    </item>
    <item>
      <title>PhluffyFotos Sample Available</title>
      <description>&lt;p&gt;
I just posted the first version of &lt;a href="http://www.codeplex.com/phluffyfotos"&gt;PhluffyFotos&lt;/a&gt;,
our SQL Server Data Services (SSDS) sample app to CodePlex.&amp;nbsp; PhluffyFotos is
a photo sharing site that allows users to upload photos and metadata (tags, description)
to SSDS for storage.&amp;nbsp; As the service gets more features and is updated, the sample
will be rev'd as well.
&lt;/p&gt;
&lt;p&gt;
Points of interest that will likely also be blog posts in themselves:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
This sample has a LINQ-to-SSDS provider in it.&amp;nbsp; You will notice we don't use
any strings for queries, but rather lambda expressions.&amp;nbsp; I had a lot of fun writing
the first version of this and I would expect that there are a few more revisions here
to go.&amp;nbsp; Of course, &lt;a href="http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx"&gt;Matt
Warren&lt;/a&gt; should get a ton of credit here for providing the base implementation.&lt;/li&gt;
&lt;li&gt;
This sample also uses a very simplistic ASP.NET Role provider for SSDS.&amp;nbsp; Likely
updates here will include encryption and hashing support.&lt;/li&gt;
&lt;li&gt;
We have a number of Powershell cmdlets included for managing authorities and containers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
I have many other ideas for this app as time progresses, so you should check back
from time to time to see the updates.
&lt;/p&gt;
&lt;p&gt;
In case anyone was wondering about the name: clouds are fluffy... get it?
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;&lt;em&gt;You need to have SSDS credentials to run this sample.&amp;nbsp; If you don't
have credentials yet, you can see an online version until then at &lt;a href="http://www.phluffyfotos.com"&gt;http://www.phluffyfotos.com&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
Even if you don' t have access to SSDS credentials yet, the code is worth taking a
look.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=6d66af42-11f1-4b07-a4a6-ab83fa44be0a" /&gt;</description>
      <link>http://dunnry.com/blog/PhluffyFotosSampleAvailable.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,6d66af42-11f1-4b07-a4a6-ab83fa44be0a.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,6d66af42-11f1-4b07-a4a6-ab83fa44be0a.aspx</guid>
      <pubDate>Wed, 09 Apr 2008 14:02:37 GMT</pubDate>
    </item>
    <item>
      <title>DEC 2008 Wrap Up and Materials</title>
      <description>&lt;p&gt;
It seems like I've deteriorated into semi-annual blog posts.&amp;nbsp; Sigh.&amp;nbsp; At
least the discussion groups at &lt;a href="http://www.directoryprogramming.net"&gt;www.directoryprogramming.net&lt;/a&gt; continue
to flourish and we are seeing a nice uptick in activity on the ADFS board there.&amp;nbsp;
I think the writing may be on the wall for me as a blogger, but who knows.&amp;nbsp; Perhaps
I'll get back on the wagon.
&lt;/p&gt;
&lt;p&gt;
Anyway, thanks to all the people who came to see my talks at DEC this year.&amp;nbsp;
I hope you enjoyed visiting my town and you got a lot out of the conference itself.&amp;nbsp;
DEC is one of my favorites and I'm happy to see it continue to do well.&amp;nbsp; I got
a lot of nice feedback on both of my talks and I'm always interested to hear what
you thought.
&lt;/p&gt;
&lt;p&gt;
My first talk this year was on customizing ADFS.&amp;nbsp; To my knowledge, this type
of stuff has never been talked about publicly before, so the session was a bit of
an experiment.&amp;nbsp; I essentially tried to cover all the different types of things
you should and could do to ADFS V1 or 1.1 (2003 R2 server or 2008 server) to make
it do different things.&amp;nbsp; It started off discussing some cosmetics to apply to
the pages displayed by the FS, moved through ADAM account store tweaks and then covered
custom claim transformation modules and some advanced hacks/mods such as non-Windows
authentication.&amp;nbsp; Some of that stuff has been discussed here on this blog and
nearly all of it was based on stuff we've actually done at work, so basically real
world experience.&amp;nbsp; My fear was that the subject matter would be a little over
the audience's head since it wasn't introductory at all (assumed you already knew
ADFS pretty well) and covered some developer stuff which might have seemed alien to
many of the audience members who tend to be more of the IT Pro sort.&amp;nbsp; Still,
I think it was valuable stuff.
&lt;/p&gt;
&lt;p&gt;
I mentioned a sample custom claim transform module that I'd post the source to.&amp;nbsp;
I'll follow that up in a separate post shortly.
&lt;/p&gt;
&lt;p&gt;
The audiences for all the ADFS talks were pretty small by comparison to the big AD
talks, but this doesn't surprise me.&amp;nbsp; Not only is ADFS still a new thing, but
to a great extent it is a luxury for most DEC attendees to be able to go to those
sessions.&amp;nbsp; ADFS largely assumes that both the directory and the identity provisioning
stuff is all a solved problem and that we have rich repositories filled with security
principals whose identity is trustworthy and stuffed with useful metadata that can
be converted to claims.&amp;nbsp; For many, that is not (yet) a solved problem at all
and federated identity is still largely wishful thinking.&amp;nbsp; Still, you have to
start somewhere.
&lt;/p&gt;
&lt;p&gt;
My second talk was essentially the talk I've done at DEC now for 3 years, although
this time modified heavily to cover the new .NET 3.5 stuff in System.DirectoryServices.AccountManagement.&amp;nbsp;
I was really dreading this talk and didn't finish the slides for it before the conference,
so between the last minute PPT work and the anxiety, I only got about an hour of sleep.
&lt;/p&gt;
&lt;p&gt;
Still, the talk seemed to go off pretty well.&amp;nbsp; I was in the big room this time
and in one of the last slots of the conference, so I had low expectations for turnout.&amp;nbsp;
It seemed like I had a pretty good crowd though (not the Dean and Joe show, but I'll
take it!) and people seemed to be into it.&amp;nbsp; I think it was probably the best
version of that talk I've done to date, so all in all I'm quite happy.&amp;nbsp; I miss
having &lt;a href="http://www.dunnry.com/blog/"&gt;Ryan&lt;/a&gt; there to bounce stuff around
with, but so it goes.
&lt;/p&gt;
&lt;p&gt;
Thanks also to &lt;a href="http://blogs.msdn.com/donovanf/"&gt;Donovan&lt;/a&gt; for inviting
me up for his case studies talk and giving me an opportunity to talk about some of
the real world stuff we are doing with federation at work and talking about the process
and legal stuff as much as the technical aspects.&amp;nbsp; People clearly have as many
if not more questions about those things than the engineering parts.
&lt;/p&gt;
&lt;p&gt;
I think I finally get CardSpace now, especially as it applies to the enterprise, and
am looking forward to having a chance to get it running internally.&amp;nbsp; That should
be interesting.&amp;nbsp; Stuart, I need some bits I can actually deploy.&amp;nbsp; :)&amp;nbsp;
Thanks to &lt;a href="http://pamelaproject.com/"&gt;Pamela Dingle&lt;/a&gt; for coming to DEC
and bringing both the CardSpace love and the non-MS platform perspective.&amp;nbsp; I'm
anxious to go to a conference where everyone knows her and no one knows me at all
and see how I do.
&lt;/p&gt;
&lt;p&gt;
She's got a nice &lt;a href="http://eternaloptimist.wordpress.com/2008/03/10/dec-2008-this-ones-for-you-wook/"&gt;follow
up&lt;/a&gt; on the Wook Lee Challenge this year which I played a tiny role in.
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/Customizing-ADFS.zip"&gt;Customizing-ADFS.zip
(1.06 MB)&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/DotNet-DS-Programming.zip"&gt;DotNet-DS-Programming.zip
(1.27 MB)&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=c59ba3ad-6e76-4aa0-9117-e52626283b4e" /&gt;</description>
      <link>http://www.joekaplan.net/DEC2008WrapUpAndMaterials.aspx</link>
      <source url="http://www.joekaplan.net/">Joe Kaplan</source>
      <comments>http://www.joekaplan.net/CommentView,guid,c59ba3ad-6e76-4aa0-9117-e52626283b4e.aspx</comments>
      <guid isPermaLink="False">http://www.joekaplan.net/PermaLink,guid,c59ba3ad-6e76-4aa0-9117-e52626283b4e.aspx</guid>
      <pubDate>Tue, 11 Mar 2008 23:41:56 GMT</pubDate>
    </item>
    <item>
      <title>.NET 3.5 VPC and Resources Available</title>
      <description>&lt;p&gt;
If you are interested in learning more about Visual Studio 2008, make sure you check
out the &lt;a href="http://go.microsoft.com/?linkid=7602397"&gt;Visual Studio 2008 Training
Kit&lt;/a&gt;.&amp;#160; Weighing in at roughly 120MB compressed, it contains, &amp;quot;&lt;em&gt;a full
5-days of technical content including 20 hands-on labs, 28 presentations, and 20 scripted
demos.&amp;#160;&amp;#160; The technologies covered in the kit include:&amp;#160; LINQ, C# 3.0,
VB 9, WCF, WF, WPF, Windows CardSpace, Silverlight, ASP.NET Ajax, .NET Compact Framework
3.5, VSTO 3.0, Visual Studio Team System, and Team Foundation Server&lt;/em&gt;&amp;quot;.
&lt;/p&gt;
&lt;p&gt;
Naturally, you will want to have a machine setup to run all these labs and samples...
so, thanks to the hard work of &lt;a href="http://blogs.msdn.com/daiken/default.aspx"&gt;David&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/jamescon/default.aspx"&gt;James&lt;/a&gt;,
you can now download a VPC with Vista, Visual Studio 2008 (trial), and the .NET 3.5
framework pre-loaded and ready to run.&amp;#160; &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=3ca34c20-0b28-4c1a-87cd-33ce952611e7&amp;amp;displaylang=en"&gt;Get
it here&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
You want more?&amp;#160; Ok, how about 17 training videos describing the technologies
and running through a number of demos?&amp;#160; &lt;a href="http://channel9.msdn.com/Showforum.aspx?forumid=38&amp;amp;tagid=267"&gt;Get
those here&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
These are truly some great resources to get you jumpstarted!
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=b763b823-288e-4b14-9337-72f0355f29cb" /&gt;</description>
      <link>http://dunnry.com/blog/NET35VPCAndResourcesAvailable.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,b763b823-288e-4b14-9337-72f0355f29cb.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,b763b823-288e-4b14-9337-72f0355f29cb.aspx</guid>
      <pubDate>Mon, 03 Dec 2007 13:11:56 GMT</pubDate>
    </item>
    <item>
      <title>Implementing Change Notifications in .NET</title>
      <description>&lt;p&gt;
There are three ways of figuring out things that have changed in Active Directory
(or ADAM).&amp;nbsp; These have been documented for some time over at MSDN in the aptly
titled "&lt;a href="http://msdn2.microsoft.com/en-us/library/ms677625.aspx"&gt;Overview
of Change Tracking Techniques"&lt;/a&gt;.&amp;nbsp; In summary:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/ms677627.aspx"&gt;Polling for Changes
using uSNChanged&lt;/a&gt;. This technique checks the 'highestCommittedUSN' value to start
and then performs searches for 'uSNChanged' values that are higher subsequently.&amp;nbsp;
The 'uSNChanged' attribute is not replicated between domain controllers, so you must
go back to the same domain controller each time for consistency.&amp;nbsp; Essentially,
you perform a search looking for the highest 'uSNChanged' value + 1 and then read
in the results tracking them in any way you wish. 
&lt;ul&gt;
&lt;li&gt;
Benefits 
&lt;ul&gt;
&lt;li&gt;
This is the most compatible way.&amp;nbsp; All languages and all versions of .NET support
this way since it is a simple search. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;
Disadvantages 
&lt;ul&gt;
&lt;li&gt;
There is a lot here for the developer to take care of.&amp;nbsp; You get the entire object
back, and you must determine what has changed on the object (and if you care about
that change). 
&lt;li&gt;
Dealing with deleted objects is a pain. 
&lt;li&gt;
This is a polling technique, so it is only as real-time as how often you query.&amp;nbsp;
This can be a good thing depending on the application. Note, intermediate values are
not tracked here either. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/ms677626.aspx"&gt;Polling for Changes
Using the DirSync Control&lt;/a&gt;.&amp;nbsp; This technique uses the ADS_SEARCHPREF_DIRSYNC
option in ADSI and the LDAP_SERVER_DIRSYNC_OID control under the covers.&amp;nbsp; Simply
make an initial search, store the cookie, and then later search again and send the
cookie.&amp;nbsp; It will return only the objects that have changed. 
&lt;ul&gt;
&lt;li&gt;
Benefits 
&lt;ul&gt;
&lt;li&gt;
This is an easy model to follow.&amp;nbsp; Both System.DirectoryServices and System.DirectoryServices.Protocols
support this option. 
&lt;li&gt;
Filtering can reduce what you need to bother with.&amp;nbsp; As an example, if my initial
search is for all users "(objectClass=user)", I can subsequently filter on polling
with "(sn=dunn)" and only get back the combination of both filters, instead of having
to deal with everything from the intial filter. 
&lt;li&gt;
Windows 2003+ option removes the administrative limitation for using this option (object
security). 
&lt;li&gt;
Windows 2003+ option will also give you the ability to return only the incremental
values that have changed in large multi-valued attributes.&amp;nbsp; This is a really
nice feature. 
&lt;li&gt;
Deals well with deleted objects. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;
Disadvantages 
&lt;ul&gt;
&lt;li&gt;
This is .NET 2.0+ or later only option.&amp;nbsp; Users of .NET 1.1 will need to use uSNChanged
Tracking.&amp;nbsp; Scripting languages cannot use this method. 
&lt;li&gt;
You can only scope the search to a partition.&amp;nbsp; If you want to track only a particular
OU or object, you must sort out those results yourself later. 
&lt;li&gt;
Using this with non-Windows 2003 mode domains comes with the restriction that you
must have replication get changes permissions (default only admin) to use. 
&lt;li&gt;
This is a polling technique.&amp;nbsp; It does not track intermediate values either.&amp;nbsp;
So, if an object you want to track changes between the searches multiple times, you
will only get the last change.&amp;nbsp; This can be an advantage depending on the application. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;
&lt;a href="http://msdn2.microsoft.com/en-us/library/aa772153.aspx"&gt;Change Notifications
in Active Directory&lt;/a&gt;.&amp;nbsp; This technique registers a search on a separate thread
that will receive notifications when any object changes that matches the filter.&amp;nbsp;
You can register up to 5 notifications per async connection. 
&lt;ul&gt;
&lt;li&gt;
Benefits 
&lt;ul&gt;
&lt;li&gt;
Instant notification.&amp;nbsp; The other techniques require polling. 
&lt;li&gt;
Because this is a notification, you will get all changes, even the intermediate ones
that would have been lost in the other two techniques. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;
Disadvantages 
&lt;ul&gt;
&lt;li&gt;
Relatively resource intensive.&amp;nbsp; You don't want to do a whole ton of these as
it could cause scalability issues with your controller. 
&lt;li&gt;
This only tells you if the object has changed, but it does not tell you what the change
was.&amp;nbsp; You need to figure out if the attribute you care about has changed or not.&amp;nbsp;
That being said, it is pretty easy to tell if the object has been deleted (easier
than uSNChanged polling at least). 
&lt;li&gt;
You can only do this in unmanaged code or with System.DirectoryServices.Protocols. 
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
For the most part, I have found that DirSync has fit the bill for me in virtually
every situation.&amp;nbsp; I never bothered to try any of the other techniques.&amp;nbsp;
However, a reader asked if there was a way to do the change notifications in .NET.&amp;nbsp;
I figured it was possible using SDS.P, but had never tried it.&amp;nbsp; Turns out, it
is possible and actually not too hard to do.
&lt;/p&gt;
&lt;p&gt;
My first thought on writing this was to use the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms676877.aspx"&gt;sample
code&lt;/a&gt; found on MSDN (and referenced from option #3) and simply convert this to
System.DirectoryServices.Protocols.&amp;nbsp; This turned out to be a dead end.&amp;nbsp;
The way you do it in SDS.P and the way the sample code works are different enough
that it is of no help.&amp;nbsp; Here is the solution I came up with:
&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; ChangeNotifier
: IDisposable&lt;/pre&gt;
&lt;pre&gt;{&lt;/pre&gt;
&lt;pre&gt;    LdapConnection _connection;&lt;/pre&gt;
&lt;pre&gt;    HashSet&amp;lt;IAsyncResult&amp;gt; _results = &lt;span&gt;new&lt;/span&gt; HashSet&amp;lt;IAsyncResult&amp;gt;();&lt;/pre&gt;
&lt;pre&gt;    &lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;public&lt;/span&gt; ChangeNotifier(LdapConnection
connection)&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        _connection = connection;&lt;/pre&gt;
&lt;pre&gt;        _connection.AutoBind = &lt;span&gt;true&lt;/span&gt;;&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;public&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Register(&lt;span&gt;string&lt;/span&gt; dn,
SearchScope scope)&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        SearchRequest request = &lt;span&gt;new&lt;/span&gt; SearchRequest(&lt;/pre&gt;
&lt;pre&gt;            dn, &lt;span&gt;//root
the search here&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;            &lt;span&gt;"(objectClass=*)"&lt;/span&gt;, &lt;span&gt;//very
inclusive&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;            scope, &lt;span&gt;//any
scope works&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;            &lt;span&gt;null&lt;/span&gt; &lt;span&gt;//we
are interested in all attributes&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;            );&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;//register
our search&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;        request.Controls.Add(&lt;span&gt;new&lt;/span&gt; DirectoryNotificationControl());&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;//we
will send this async and register our callback&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;//note
how we would like to have partial results&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;        IAsyncResult result = _connection.BeginSendRequest(&lt;/pre&gt;
&lt;pre&gt;            request,&lt;/pre&gt;
&lt;pre&gt;            TimeSpan.FromDays(1), &lt;span&gt;//set
timeout to a day...&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;            PartialResultProcessing.ReturnPartialResultsAndNotifyCallback,&lt;/pre&gt;
&lt;pre&gt;            Notify,&lt;/pre&gt;
&lt;pre&gt;            request&lt;/pre&gt;
&lt;pre&gt;            );&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;//store
the hash for disposal later&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;        _results.Add(result);&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;private&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Notify(IAsyncResult
result)&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;//since
our search is long running, we don't want to use EndSendRequest&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;        PartialResultsCollection prc = _connection.GetPartialResults(result);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;foreach&lt;/span&gt; (SearchResultEntry
entry &lt;span&gt;in&lt;/span&gt; prc)&lt;/pre&gt;
&lt;pre&gt;        {&lt;/pre&gt;
&lt;pre&gt;            OnObjectChanged(&lt;span&gt;new&lt;/span&gt; ObjectChangedEventArgs(entry));&lt;/pre&gt;
&lt;pre&gt;        }&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;private&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; OnObjectChanged(ObjectChangedEventArgs
args)&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;if&lt;/span&gt; (ObjectChanged
!= &lt;span&gt;null&lt;/span&gt;)&lt;/pre&gt;
&lt;pre&gt;        {&lt;/pre&gt;
&lt;pre&gt;            ObjectChanged(&lt;span&gt;this&lt;/span&gt;,
args);&lt;/pre&gt;
&lt;pre&gt;        }&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;public&lt;/span&gt; &lt;span&gt;event&lt;/span&gt; EventHandler&amp;lt;ObjectChangedEventArgs&amp;gt;
ObjectChanged;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;#region&lt;/span&gt; IDisposable
Members&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;public&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Dispose()&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;foreach&lt;/span&gt; (var
result &lt;span&gt;in&lt;/span&gt; _results)&lt;/pre&gt;
&lt;pre&gt;        {&lt;/pre&gt;
&lt;pre&gt;            &lt;span&gt;//end
each async search&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;            _connection.Abort(result);&lt;/pre&gt;
&lt;pre&gt;        }&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;#endregion&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;}&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; ObjectChangedEventArgs
: EventArgs&lt;/pre&gt;
&lt;pre&gt;{&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;public&lt;/span&gt; ObjectChangedEventArgs(SearchResultEntry
entry)&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        Result = entry;&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;public&lt;/span&gt; SearchResultEntry
Result { get; set;}&lt;/pre&gt;
&lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
It is a relatively simple class that you can use to register searches.&amp;nbsp; The trick
is using the GetPartialResults method in the callback method to get only the change
that has just occurred.&amp;nbsp; I have also included the very simplified EventArgs class
I am using to pass results back.&amp;nbsp; Note, I am not doing anything about threading
here and I don't have any error handling (this is just a sample).&amp;nbsp; You can consume
this class like so:
&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;pre&gt;&lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Main(&lt;span&gt;string&lt;/span&gt;[]
args)&lt;/pre&gt;
&lt;pre&gt;{&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;using&lt;/span&gt; (LdapConnection
connect = CreateConnection(&lt;span&gt;"localhost"&lt;/span&gt;))&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;using&lt;/span&gt; (ChangeNotifier
notifier = &lt;span&gt;new&lt;/span&gt; ChangeNotifier(connect))&lt;/pre&gt;
&lt;pre&gt;        {&lt;/pre&gt;
&lt;pre&gt;            &lt;span&gt;//register
some objects for notifications (limit 5)&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;            notifier.Register(&lt;span&gt;"dc=dunnry,dc=net"&lt;/span&gt;,
SearchScope.OneLevel);&lt;/pre&gt;
&lt;pre&gt;            notifier.Register(&lt;span&gt;"cn=testuser1,ou=users,dc=dunnry,dc=net"&lt;/span&gt;,
SearchScope.Base);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;            notifier.ObjectChanged += &lt;span&gt;new&lt;/span&gt; EventHandler&amp;lt;ObjectChangedEventArgs&amp;gt;(notifier_ObjectChanged);&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;            Console.WriteLine(&lt;span&gt;"Waiting
for changes..."&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;            Console.WriteLine();&lt;/pre&gt;
&lt;pre&gt;            Console.ReadLine();&lt;/pre&gt;
&lt;pre&gt;        }&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;}&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; notifier_ObjectChanged(&lt;span&gt;object&lt;/span&gt; sender,
ObjectChangedEventArgs e)&lt;/pre&gt;
&lt;pre&gt;{&lt;/pre&gt;
&lt;pre&gt;    Console.WriteLine(e.Result.DistinguishedName);&lt;/pre&gt;
&lt;pre&gt;    &lt;span&gt;foreach&lt;/span&gt; (&lt;span&gt;string&lt;/span&gt; attrib &lt;span&gt;in&lt;/span&gt; e.Result.Attributes.AttributeNames)&lt;/pre&gt;
&lt;pre&gt;    {&lt;/pre&gt;
&lt;pre&gt;        &lt;span&gt;foreach&lt;/span&gt; (var
item &lt;span&gt;in&lt;/span&gt; e.Result.Attributes[attrib].GetValues(&lt;span&gt;typeof&lt;/span&gt;(&lt;span&gt;string&lt;/span&gt;)))&lt;/pre&gt;
&lt;pre&gt;        {&lt;/pre&gt;
&lt;pre&gt;            Console.WriteLine(&lt;span&gt;"\t{0}:
{1}"&lt;/span&gt;, attrib, item);&lt;/pre&gt;
&lt;pre&gt;        }&lt;/pre&gt;
&lt;pre&gt;    }&lt;/pre&gt;
&lt;pre&gt;    Console.WriteLine();&lt;/pre&gt;
&lt;pre&gt;    Console.WriteLine(&lt;span&gt;"===================="&lt;/span&gt;);&lt;/pre&gt;
&lt;pre&gt;    Console.WriteLine();&lt;/pre&gt;
&lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
And there you have it... change notifications in .NET.&amp;nbsp; You can also &lt;a href="http://cid-4225352e17899c6d.skydrive.live.com/self.aspx/Public/ChangeNotifications.zip"&gt;download
my project file&lt;/a&gt; for Visual Studio 2008.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=b1e8405e-8e34-4692-83bf-10fa2e69e6c4" /&gt;</description>
      <link>http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,b1e8405e-8e34-4692-83bf-10fa2e69e6c4.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,b1e8405e-8e34-4692-83bf-10fa2e69e6c4.aspx</guid>
      <pubDate>Tue, 30 Oct 2007 13:14:23 GMT</pubDate>
    </item>
    <item>
      <title>Welcome to the world, baby Micah!</title>
      <description>&lt;p&gt;
Given that I haven't posted on this blog for months, I'm not sure if anyone still
reads it, but I thought I'd take a few moments to inform my loyal readers of a new
addition to the family.
&lt;/p&gt;
&lt;p&gt;
Micah Kaplan Yarbrough was born October 15, 2007 at 11:40 AM at Prentise Women's Hospital
(part of Northwestern Memorial Hospital) in downtown Chicago, just like like his older
brother Evan.
&lt;/p&gt;
&lt;p&gt;
He arrived 10 days before he was expected and almost a month sooner than his brother
(who was quite late) and was a little smaller, but still a robust 7lb 11oz and 20.25"
long.
&lt;/p&gt;
&lt;p&gt;
Mom and baby are doing fine.&amp;nbsp; They are both sleeping right now in fact.&amp;nbsp;
We'll be heading home in a few days and start to figure out the mysteries of chasing
two kids around.
&lt;/p&gt;
&lt;p&gt;
Evan is at home with his grandparents right now trying to convince them that he goes
to bed at 10:30 usually and get 10 cookies everynight before bed.
&lt;/p&gt;
&lt;p&gt;
Here is a picture at 1 hour old.&amp;nbsp; He was sleeping then too.&amp;nbsp; He sleeps a
lot so far.&amp;nbsp; He barely ever cries, but I'm sure he's just building up his strength
for later. :)
&lt;/p&gt;
&lt;img src="http://www.joekaplan.net/content/binary/babymicahsmall.jpg" border=0&gt;&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=86b985f5-ff32-4efc-b477-d72517dc98f3" /&gt;</description>
      <link>http://www.joekaplan.net/WelcomeToTheWorldBabyMicah.aspx</link>
      <source url="http://www.joekaplan.net/">Joe Kaplan</source>
      <comments>http://www.joekaplan.net/CommentView,guid,86b985f5-ff32-4efc-b477-d72517dc98f3.aspx</comments>
      <guid isPermaLink="False">http://www.joekaplan.net/PermaLink,guid,86b985f5-ff32-4efc-b477-d72517dc98f3.aspx</guid>
      <pubDate>Tue, 16 Oct 2007 09:23:54 GMT</pubDate>
    </item>
    <item>
      <title>Using SQLDependency objects with LINQ</title>
      <description>&lt;p&gt;
A question came up the other day on how to get LINQ to SQL to participate in using
the SQL Notification Services.&amp;nbsp; Of course, I didn't know, but Mike Pizzo from
the ADO.NET team was kind enough to answer.&amp;nbsp; I figured it must be possible, and
sure enough, it is.&amp;nbsp; Essentially, you have to create a SQL dependency context,
which is very similar to a transaction context.&amp;nbsp; Any code that participates within
that context will automatically be associated with the SQLDependency.&amp;nbsp; Create
the dependency first, before any LINQ (or other data access technology).&amp;nbsp; Here
is the relevant code (note: this code is not optimized, so you might want to do things
like change the SQLDependency to static or pass it in so it won't be garbage collected).
&lt;/p&gt;
&lt;p&gt;

&lt;div&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; &lt;span&gt;GlobalNotifications&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;public&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;event&lt;/span&gt; OnChangeEventHandler
OnChange;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;public&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; InitializeNotifications(&lt;span&gt;string&lt;/span&gt; connectString)
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;//
Initialize notifications&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; SqlDependency.Start(connectString);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;//
Create and register a new dependency&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; SqlDependency dependency
= &lt;span&gt;new&lt;/span&gt; SqlDependency();
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; dependency.OnChange += &lt;span&gt;new&lt;/span&gt; OnChangeEventHandler(NotificationCallback);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; System.Runtime.Remoting.Messaging.&lt;span&gt;CallContext&lt;/span&gt;.SetData(&lt;span&gt;"MS.SqlDependencyCookie"&lt;/span&gt;,
dependency.Id);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;internal&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; NotificationCallback(&lt;span&gt;object&lt;/span&gt; o,
SqlNotificationEventArgs args)
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; OnChange.Invoke(o, args);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;
There is also one major caveat to using this with LINQ: Beware of complex queries
that can be easily generated using LINQ.&amp;nbsp; The Query Processor will invalidate
the command and fire an error event saying it was too complex.&amp;nbsp; Since you can
easily generate lots of complex queries using LINQ (part of its power really), you
need to really be cognizant of this limitation.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=d29c54e5-8dd0-4779-96c0-40c87f07e500" /&gt;</description>
      <link>http://dunnry.com/blog/UsingSQLDependencyObjectsWithLINQ.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,d29c54e5-8dd0-4779-96c0-40c87f07e500.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,d29c54e5-8dd0-4779-96c0-40c87f07e500.aspx</guid>
      <pubDate>Fri, 14 Sep 2007 15:24:19 GMT</pubDate>
    </item>
    <item>
      <title>Range Retrieval using System.DirectoryServices.Protocols</title>
      <description>&lt;p&gt;
Link pair attributes in Active Directory and ADAM can be quite big.&amp;nbsp; I don't
know the official limit, but needless to say, for practical purposes you can assume
they are quite large indeed.&amp;nbsp; By default, AD and ADAM will not return the entire
attribute if it contains more than a certain number of values (1000 for Windows 2000
and 1500 for Windows 2003+ by default).&amp;nbsp; As such, if you truly want robust code,
you need to always use what is called range retrieval for link value paired attributes.
&lt;/p&gt;
&lt;p&gt;
Range retrieval is a process similar to paging in directory services, whereby you
ask the directory for a certain range of particular attribute.&amp;nbsp; You know that
you are using range retrieval when you see the attribute being requested in the following
format:
&lt;/p&gt;
&lt;p&gt;
"[attribute];range=[start]-[end]"
&lt;/p&gt;
&lt;p&gt;
As an example, in the case of the 'member' attribute, you might ask for the first
1500 values like so:
&lt;/p&gt;
&lt;p&gt;
"member;range=0-1499"
&lt;/p&gt;
&lt;p&gt;
Notice, it is zero based so you need to take this into account.&amp;nbsp; The general
algorithm as such is:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Ask for as big as you can get.&amp;nbsp; This means use the "*" for the ending range to
ask for it all. 
&lt;li&gt;
The directory will respond with either the actual max value (some integer), or with
a "*" indicating you got everything.&amp;nbsp; If you got everything, you are done. 
&lt;li&gt;
If not, using the max value now as your step, repeatedly ask for larger and larger
values inside a loop until the directory responds with a "*" as the end range.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
We covered how to use range retrieval in SDS in &lt;a href="http://www.amazon.com/exec/obidos/redirect?link_code=as2&amp;amp;path=ASIN/0321350170&amp;amp;tag=extemporaneou-20&amp;amp;camp=1789&amp;amp;creative=9325"&gt;our
book&lt;/a&gt;, and you can download sample code that shows how from the &lt;a href="http://directoryprogramming.net"&gt;book's
website&lt;/a&gt;.&amp;nbsp; What we didn't cover was how to do it in SDS.P.
&lt;/p&gt;
&lt;p&gt;
SDS.P is a layer closer the the metal than our ADSI based System.DirectoryServices
(SDS).&amp;nbsp; As such, if you are expected to do range retrieval for SDS, you can be
assured that you need to do it for SDS.P as well.&amp;nbsp; Adopting the code SDS, you
get something like this (but modified to some extent):
&lt;/p&gt;

&lt;div&gt;
&lt;p&gt;
&lt;span&gt;static&lt;/span&gt; &lt;span&gt;List&lt;/span&gt;&amp;lt;&lt;span&gt;string&lt;/span&gt;&amp;gt;
RangeRetrieve(
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; LdapConnection connect, &lt;span&gt;string&lt;/span&gt; dn, &lt;span&gt;string&lt;/span&gt; attribute)
&lt;/p&gt;
&lt;p&gt;
{
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;int&lt;/span&gt; idx = 0;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;int&lt;/span&gt; step = 0;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;List&lt;/span&gt;&amp;lt;&lt;span&gt;string&lt;/span&gt;&amp;gt;
list = &lt;span&gt;new&lt;/span&gt; &lt;span&gt;List&lt;/span&gt;&amp;lt;&lt;span&gt;string&lt;/span&gt;&amp;gt;();
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;string&lt;/span&gt; range = &lt;span&gt;String&lt;/span&gt;.Format(
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;span&gt;"{0};range={{0}}-{{1}}"&lt;/span&gt;,
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; attribute
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; );
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;string&lt;/span&gt; currentRange = &lt;span&gt;String&lt;/span&gt;.Format(range,
idx, &lt;span&gt;"*"&lt;/span&gt;);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; SearchRequest request = &lt;span&gt;new&lt;/span&gt; SearchRequest(
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; dn,
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;String&lt;/span&gt;.Format(&lt;span&gt;"({0}=*)"&lt;/span&gt;,
attribute),
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; SearchScope.Base,
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;new&lt;/span&gt; &lt;span&gt;string&lt;/span&gt;[]
{ currentRange }
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; );
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; SearchResultEntry entry = &lt;span&gt;null&lt;/span&gt;;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;bool&lt;/span&gt; lastSearch = &lt;span&gt;false&lt;/span&gt;;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;while&lt;/span&gt; (&lt;span&gt;true&lt;/span&gt;)
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; SearchResponse response = 
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; (SearchResponse)connect.SendRequest(request);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;if&lt;/span&gt; (response.Entries.Count
== 1) &lt;span&gt;//should only be one&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; entry = response.Entries[0];
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;//this
might be optimized to find full step or just use 1000 for&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;//compromise&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;foreach&lt;/span&gt; (&lt;span&gt;string&lt;/span&gt; attrib &lt;span&gt;in&lt;/span&gt; entry.Attributes.AttributeNames)
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; currentRange
= attrib;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; lastSearch
= currentRange.IndexOf(&lt;span&gt;"*"&lt;/span&gt;, 0) &amp;gt; 0;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; step =
entry.Attributes[currentRange].Count;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;foreach&lt;/span&gt; (&lt;span&gt;string&lt;/span&gt; member &lt;span&gt;in&lt;/span&gt; 
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; entry.Attributes[currentRange].GetValues(&lt;span&gt;typeof&lt;/span&gt;(&lt;span&gt;string&lt;/span&gt;)))
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; {
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; list.Add(member);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; idx++;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;if&lt;/span&gt; (lastSearch)
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;break&lt;/span&gt;;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; currentRange = &lt;span&gt;String&lt;/span&gt;.Format(range,
idx, (idx + step));
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; request.Attributes.Clear();
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; request.Attributes.Add(currentRange);
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;else&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;break&lt;/span&gt;;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span&gt;return&lt;/span&gt; list;
&lt;/p&gt;
&lt;p&gt;
}
&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;
Happy coding... of course, if you are clever you will realize you can avoid all this
range retrieval mess by using an attribute scope query (ASQ). :)
&lt;/p&gt;
&lt;p&gt;
*edit: tried to fix the style for code to render in Google Reader correctly
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=ebbcda67-a1ac-45fa-9aca-1b6de2f7a058" /&gt;</description>
      <link>http://dunnry.com/blog/RangeRetrievalUsingSystemDirectoryServicesProtocols.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,ebbcda67-a1ac-45fa-9aca-1b6de2f7a058.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,ebbcda67-a1ac-45fa-9aca-1b6de2f7a058.aspx</guid>
      <pubDate>Fri, 10 Aug 2007 15:53:21 GMT</pubDate>
    </item>
    <item>
      <title>Getting Active Directory Group Membership in .NET 3.5</title>
      <description>&lt;p&gt;
I have &lt;a href="http://dunnry.com/blog/CategoryView,category,Groups.aspx"&gt;previously
covered&lt;/a&gt; pretty extensively the options for getting a user's group membership in
Active Directory or ADAM (soon to be Active Directory LDS (Lightweight Directory Services))
here on the blog, in the &lt;a href="http://directoryprogramming.net/forums/9/ShowForum.aspx"&gt;forum&lt;/a&gt;,
and in &lt;a href="http://www.amazon.com/exec/obidos/redirect?link_code=as2&amp;amp;path=ASIN/0321350170&amp;amp;tag=extemporaneou-20&amp;amp;camp=1789&amp;amp;creative=9325"&gt;the
book&lt;/a&gt;.&amp;nbsp; However, there is a new option for users of .NET 3.5 that should be
of interest.
&lt;/p&gt;
&lt;p&gt;
The Directory Services group at Microsoft has released in beta form a new API for
dealing with a lot of the common things we need to do with users, groups, and computers
in Active Directory, ADAM, and the local machine.&amp;nbsp; This API is called System.DirectoryServices.AccountManagement
(or SDS.AM).&amp;nbsp; Here is a simple example of how to get a users groups (including
nested, and primary): &lt;pre class="code"&gt;&lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Main(&lt;span&gt;string&lt;/span&gt;[]
args) { &lt;span&gt;PrincipalContext&lt;/span&gt; ctx = &lt;span&gt;new&lt;/span&gt; &lt;span&gt;PrincipalContext&lt;/span&gt;(&lt;span&gt;ContextType&lt;/span&gt;.Domain); &lt;span&gt;using&lt;/span&gt; (ctx)
{ &lt;span&gt;Principal&lt;/span&gt; p = &lt;span&gt;Principal&lt;/span&gt;.FindByIdentity(ctx, &lt;span&gt;"ryandunn"&lt;/span&gt;); &lt;span&gt;using&lt;/span&gt; (p)
{ &lt;span&gt;var&lt;/span&gt; groups = p.GetGroups(); &lt;span&gt;using&lt;/span&gt; (groups)
{ &lt;span&gt;foreach&lt;/span&gt; (&lt;span&gt;Principal&lt;/span&gt; group &lt;span&gt;in&lt;/span&gt; groups)
{ &lt;span&gt;Console&lt;/span&gt;.WriteLine(group.SamAccountName
+ &lt;span&gt;"-"&lt;/span&gt; + group.DisplayName); } } } } &lt;span&gt;Console&lt;/span&gt;.ReadLine();
}&lt;/pre&gt;
&lt;p&gt;
That's not too bad - in fact, it looks worse than it is because I am trying to make
sure everything is wrapped in a 'using' statement where necessary.&amp;nbsp; The equivalent
code to do this would be many times more (using DsCrackNames or LDAP searches) and
would yield far less information being returned (just the DN in most cases). 
&lt;p&gt;
Over the next few weeks and months, I intend to dig more deeply into this namespace
and put some samples up here for everyone.&amp;nbsp; This is just a taste for now, but
it should show you how powerful this namespace really is.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;*Updated to fix CSS renderings in Google Reader
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=54fbb420-9958-48e8-9d0f-e6f0f76b512d" /&gt;</description>
      <link>http://dunnry.com/blog/GettingActiveDirectoryGroupMembershipInNET35.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,54fbb420-9958-48e8-9d0f-e6f0f76b512d.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,54fbb420-9958-48e8-9d0f-e6f0f76b512d.aspx</guid>
      <pubDate>Wed, 01 Aug 2007 15:33:16 GMT</pubDate>
    </item>
    <item>
      <title>Discovering IIS7 Schema</title>
      <description>&lt;p&gt;
One of the major changes introduced with IIS7 is the removal of the metabase.&amp;nbsp;
This probably comes as a great relief to many of the admins and developers that struggled
with coordinating configuration across multiple machines.&amp;nbsp; Instead of the metabase,
IIS7 has chosen to schematize all the web server settings and host them centrally
in a new XML file called applicationHost.config located at '%windir%\system32\inetsrv\config'.
&lt;/p&gt;
&lt;p&gt;
The schema itself can be found in the 'schema' folder right below the 'config' directory.&amp;nbsp;
This new centralized configuration file holds all the global default values for IIS7
as well as defines what sites, applications, virtual directories, app pools, etc.
are on the server.
&lt;/p&gt;
&lt;p&gt;
Let's take a look just a couple items I found in my&amp;nbsp;applicationHost.config:
&lt;/p&gt;
&lt;pre class="code"&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;applicationPools&lt;/span&gt;&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;DefaultAppPool&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;Classic
.NET AppPool&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;managedPipelineMode&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;Classic&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;applicationPoolDefaults&lt;/span&gt;&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;processModel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;identityType&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;NetworkService&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span&gt;applicationPoolDefaults&lt;/span&gt;&lt;span&gt;&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span&gt;applicationPools&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt; 
&lt;p&gt;
Here is the configuration of the application pools on my IIS7 server.&amp;nbsp; Let's
see what happens if we just edit this directly and add a new application pool.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/content/binary/WindowsLiveWriter/DiscoveringIIS7Schema_D463/AppPool.png"&gt;&lt;img height="196" alt="AppPool" src="http://dunnry.com/blog/content/binary/WindowsLiveWriter/DiscoveringIIS7Schema_D463/AppPool_thumb.png" width="525" border="0"&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
If we open our new IIS Manager (Start &amp;gt; Run &amp;gt; inetmgr), we can see that this
is all there is to it.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/content/binary/WindowsLiveWriter/DiscoveringIIS7Schema_D463/AppPoolsInManager.png"&gt;&lt;img height="255" alt="AppPoolsInManager" src="http://dunnry.com/blog/content/binary/WindowsLiveWriter/DiscoveringIIS7Schema_D463/AppPoolsInManager_thumb.png" width="640" border="0"&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
Further poking around will yield lists of sites, applications, and virtual directories
as well - all described neatly in XML format.&amp;nbsp; Now, how did the IIS team put
all this together?&amp;nbsp; Were they using their own black box implementation?&amp;nbsp;
If we inspect the schema, we will find there is no magic here and the IIS team is
leveraging its own schema system for IIS itself.&amp;nbsp; Opening the "IIS_Schema.xml"
file found in the 'schema' directory shows us the exact settings available for 'applicationPools'.
&lt;/p&gt;
&lt;pre class="code"&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;sectionSchema&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;system.applicationHost/applicationPools&lt;/span&gt;"&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;collection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;addElement&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;add&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultElement&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;applicationPoolDefaults&lt;/span&gt;"&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;name&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;string&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;true&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;isUniqueKey&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;true&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;validationType&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;applicationPoolName&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;queueLength&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;uint&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;1000&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;validationType&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;integerRange&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;validationParameter&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;10,65535&lt;/span&gt;"&lt;span&gt;/&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;autoStart&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;bool&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;true&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;enable32BitAppOnWin64&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;bool&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;false&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;managedRuntimeVersion&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;string&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;v2.0&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;managedPipelineMode&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;enum&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;Integrated&lt;/span&gt;"&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;Integrated&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;0&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;Classic&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;1&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;passAnonymousToken&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;bool&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;true&lt;/span&gt;"&lt;span&gt; /&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;processModel&lt;/span&gt;"&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;identityType&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;enum&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultValue&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;NetworkService&lt;/span&gt;"&lt;span&gt;&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;LocalSystem&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;0&lt;/span&gt;"&lt;span&gt;/&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;LocalService&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;1&lt;/span&gt;"&lt;span&gt;/&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;NetworkService&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;2&lt;/span&gt;"&lt;span&gt;/&amp;gt;
&amp;lt;&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;SpecificUser&lt;/span&gt;"&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;"&lt;span&gt;3&lt;/span&gt;"&lt;span&gt;/&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span&gt;attribute&lt;/span&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt; &amp;lt;...SNIP...&lt;/span&gt;&lt;span&gt;&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span&gt;sectionSchema&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;
As we can see here, there are some basic typed attributes that describe our application
pool.&amp;nbsp; They have things like types, default values, and even enumerations with
possible values (managedPipelineMode is one such).&amp;nbsp; There really is no magic
here.&amp;nbsp; Someone could very easily write a program that inspected the schema of
IIS7 fully and presented all possible options just by inspecting this schema.
&lt;/p&gt;
&lt;p&gt;
What should immediately come to mind as a developer is, "&lt;strong&gt;how can I leverage
this system&lt;/strong&gt;"?&amp;nbsp; If there is one word that describes IIS7, it is "&lt;strong&gt;Extensible&lt;/strong&gt;".&amp;nbsp;
In my next post, I will show you how to leverage this powerful schema system that
IIS7 introduces for your own application purposes &lt;strong&gt;without code&lt;/strong&gt;.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=7d80160c-6f7f-4865-b0b9-6d181d6a8fdc" /&gt;</description>
      <link>http://dunnry.com/blog/DiscoveringIIS7Schema.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,7d80160c-6f7f-4865-b0b9-6d181d6a8fdc.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,7d80160c-6f7f-4865-b0b9-6d181d6a8fdc.aspx</guid>
      <pubDate>Fri, 27 Jul 2007 16:08:23 GMT</pubDate>
    </item>
    <item>
      <title>Directory Services Samples Milestone</title>
      <description>&lt;p&gt;
I was checking on the &lt;a href="http://directoryprogramming.net"&gt;book's website&lt;/a&gt; the
other day and noticed that we broke the 54,000 mark for downloads of our sample code.&amp;nbsp;
That really surprised me.&amp;nbsp; I just didn't think that there were that many people
in the world working on these types of scenarios.
&lt;/p&gt;
&lt;p&gt;
&amp;nbsp;Now, I happen to know that we did not sell that many copies of &lt;a href="http://www.amazon.com/exec/obidos/redirect?link_code=as2&amp;amp;path=ASIN/0321350170&amp;amp;tag=extemporaneou-20&amp;amp;camp=1789&amp;amp;creative=9325"&gt;the
book&lt;/a&gt;, so this means a lot of people are downloading just the samples.&amp;nbsp; For
the record, that is perfectly fine and we encourage people to look at the samples.&amp;nbsp;
Admittedly, some of the samples might be head-scratchers without the book for context,
but hey, its free and so is our time on &lt;a href="http://directoryprogramming.net/forums/default.aspx"&gt;the
forums&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Joe and I log a lot of time here and in other forums helping people and we love to
hear your feedback on what works and what doesn't.&amp;nbsp; Give us a shout when samples
don't make sense, or when a scenario seems to be overly painful.&amp;nbsp; Some people
have asked how they can repay us for our time (I have had offers for beer, wine, and
lodging so far).&amp;nbsp; A simple thanks is enough for both of us, however, and a perhaps
a recommendation to others.&amp;nbsp; If you really feel the need to contribute beyond
nice words - simple, just &lt;a href="http://www.amazon.com/exec/obidos/redirect?link_code=as2&amp;amp;path=ASIN/0321350170&amp;amp;tag=extemporaneou-20&amp;amp;camp=1789&amp;amp;creative=9325"&gt;buy
the book&lt;/a&gt;!
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=d10681bd-a808-4e3d-b761-fe42bd25e6d8" /&gt;</description>
      <link>http://dunnry.com/blog/DirectoryServicesSamplesMilestone.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,d10681bd-a808-4e3d-b761-fe42bd25e6d8.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,d10681bd-a808-4e3d-b761-fe42bd25e6d8.aspx</guid>
      <pubDate>Thu, 28 Jun 2007 17:00:18 GMT</pubDate>
    </item>
    <item>
      <title>Working with Large Amounts of Data in Directory Services</title>
      <description>&lt;p&gt;
I almost missed &lt;a href="http://blogs.dirteam.com/blogs/tomek/archive/2007/05/18/s-ds-and-large-data-sets.aspx"&gt;this
one from Tomek&lt;/a&gt;, but he has a good analysis of what happens when you have many
results to return from the directory and a nice comparison of how the different stacks
(System.DirectoryServices vs. System.DirectoryServices.Protocols) handle it.&amp;nbsp;
The moral of the story:&amp;nbsp; if you have a ton of results coming back, it might be
in your interest to pursue using the Protocols stack.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=87d9e628-5d7d-4aec-9b0a-9c90bfae3495" /&gt;</description>
      <link>http://dunnry.com/blog/WorkingWithLargeAmountsOfDataInDirectoryServices.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,87d9e628-5d7d-4aec-9b0a-9c90bfae3495.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,87d9e628-5d7d-4aec-9b0a-9c90bfae3495.aspx</guid>
      <pubDate>Fri, 01 Jun 2007 12:39:58 GMT</pubDate>
    </item>
    <item>
      <title>Interested in ORcas or IIS7? We're Hiring</title>
      <description>&lt;p&gt;
If you are the type of person that loves learning the latest and greatest technologies
and sharing it with others, then we have two job openings right up your alley.&amp;nbsp;
My manager has &lt;a href="http://blogs.msdn.com/jamescon/archive/2007/05/31/now-hiring-technical-evangelists-for-orcas-and-iis7.aspx"&gt;more
details here&lt;/a&gt;.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://dunnry.com/blog/aggbug.ashx?id=f496c9e1-6ecf-4061-b801-a89e2a27b285" /&gt;</description>
      <link>http://dunnry.com/blog/InterestedInORcasOrIIS7WereHiring.aspx</link>
      <source url="http://dunnry.com/blog/">Extemporaneous Mumblings - .NET</source>
      <comments>http://dunnry.com/blog/CommentView,guid,f496c9e1-6ecf-4061-b801-a89e2a27b285.aspx</comments>
      <guid isPermaLink="False">http://dunnry.com/blog/PermaLink,guid,f496c9e1-6ecf-4061-b801-a89e2a27b285.aspx</guid>
      <pubDate>Fri, 01 Jun 2007 12:33:27 GMT</pubDate>
    </item>
    <item>
      <title>DEC 2007 Follow Up</title>
      <description>&lt;p&gt;
I was lucky enough to attend DEC again this year and was even more lucky to have been
asked to speak due to an unfortunate last minute cancellation.&amp;nbsp; This year, I
presented on a variation of the same type of stuff that Ryan and I &lt;a href="http://dunnry.com/blog/DEC2006WrapUpAndPresentationMaterial.aspx"&gt;presented
on at DEC 2006&lt;/a&gt;.&amp;nbsp; This year, I had to fly solo as Ryan could not attend.&amp;nbsp;
:(
&lt;/p&gt;
&lt;p&gt;
Here's what we did differently this time around:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
No PowerShell (DEC already had 2 PowerShell sessions, so why bother?) 
&lt;li&gt;
Focus on some new Longhorn LDAP and AD features (Fine Grained Password Policy) 
&lt;li&gt;
A "slideware" overview of what's coming in .NET 3.5 "Orcas" with the new System.DirectoryServices.AccountManagement
namespace (formerly known as the Principal API).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
I'd like to thank all of those who attended.&amp;nbsp; I hope you enjoyed the talk and
hope that some of you got free books.&amp;nbsp; I apologize if I could not accomodate
all of you.&amp;nbsp; :(&amp;nbsp; Thanks to the &lt;a href="http://www.awprofessional.com/index.asp?rl=1"&gt;Addison-Wesley&lt;/a&gt; marketing
team for providing the books for your enjoyment.
&lt;/p&gt;
&lt;p&gt;
For those of you interested in the Snippet Compiler tool I used in my demos, you can
find it &lt;a href="http://www.sliver.com/dotnet/SnippetCompiler/"&gt;here&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The slides and code for the demos are attached and I did get around to converting
them to VB for all of you VB people (I'm a VB.NET guy too; I really don't know why
I coded all the demos in C# :)).&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Note that my application of the "in chain" matching rule turns out to be incorrect
usage.&amp;nbsp; Don't do it like that!&amp;nbsp; Read more &lt;a href="http://www.joekaplan.net/InChainMatchingRuleShouldNotBeUsedForTransitiveLinkExpansion.aspx"&gt;here&lt;/a&gt;.&amp;nbsp;
I feel silly.
&lt;/p&gt;
&lt;p&gt;
Note that if you are confused about which API to use, S.DS or S.DS.P, I discussed
that in some detail &lt;a href="http://www.joekaplan.net/ATaleOfTwoLDAPStacks.aspx"&gt;here&lt;/a&gt;.&amp;nbsp;
There is really no right answer, but hopefully that helps.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
To ask us any specific questions about LDAP programming, please use the book's &lt;a href="http://www.directoryprogramming.net"&gt;discussion
forum&lt;/a&gt;.&amp;nbsp; This is the only place that Ryan and I both use together.
&lt;/p&gt;
&lt;p&gt;
As always, DEC is a treat and I really enjoyed all the conversations and interaction
and am happy to see ADFS gaining a little traction.&amp;nbsp; Now, about that hot chicken...
&lt;/p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/DEC2007.zip"&gt;DEC2007.zip (536.52
KB)&lt;/a&gt;&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=5c846938-9aba-467a-b1b0-99bbef193bcb" /&gt;</description>
      <link>http://www.joekaplan.net/DEC2007FollowUp.aspx</link>
      <source url="http://www.joekaplan.net/">Joe Kaplan</source>
      <comments>http://www.joekaplan.net/CommentView,guid,5c846938-9aba-467a-b1b0-99bbef193bcb.aspx</comments>
      <guid isPermaLink="False">http://www.joekaplan.net/PermaLink,guid,5c846938-9aba-467a-b1b0-99bbef193bcb.aspx</guid>
      <pubDate>Fri, 27 Apr 2007 11:58:34 GMT</pubDate>
    </item>
    <item>
      <title>In Chain Matching Rule should not be Used for Transitive Link Expansion</title>
      <description>&lt;p&gt;
Microsoft has added a new LDAP feature to AD in Windows 2003 SP2 and Longhorn server
called the LDAP_MATCHING_RULE_IN_CHAIN.&amp;nbsp; Essentially, it is an extension filter
type that allows you to search withing the content of a distinguished name-syntax
attribute and do matching throughout the entire chain of linked values instead of
just within the immediate values.&amp;nbsp; The docs are &lt;a href="http://msdn2.microsoft.com/en-us/library/aa746475.aspx"&gt;here&lt;/a&gt; and
the syntax looks like this:
&lt;/p&gt;
&lt;p&gt;
&lt;font face="Courier New"&gt;(memberOf:1.2.840.113556.1.4.1941:=CN=some group,CN=xxxx,DC=xxxx,DC=xxxx)&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
I showed some examples of this in my talk at DEC where I used some searches with and
without the extension filter type to show traversal of some nested group membership.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://dunnry.com/blog/"&gt;Ryan&lt;/a&gt; also &lt;a href="http://dunnry.com/blog/TransitiveLinkValueFilterEvaluation.aspx"&gt;wrote
about this&lt;/a&gt; a while ago and discovered that while useful, this technique is very
slow for expanding group membership and it seems to be much faster to just use recursive
searches.
&lt;/p&gt;
&lt;p&gt;
As it turns out, the problem is that we were trying to use this feature incorrectly.&amp;nbsp;
The "In Chain" filter type should NOT be used for transitive link expansion!&amp;nbsp;
It is intended to&amp;nbsp;be used for matching only.&amp;nbsp; Perhaps that's why they called
it LDAP_MATCHING_RULE_IN_CHAIN.&amp;nbsp; :)
&lt;/p&gt;
&lt;p&gt;
As such, you should really only use it in a base level query.&amp;nbsp; If you use the
filter shown above in a base level query, it will still tell you if the object that
is used as the search root is a member of the specified group anywhere in the nesting
chain, but it will perform fine.&amp;nbsp; If you use it for anything else, you are asking
for trouble.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Now, when will we get some sort of transitive link expansion widget that actually
works well for this purpose?
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=622e9be0-6539-4ddc-8096-820c33893ffc" /&gt;</description>
      <link>http://www.joekaplan.net/InChainMatchingRuleShouldNotBeUsedForTransitiveLinkExpansion.aspx</link>
      <source url="http://www.joekaplan.net/">Joe Kaplan</source>
      <comments>http://www.joekaplan.net/CommentView,guid,622e9be0-6539-4ddc-8096-820c33893ffc.aspx</comments>
      <guid isPermaLink="False">http://www.joekaplan.net/PermaLink,guid,622e9be0-6539-4ddc-8096-820c33893ffc.aspx</guid>
      <pubDate>Fri, 27 Apr 2007 10:24:21 GMT</pubDat