Thursday, January 7, 2010

NullReferenceException in EnumerationResultEnumerator.Dispose()

Still working with the unsupported web service client for RC1, and I ran into the following error:

System.NullReferenceException

Object reference not set to an instance of an object.

at Microsoft.ResourceManagement.Client.EnumerationResultEnumerator.Dispose() in C:\FIM2010Dev\Microsoft.ResourceManagement.Samples\Microsoft.ResourceManagement.Client\EnumerationResultEnumerator.cs:line 46


The problem was pretty easy to find, but I thought I'd at least change the code to throw a more helpful exception. The problem happened because I naively tried to use the LINQ methods Count() and First() consecutively:

IEnumerable<RmResource> objects =
client.Enumerate(xpath, selection.ToArray());
if (objects != null && objects.Count() > 0)
{
string result = objects.First()[ATTRIBUTE_DISPLAY_NAME].Value.ToString();
if (!string.IsNullOrEmpty(result))
{
displayName = result;
break;
}
}

However, much like LINQ to SQL behavior, the queries are run on-the-fly as the results are enumerated, and then they're disposed. So, you guessed it, we can't enumerate the results more than once (or at least we should avoid it). You've probably seen this exception from the LINQ to SQL libraries, "The query results cannot be enumerated more than once."

Here's my modified code for the EnumerationResultEnumerator class. I'm throwing a more helpful exception with the message above. I've highlighted my changes:

using System;
using System.Collections.Generic;
using System.Xml.Schema;
using System.Text;

using Microsoft.ResourceManagement.Client.WsEnumeration;
using Microsoft.ResourceManagement.ObjectModel;

namespace Microsoft.ResourceManagement.Client
{
class EnumerationResultEnumerator : IEnumerator<RmResource>, IEnumerable<RmResource>
{
WsEnumerationClient client;
List<RmResource> results;
int resultIndex;
bool endOfSequence;
EnumerationContext context;
String filter;
String[] attributes;
RmResource current;
RmResourceFactory resourceFactory;

bool disposed = false;

internal EnumerationResultEnumerator(WsEnumerationClient client, RmResourceFactory factory, String filter, String[] attributes)
{
results = new List<RmResource>();
this.client = client;
this.filter = filter;
this.resourceFactory = factory;
this.attributes = attributes;
}

#region IEnumerator<RmResource> Members

public RmResource Current
{
get { return current; }
}

#endregion

#region IDisposable Members


public void Dispose()
{
if (!disposed)
{
this.context = null;
this.results.Clear();
this.results = null;
this.disposed = true;
}
}

#endregion

#region IEnumerator Members

object System.Collections.IEnumerator.Current
{
get { return current; }
}

public bool MoveNext()
{

if (disposed)
{
throw new InvalidOperationException("The query results cannot be enumerated more than once.");
}

lock (this.client)
{
if (resultIndex < results.Count)
{
this.current = results[resultIndex++];
return true;
}
else
{
PullResponse response;
if (this.context == null)
{
if (resultIndex > 0)
{
// case: previous pull returned an invalid context
return false;
}
EnumerationRequest request = new EnumerationRequest(filter);
if (attributes != null)
{
request.Selection = new List<string>();
request.Selection.AddRange(this.attributes);
}
response = client.Enumerate(request);
this.endOfSequence = response.EndOfSequence != null;
}
else
{
if (this.endOfSequence == true)
{
// case: previous pull returned an end of sequence flag
this.current = null;
return false;
}
PullRequest request = new PullRequest();
request.EnumerationContext = this.context;
response = client.Pull(request);
}

if (response == null)
return false;
resultIndex = 0;
this.results = resourceFactory.CreateResource(response);
this.context = response.EnumerationContext;
this.endOfSequence = response.IsEndOfSequence;
if (this.results.Count > 0)
{
this.current = results[resultIndex++];
return true;
}
else
{
this.current = null;
return false;
}
}
}
}


public void Reset()
{
if (!disposed)
{
this.results.Clear();
this.context = null;
}
}

#endregion

#region IEnumerable<RmResource> Members

public IEnumerator<RmResource> GetEnumerator()
{
return this;
}

#endregion

#region IEnumerable Members

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this;
}

#endregion
}
}

Paolo Tedesco's changes for object count in enumeration responses

As one final note, Paolo has posted code for including the object count in enumeration responses. I haven't tried it yet, but it looks like something I could have used here. Here's the link (note that I'm also posting links to my changes on this thread):

http://social.technet.microsoft.com/Forums/en-US/ilm2/thread/ffc16720-0dfb-4131-b676-9225f15b4f72?prof=required

5 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete