Navigation

Search

Categories

On this page

SQLCE Error 26100: The table name is not valid
FatalExecutionEngineError returning from WPF PageFunction
ClickOnce Error after upgrading to VS 2008
Service Locator in Code?
Blog Action Day
ObjectListView Update (1.0.0.11): Find, Select, & Property Paths
Installing AnkhSVN on Vista
Serialization Problems and Solutions
ObjectListView Update (1.0.0.9): Debugger Visualizer

Archive

Blogroll

Disclaimer
Postings are provided "As Is" with no warranties and confer no rights.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 38
This Year: 1
This Month: 0
This Week: 0
Comments: 21

Sign In
Pick a theme:

 Tuesday, April 15, 2008
Tuesday, April 15, 2008 10:05:26 PM (Pacific Standard Time, UTC-08:00) ( )

If you're using LINQ to SQL with SQLCE and use the DataContext.CreateDatabase() method, you may encounter this error.  If you used the LINQ to SQL class designer to generate the classes from a different database provider (such as SQL Server 2005 or SQL Server Express), the table source names may be prefixed with "dbo."

Just remove the prefix, and CreateDatabase() will succeed.

 Thursday, November 29, 2007
Thursday, November 29, 2007 11:17:30 PM (Pacific Standard Time, UTC-08:00) ( )

I'm posting this for future reference.  If you invoke a PageFunction and add an event handler to the PageFunction's Return event, the event handler must be an instance method of the calling page.  No other method will do.  If the event handler doesn't meet this criteria, you'll see the FatalExecutionEngineError thrown when you return from the PageFunction.  Sorry, no separation of concerns allowed.

More details in this forum thread: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1975763&SiteID=1.

Thursday, November 29, 2007 8:04:16 PM (Pacific Standard Time, UTC-08:00) ( )

We recently upgraded to VS 2008 RTM at work, and shortly after discovered that a new build of our ClickOnce-deployed WinForms application failed to install correctly!  During the download process, a message box would display, and the log details revealed an InvalidDeploymentException (RefDefValidation) in System.Deployment.Application.DownloadManager.ProcessDownloadedFile().

Let me just say that days of fruitless investigation followed.

I'm happy to report that I finally did find the solution (completely by accident).  A new option has been added to Visual Studio, allowing you to automatically create and embed a manifest in your application.  This manifest allows you to provide proper application behavior when running under UAC in Vista.  Here's the option, on the Application property page of the Visual Studio project:Manifest Options

When you upgrade a WinForms project from VS2005 to VS2008, this option is automatically set for you.  If you had identified the application as a ClickOnce application in VS 2005, the resulting deployment from a VS2008 build works correctly.

Real-world ClickOnce

If you're like me, you're developing in a professional environment where you can't just click the Publish Now button and send an app from your development box into production.  Is it just me, or does everyone think that's insane?  Do you think we might want to QA those phasers first, Captain?

We have a relatively complex build that builds our ClickOnce manifests based on the target environment (Dev, QA, or Production) so that we can test ClickOnce updates from previous versions before going to production.  Also, since we've had our ClickOnce app deployed for more than one year, we had to overcome the certificate renewal problem, which requires a special signing process using both the expired and the current signing certificate.  Needless to say, neither of these can be set up in Visual Studio.  What we ended up with is a build where the ClickOnce options are not set in the Visual Studio project, but externally in an MSBuild project file, using many of the built-in targets that VS uses.

Migrating from VS2005

When the project was converted to VS2008, it was upgraded by the wizard as if it wasn't a ClickOnce project.  This means that the manifest setting was set incorrectly.

Here's what the Visual Studio documentation has to say about the manifest setting:

Manifest
Selects a manifest generation option when the application runs on Windows Vista under User Account Control (UAC). This option can have the following values:
  • Embed manifest with default settings. Supports the typical manner in which Visual Studio operates on Windows Vista, which is to embed security information in the application's executable file, specifying that requestedExecutionLevel be AsInvoker. This is the default option.
  • Create application without a manifest. This method is known as virtualization. Use this option for compatibility with earlier applications.
  • Properties\app.manifest. This option is required for applications deployed by ClickOnce or Registration-Free COM. If you publish an application by using ClickOnce deployment, Manifest is automatically set to this option.

The only options I see in the combo box are the first and second.  In our application, "Embed manifest with default settings" was selected by the conversion.  As soon as I chose "Create application without a manifest", I was able to build, deploy, and download with no trouble at all.

I still need to investigate the manifest issue further.  I want my users to have a great UAC experience with our app, and that means getting the manifest right.  For now though, "create without a manifest" is your quick fix.

Click On!

 Wednesday, November 28, 2007
Wednesday, November 28, 2007 1:04:57 AM (Pacific Standard Time, UTC-08:00) ( )

Phil Haack posed a question today about dependency injection frameworks:

Normally, I would just specify a type to instantiate as another PluginFamily entry. But what I really want to happen in this case is for StructureMap to call a method or delegate and use the value returned as the constructor argument.

...

Does anyone know if something like this is possible with any of the Dependency Injection frameworks out there? Whether via code or configuration?

I don't have much experience with the big three DI frameworks Castle Windsor, Spring.NET and StructureMap, but what I've seen suggests that these are strongly configuration based.

That means that I can specify a concrete type name in a configuration file that the framework will use to instantiate a particular interface type.  For example, in Spring.NET, you might specify the concrete type like this:

<objects xmlns="http://www.springframework.net">
    <object
        name="TheClassIWant"
        type="MyNamespace.MyConcreteClass, MyAssembly"
    </object>
</objects>

Then in your code, you can ask Spring to create an instance of MyConcreteClass like so:

IApplicationContext ctx = ContextRegistry.GetContext();
MyConcreteClass instance = (MyConcreteClass)ctx.GetObject("TheClassIWant");

That's all well and good.  In Phil's case, though, he wants to specify a way to create an instance of MyConcreteClass in code rather than configuration.  Let's take that requirement more generally and assert that we know what concrete classes we want to instantiate in our application, but just don't want the whole application to know.  Maybe I need an instance of ISomeInterface in some part of my code, but that code doesn't know how to get an ISomeInterface.  What I want is some global mechanism to define how to get an instance of some type, and then a way to get the type.  Now, how do we do that in code?

Simple Service Locator

I had some spare time while at DevConnections, and decided to create just that - a code-based service locator.  The basic idea is that we'll have a ServiceLocator class that maps interface types to concrete types.

At some point in your code, you register a concrete type for an interface:

ServiceLocator locator = new ServiceLocator();
locator.Register<ITest>(typeof(Test));

This means that when we ask for instance of the interface ITest, ServiceLocator will create and return an instance of Test (the implication of course is that Test implements ITest).  Here's how we ask for an ITest:

ITest instance = locator.Get<ITest>();

If you expose a singleton instance of ServiceLocator in your application, you can register your types at startup, and then use the locator to instantiate the concrete types from anywhere in the code.

What about parameterized constructors?

Default constructors are the trivial case.  How do we handle creation of concrete types that require constructor parameters?  We have two cases:

  1. The parameter is an interface that we want ServiceLocator to instantiate
  2. The parameter is a value that we want to provide explicitly.

Here's how we handle the first case:

locator.Register<ICat>(typeof(Cat), typeof(IOwner));
locator.Register<IOwner>(typeof(Owner));
 
ICat instance = locator.Get<ICat>();

Here we're saying that if we want an ICat, we need to create a Cat with the constructor Cat(IOwner).  By registering IOwner to create an Owner, ServiceLocator is able to create a Cat by first instantiating an Owner and passing that value to the Cat constructor.

In the second case, we have some value that ServiceLocator doesn't know how to create.  Let's suppose the Cat constructor takes an IOwner and a string:

locator.Register<IOwner>(typeof(Owner));
locator.Register<ICat>(typeof(Cat), new TypeParameter(typeof(IOwner)), new TypeParameter(typeof(string), "Kitty"));
ICat cat = locator.Get<ICat>();

In this case, ServiceLocator will call the constructor Cat(IOwner, string) with an IOwner it creates, and the string "Kitty".

And Singletons?

Sometimes we always want the same instance of an object:

Cat kitty = new Cat();
locator.Register<ICat>(kitty);
ICat cat = locator.Get<ICat>();

Here we always return the instance kitty when an ICat is requested.  We all know how hard it is to work with a large number of cats.

Le Delegate

Yes, there is a Santa Claus.  I might have some code that is responsible for creating an object.  Perhaps it's responsible for managing a pool of objects, or just has some complexity that would be difficult to express in configuration.  I want to register a delegate that will provide the instance.

locator.Register<ICat>(delegate() { return kitty.IsSleeping ? buffy : kitty; });
ICat cat = locator.Get<ICat>();

Here I provided an anonymous method in the Register() call, but I could just as easily register some other class method.

So there you have it - a configuration-free service locator with quite a bit of flexibility in object creation.

You can download the source for my ServiceLocator here.  The project was built in Visual Studio 2008.

Should I Use it?

If it works for you!  It's free of course.  However, I conjured this up in an hour or two, so it's not likely that ServiceLocator offers anywhere near the capabilities of a full-fledged dependency injection framework.  I'd encourage you to look closely at Castle Windsor, Spring.NET and StructureMap.  I'm sure that there are other great frameworks too.

I'd be grateful for any feedback as always.

 Monday, October 15, 2007
Monday, October 15, 2007 10:09:49 PM (Pacific Standard Time, UTC-08:00) ( )

Today's entry is dedicated to environmental appreciation.

In the early ages of man, the earth was wild, cold, and full of danger.  We have waged war on the unruly forces of nature for thousands of years.  Compared to our savage upbringings, we live now in an unimaginable paradise of warmth, comfort and plenty.  A few isolated families scattered over a wide dark land now find themselves amongst a crowd of constant companionship and entertainments.  So far we have come.

We have laid low mountains, dammed rivers, harnessed the atom.  Nothing can withstand us.  Yet with our great advances, we've lost many of the empty spaces in our world.  Most is paved, groomed and domesticated.  So much so that few of us could live without civilization.  We could not feed or shelter ourselves, or find our way through a wild land.  Our very souls are tamed by the comfort and convenience of modern life.

That is why I value what wilderness and open spaces we have left.  Walking amongst the mountains and woods of nature is a breath-taking experience of the natural beauty and majesty of creation, so absent from our everyday modern reality.

2468It's an opportunity to recapture the simple joy of challenging ourselves against primal forces.  Hiking long distances, climbing, finding food, making fire and shelter.  Having to rely on your own problem-solving abilities and carry your own water.  Can we claim to be human without doing these things?

Every year I take my family on a pilgrimage to the woods.  Every time there are aches and pains, cold mornings, and often rain.  And yet it's always the best week of the year.

In the struggle against nature, we have defined ourselves.  Now that we are victorious, we have the luxury to choose how we relate to nature.  I'm grateful that we have chosen to preserve at least some of it, and hopeful that we will be good stewards to what is left of this crucible of humanity.  We have enough pavement.

 Sunday, May 06, 2007
Sunday, May 06, 2007 9:03:09 PM (Pacific Standard Time, UTC-08:00) ( )

Note: You can download the complete implementation here.

I just posted a new version of ObjectListView that fixes a couple of bugs and adds some minor usability enhancements.

I've been using ObjectListView more in different projects, and I'm feeling the pain points.  Foremost, a complete IBindingListView implementation means that interaction with Windows Forms controls works smoothly, but that doesn't mean it's easy for the developer to use.

PropertyDescriptors Everywhere

I don't like working with PropertyDescriptors as method parameters.  In order to find a list item via IBindingList,.Find(), you need to get the descriptors for the list item type from TypeDescriptor, and then look up the correct one by the property name.  Awkward.  Moreover, all that Find() gives you is the first item found for which one property equals a specified value.  I've added two more useful Find() overloads.  One takes a string expression that can be an arbitrarily complex set of property comparisons (using the same syntax as the Filter property).  The other allows you to specify a Predicate delegate to provide a comparison in code.  Given these, the original Find() required for IBindingList seems unlikely to be used, so I changed it to an explicit interface implementation.  This has the effect of hiding the overload from the publicly exposed class methods, but still making it available to consumers of IBindingList.

Select

One of the useful methods of DataTable is Select(), which returns the set of DataRows in the table that match a given criteria.  Now clearly, you could iterate over the items in the view yourself and evaluate each, but having this built in to ObjectListView is very convenient.  As with Find(), I added two versions, one that takes a string expression, and the other that takes a delegate.  The return value of Select is a list of items that match the criteria.

Property Paths

Something I've had in mind for a while is being able to filter on list items based on the values of properties of properties.  You could use FilterPredicate to specify such a filter with a delegate, but it would be even more convenient to use an expression like "Customer.AccountRep.Department = 2".  The new version 1.0.0.11 supports this kind of property path in the Filter property, Find() and Select().  Currently, changing the value of one of the sub-properties specified in a Filter property path will not cause the view to be updated.  I need to do more work before I have an efficient implementation of the notification logic.

Item Deletion Events

ObjectListView has always provided the ListChanged event, which specifies an action type (ListChangedType).  One of the actions reported is Deleted, indicating that a list item has been removed.  Unfortunately, only the index of the item deleted is available at the time the event is raised; the item has already been removed.  I've added the RemovingItem event, which is raised just before an item is removed from the list.  This event is only raised when an item is removed through a method of ObjectListView (for example, view.Remove() or view.RemoveAt()).  If an item is removed through a method of the underlying list, the deletion is reported to ObjectListView after the fact, so RemovingItem cannot be raised.

Bugs

As always, a few bugs are fixed, and some minor clean-up done.  See the change log for details.

What's Cooking?

As I mentioned, there's work yet to be done in keeping the view up to date as sub-properties specified in the Filter expression change.  I'm also working on dynamic properties that can be added to the view.  These would be analogous to the expression columns supported by DataTable.

I'm also excited to be presenting ObjectListView at the Portland Code Camp in two weeks!  I think it will be hard to explain it all in an hour.  I'll just have to talk really, really fast.

 Wednesday, April 25, 2007
Wednesday, April 25, 2007 10:03:04 PM (Pacific Standard Time, UTC-08:00) ( )

I just got a new notebook PC with Vista pre-installed.  After removing all the OEM applications and finally re-paving to remove a wasted (er, "recovery") partition, I'm installing all my development tools.

One tool I've really come to appreciate is the AnkhSVN plugin for Visual Studio 2005.  Ankh integrates the Subversion (SVN) source code control system with VS.  I've used Subversion at work for the last two years, and I have to say I'm a fan.  Even so, my prior history was with Visual SourceSafe, and I've kept that on as my source control system at home.  No more - I've decided to take the plunge and move to SVN everywhere.

Unfortunately, after installing AnkhSVN and restarting Visual Studio, I received a not-too-helpful error message from Visual Studio: "The Add-in AnkhSVN failed to load or caused an exception, etc etc...Class not registered  Error number: 80040154".

Well.

Googling turned up a thread between Jon Skeet and Arild Fines discussing the problem.  Jon was able to successfully install Ankh after uninstalling it, adding the following registry keys, and then reinstalling AnkhSVN:

[HKEY_CLASSES_ROOT\AnkhUserControlHost.Ankh] 
[HKEY_CLASSES_ROOT\AnkhUserControlHost.Ankh\CLSID] 
[HKEY_CLASSES_ROOT\AnkhUserControlHost.AnkhUserControlHostCtl] 
[HKEY_CLASSES_ROOT\AnkhUserControlHost.AnkhUserControlHostCtl\CLSID] 
[HKEY_CLASSES_ROOT\AnkhUserControlHost.AnkhUserControlHostCtl.1\CLSID] 
[HKEY_CLASSES_ROOT\AnkhUserControlHost.AnkhUserControlHostCtl.1\CLSID] 

I tried this and it didn't work at all.  After trying a number of over things (also starting with "reg"), I re-tried the above steps, but chose to install for ALL USERS instead of just me.  It worked!  Whew.

If you're still using Visual SourceSafe, you might try giving Subversion and AnkhSVN a shot.  I've used sccs, rcs, PVCS, VSS, CVS, and Subversion, and the Subversion tool set is the clear winner in my opinion.  It's free, too!

Update (2/13/08):  The current development pre-releases of AnkhSVN support Vista and VS2008 with no registry changes.  I've been using version 1.0.3.2800 for a few weeks now and it's working great.  You can get the latest here.

 Sunday, April 08, 2007
Sunday, April 08, 2007 8:11:42 PM (Pacific Standard Time, UTC-08:00) ( )

.NET serialization is a very simple concept, yet often fails to work the way we'd expect.  The basic idea is that you're converting objects from their in-memory format to some other form, and then back again.

Today I'll walk through some common scenarios that you might encounter when serializing objects to a file with the BinaryFormatter.  The complete set of code examples is provided for download here.

The Serializable Attribute

In order to enable serialization, you must annotate the class to be serialized with the Serializable attribute:

    [Serializable]

    public class CustomerSerializable

    {

        private string id;

        private string company;

        ...

To convert an object from it's in-memory form and write it to a file, we use a BinaryFormatter:

BinaryFormatter formatter = new BinaryFormatter();

 

CustomerSerializable customer1 = new CustomerSerializable();

customer1.City = "Bothell";

customer1.Region = "WA";

 

using (FileStream stream = File.Create("Serialized.dat"))

{

    formatter.Serialize(stream, customer1);

}

Once we have persisted an object to a file using the above code, we can reverse the process by reading the object from the file and rehydrating it in memory:

BinaryFormatter formatter = new BinaryFormatter();

CustomerSerializable customer2 = null;

using (FileStream stream = File.OpenRead("Serialized.dat"))

{

    customer2 = (CustomerSerializable)formatter.Deserialize(stream);

}

Pretty simple, huh?  Of course there are complications in the real world, as we shall see.

How Does it Work?

Essentially, in the simple case shown above, the formatter finds all of the fields of your class using Reflection, and reads their values during serialization.  During deserialization, the data is read from the file and restored to a new empty object.  Interestingly, in the simple case, no constructor of your serialized class is called during deserialization.

ISerializable

You might have heard about the ISerializable interface.  Isn't implementing that just another way of saying that my class is serializable?  Not quite.  You still have to apply the Serializable attribute to the class.  ISerializable just allows us to serialize and deserialize the data in a customized way.  Usually, this means saving only some of the data, or transforming the data before saving it.  Again, you must still apply the Serializable attribute to your class.  If you don't, BinaryFormatter.Serialize() will throw a SerializationException, even though your class supports ISerializable.

Unserializable Fields and Bases

Adding the Serializable attribute to your class is all well and good if the base class (and it's base, etc) and all of the fields of your class are also serializable.  But what if they aren't?  In either case, the Serializable attribute isn't enough - this is where ISerializable is needed.  Let's suppose the base class isn't serializable:

public class BaseNotSerializable

{

    private int totalOrders;

 

    public int OrderTotal

    {

        get { return totalOrders; }

        set { totalOrders = value; }

    }

}

 

[Serializable]

public class CustomerBaseNotSerializableISerializable : BaseNotSerializable, ISerializable

{

    private string id;

    private string company;

    ...

By declaring support for ISerializable, we're telling BinaryFormatter to do two things.  First, when serializing, our GetObjectData method should be called instead of using the Reflection-based mechanism for reading our object's data.  Secondly, during deserialization a special constructor should be called instead of rehydrating the object in the normal way.  In each case, we'll be provided with a SerializationInfo object that contains the data being transferred to or from the object.  Our job in GetObjectData() is to save the data from our object and from the unserializable base of our object:

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)

{

    // Serialize data from the base.

    info.AddValue("OrderTotal", this.OrderTotal);

 

    info.AddValue("Address", this.address);

    info.AddValue("City", this.city);

    info.AddValue("Company", this.company);

    info.AddValue("ContactName", this.contactName);

    info.AddValue("ContactTitle", this.contactTitle);

    info.AddValue("Country", this.country);

    info.AddValue("Fax", this.fax);

    info.AddValue("Id", this.id);

    info.AddValue("Phone", this.phone);

    info.AddValue("Region", this.region);

    info.AddValue("ZipCode", this.zipcode);

}

If our class contained a field that wasn't serializable, we could just skip it in GetObjectData(), or save some other piece of serializable information that we could use later to restore the value.

During deserialization, the special constructor is called, allowing us to read the data back into our object:

public CustomerBaseNotSerializableISerializable(SerializationInfo info, StreamingContext context)

{

    // Deserialize data to the base.

    this.OrderTotal = info.GetInt32("OrderTotal");

 

    this.address = info.GetString("Address");

    this.city = info.GetString("City");

    this.company = info.GetString("Company");

    this.contactName = info.GetString("ContactName");

    this.contactTitle = info.GetString("ContactTitle");

    this.country = info.GetString("Country");

    this.fax = info.GetString("Fax");

    this.id = info.GetString("Id");

    this.phone = info.GetString("Phone");

    this.region = info.GetString("Region");

    this.zipcode = info.GetString("ZipCode");

}

Something to note about this constructor is that it is required when ISerializable is implemented, but it is not part of the ISerializable interface, so it is easy to accidentally omit.  The compiler won't complain, because you have met the ISerializable implementation requirements by providing GetObjectData().  A SerializationException will be thrown at run-time if the constructor is not present.  As I mentioned earlier, no constructor is called in the simple case where ISerializable is not implemented.

But it's not my class!

Implementing ISerializable is a great solution when you're writing your own class.  Sometimes, however, you need to serialize an object provided by a third-party library or the .NET framework itself.  If the class wasn't implemented with the Serializable attribute, you might think that you're stuck.  Fortunately, there's a way around this obstacle, too.  Enter the serialization surrogate.

A serialization surrogate is a class that implements ISerializationSurrogate, and knows how to serialize and deserialize some other class.  We inject this knowledge into the serialization process by attaching the surrogate to the SurrogateSelector of the BinaryFormatter:

BinaryFormatter formatter = new BinaryFormatter();

 

SurrogateSelector selector = new SurrogateSelector();

selector.AddSurrogate(typeof(CustomerNotSerializable), new StreamingContext(StreamingContextStates.All), new CustomerNotSerializableSerializationSurrogate());

formatter.SurrogateSelector = selector;

 

CustomerNotSerializable customer1 = new CustomerNotSerializable();

 

using (FileStream stream = File.Create("Serialized.dat"))

{

    formatter.Serialize(stream, customer1);

}

 

CustomerNotSerializable customer2 = null;

using (FileStream stream = File.OpenRead("Serialized.dat"))

{

    customer2 = (CustomerNotSerializable)formatter.Deserialize(stream);

}

Now when formatter.Serialize() and formatter.Deserialize() are called, the SurrogateSelector is consulted, and methods of the provided CustomerNotSerializableSerializationSurrogate class are called to perform the needed operations:

public class CustomerNotSerializableSerializationSurrogate : ISerializationSurrogate

{

    // GetObjectData is called to serialize the object.

    void ISerializationSurrogate.GetObjectData(object obj, SerializationInfo info, StreamingContext context)

    {

        CustomerNotSerializable customer = (CustomerNotSerializable)obj;

 

        info.AddValue("Address", customer.Address);

        info.AddValue("City", customer.City);

        info.AddValue("Company", customer.Company);

        info.AddValue("ContactName", customer.ContactName);

        info.AddValue("ContactTitle", customer.ContactTitle);

        info.AddValue("Country", customer.Country);

        info.AddValue("Fax", customer.Fax);

        info.AddValue("Id", customer.Id);

        info.AddValue("Phone", customer.Phone);

        info.AddValue("Region", customer.Region);

        info.AddValue("ZipCode", customer.ZipCode);

    }

 

    // SetObjectData is called to deserialize the object.

    object ISerializationSurrogate.SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)

    {

        CustomerNotSerializable customer = (CustomerNotSerializable)obj;

 

        customer.Address = info.GetString("Address");

        customer.City = info.GetString("City");

        customer.Company = info.GetString("Company");

        customer.ContactName = info.GetString("ContactName");

        customer.ContactTitle = info.GetString("ContactTitle");

        customer.Country = info.GetString("Country");

        customer.Fax = info.GetString("Fax");

        customer.Id = info.GetString("Id");

        customer.Phone = info.GetString("Phone");

        customer.Region = info.GetString("Region");

        customer.ZipCode = info.GetString("ZipCode");

 

        return customer;

    }

}

You've probably noticed some similarity between the surrogate class and an implementation of ISerializable.  It's essentially the same thing, with the surrogate being decoupled from the class being serialized.

The Evil Conspiracy of Event Handlers

.NET events are a great architectural feature, but they can pose unexpected problems in serialization.  When you're serializing an object that contains an event, you potentially could be serializing objects of any .NET class.  How could this possibly be, you ask?

Events are sneaky.  Very sneaky.  Think about it for a minute.  An event is just a delegate field with some sugary syntax to make it more convenient.  A delegate is a list of zero or more methods to call when the event is raised.  Each method in the delegate's list references a specific object (if it is an instance method).  If any event handlers have been added to your event, these objects will be serialized when you serialize the object containing the event!

Typically, you just don't want to serialize the delegate field of an event.  You might also have other fields in your class that aren't serializable or that you just don't need to persist.  For these cases, the NonSerialized attribute can be applied to the field, and the serialization mechanism will ignore the field during serialization and deserialization.  Consider these fields of a class:

private string id;

private string company;

private string contactName;

 

[NonSerialized]

private PropertyDescriptor lastPropertyChanged;

 

[NonSerialized]

private PropertyDescriptorCollection properties;

 

[NonSerialized]

private PropertyChangedEventHandler propChanged;

 

public event PropertyChangedEventHandler PropertyChanged

{

    add { propChanged = (PropertyChangedEventHandler)Delegate.Combine(propChanged, value); }

    remove { propChanged = (PropertyChangedEventHandler)Delegate.Remove(propChanged, value); }

}

Here we have a couple of fields that aren't serializable; neither PropertyDescriptor nor PropertyDescriptorCollection can be serialized.  I've annotated these with [NonSerialized] so that the formatter won't attempt to serialize or deserialize them.  Making the PropertyChanged event non-serialized requires the event to be broken up into an declared delegate field and an event with explicit add and remove accessors.  The delegate field (propChanged above) is then annotated with [NonSerialized].  If I had declared the event in the conventional way, I couldn't have applied the NonSerialized attribute to the event.  The layout shown above, with an explicit delegate field (propChanged) and event accessors acting on the delegate mirrors exactly the structure that you would get by specifying the event conventionally:

public event PropertyChangedEventHandler PropertyChanged;

The code above is perfect for ignoring certain fields during serialization and deserialization.  However, it may be the case that after deserialization, these fields really do need valid values.  There are a few ways to handle this.  We could implement ISerializable and restore the values in the special deserializing constructor.  Alternatively, we could provide a surrogate and restore the values in the surrogate's SetObjectData() method.

In the example above, lastPropertyChanged represents the last property that was changed.  Even though PropertyDescriptor is not serializable, I can save a memento of the value in ISerializable.GetObjectData(), and then restore the value from the memento in the deserialization constructor:

// Called during serialization (because support for ISerializable is declared).

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)

{

    info.AddValue("Address", this.address);

    info.AddValue("City", this.city);

    info.AddValue("Company", this.company);

    ...

 

    if (lastPropertyChanged != null)

        info.AddValue("LastPropertyChanged", lastPropertyChanged.Name);

}

 

// Called during deserialization (because support for ISerializable is declared).

public CustomerSerializableFieldsNonSerialized(SerializationInfo info, StreamingContext context)

{

    this.address = info.GetString("Address");

    this.city = info.GetString("City");

    this.company = info.GetString("Company");

    ...

 

    try

    {

        string propertyName = info.GetString("LastPropertyChanged");

        if (propertyName != null)

            lastPropertyChanged = TypeDescriptor.GetProperties(this)[propertyName];

    }

    catch (SerializationException)

    {

        // LastPropertyChanged was not added when the object was serialized (see GetObjectData).

    }

}

The key here is that even though lastPropertyChanged is not serializable, the memento that I save instead (lastPropertyChanged.Name) is serializable.

IDeserializationCallback

Another, simpler way of restoring non-serialized data, is to implement IDeserializationCallback on the class being serialized:

// Called after deserialization (because support for IDeserializationCallback is declared).

void IDeserializationCallback.OnDeserialization(object sender)

{

    properties = TypeDescriptor.GetProperties(this);

}

There's just one method to be provided, as shown.  It is called after deserialization is complete.  In this example, the properties field is a collection of PropertyDescriptors for the type being serialized.  This is the kind of data that is a perfect candidate for IDeserializationCallback - something that can be restored without specific knowledge of the object, or that has a reasonable default value.

Whew.

Serialization can be a little complicated.  It's easier if you just remember a few things:

  1. The Serializable class attribute must always be applied OR a serialization surrogate provided.
  2. Unserializable bases or fields can be serialized with an ISerializable implementation OR by a serialization surrogate.
  3. Fields and events can be omitted from serialization with the NonSerialized attribute.
  4. Non-serialized data can be restored in the ISerializable deserialization constructor OR in the SetObject() method of a serialization surrogate OR in IDeserializationCallback.OnDeserialization().

And that's all I have to say about that.  Download the sample code if you'd like to explore more.

 Sunday, March 11, 2007
Sunday, March 11, 2007 5:46:41 PM (Pacific Standard Time, UTC-08:00) ( )

Note: You can download the complete implementation here.

For those of you just tuning in, ObjectListView is my answer to the .NET need for a DataView-like construct for ordinary objects.  With ObjectListView, you can have a sorted and filtered view of any IList or IList<T>.  You can bind this view to controls like the DataGridView.

This new version adds a debugger visualizer and includes a few other small enhancements and bug fixes.

Bug Fixes

Ecki found a bug in the Master/Details demo where I was doing an invalid cast in the New button click handler.  I also found that .NET 1.1 style property change events weren't wired properly for items already in the list at the time an ObjectListView was constructed.  Those two are fixed.

Boris reported that changing a list item property while iterating over ObjectListView with a foreach loop would cause an InvalidOperationException to be thrown.  Indeed!  This is by design; any change to a .NET collection must invalidate all enumerators of the collection.   ObjectListView is really a view over a collection, though, and not a collection itself.  This view presents the appearance of a collection.  This virtual collection does change when items are added, removed, or replaced in the underlying collection.  When the value of a list item property changes, however, the virtual collection only changes in certain circumstances.  Fortunately, these circumstances are well-understood:  if the property is not included in either the ObjectListView sort or filter, the virtual collection does not change.  In this case, there is no need to invalidate any enumeration in progress.  In the updated version, ObjectListView allows enumeration to continue following a list item property change if the property does not belong to the sort or the filter criteria.

Small Changes

I added a ToArray() method, as I find it irritating to have to declare an array and copy into it with CopyTo() in two steps.  By request, OnListChanged(), OnSorted() and OnAddingNew() are now protected virtual, for extensibility.  These methods follow the usual convention of event-raising methods in .NET.  If you override one of them, be sure to call the base version so that the event will in fact be raised.

License

Since more than a few have asked, I've added the license terms to the Readme.txt file of the download.  ObjectListView is free - no strings attached.

The Visualizer

This is the big cool addition in my estimation.  If you're debugging a program that uses ObjectListView, you can see a nice dialog that shows you the current state of the ObjectListView and it's underlying list.  At a breakpoint, move the mouse over the variable that is the ObjectListView, and click the little magnifying glass.  You'll see something like this:

On the left side, you'll see information about the type of objects in the underlying list ("List Item Type"), and below that, information about the list itself ("List Type").  If the ObjectListView is sorted, the sort properties and sort direction of each are listed.  Below that is the current filter expression, if any.  If you're using a filter predicate (as I am in the example), it will tell you that.

The right side shows two tabs.  The one on the top ("View") shows the list items that are currently exposed by the ObjectListView, in the order defined by the current sort, and excluding any items that are filtered out.  This is exactly what you would see in a foreach loop, enumerating the items in the ObjectListView.  The second tab ("List") shows all of the list items in the underlying list, in the order in which they appear in the list.  Cool, huh?

But wait, there's more!  If you're having problems getting ObjectListView to work the way that you expect it to, check out the Analysis button in the lower left.  If you click that, you'll see a synopsis of how the list and list item types work with ObjectListView.  It will tell you about any potential problems, and recommend a solution.  Here's what it looks like:

I hope that the visualizer is helpful.  Debug visualizers are really not very hard to write, and I encourage you to write your own for any complex types that you're coding up.  Take a look at the source in ObjectListViewVisualizer.cs to see how to do it.  The only significant constraint on your code is that the type you're visualizing needs to be serializable.

Enjoy - and please don't hesitate to speak up if you have questions or thoughts about ObjectListView!