Question

Question:
Is it possible to (automatically) change the base class of auto-generated domain objects created through the Visual Studio 'Add Web Reference' functionality, without manually modifying References.cs?

Background:
When we add a reference to a web service (via the Visual Studio 'Add Web Reference' functionality), a number of classes are auto-generated. Theses represent a proxy object (for example a MyServiceSoapClient) and a number of auto-generated domain objects (say, for example a CustomerInfo).

So, if I do something along the lines of the following:

MyServiceSoapClient client = new MyServiceSoapClient();
CustomerInfo cust = client.GetCustomer("John Smith");

I will get back a CustomerInfo object with various properties etc, all nicely deserialized from whatever XML the server returned.

The Problem Is...
Lets say I change the value of the Name Property in the cust object to "Bob Dylan".
Ideally, I'd like to have a base class called ServiceEntity that will track if changes have been made (by trapping the continently-provided INotifyPropertyChanged.PropertyChanged event in the base class), to provide a 'Dirty' property indicating that the object has changed since it was fetched from the service.

The Solution
Although the below answer is a good one, we took a slightly different approach...
As the sync status only needs to be recorded in a few situations, it made more sense to add sync tracking through a Generic class, so we can use it as and when needed.
Here's an example generic class and interface:

Interface:

public interface ISyncEntity
{
    /// <summary>
    /// Gets or Sets the Entity Sync State
    /// </summary>
    [XmlIgnore]
    [SoapIgnore]
    EntitySyncState SyncState { get; set; }

    /// <summary>
    /// Flag for deletion
    /// </summary>
    void DeleteOnSync();

    /// <summary>
    /// Flag for Creation
    /// </summary>
    void CreateOnSync();
}

Class:

public class SyncEntity<TEntity> : ISyncEntity
{
    /// <summary>
    /// Backing Field for Entity Property
    /// </summary>
    private TEntity _entity;

    /// <summary>
    /// Gets or Sets the Entity in question
    /// </summary>
    public TEntity Entity
    {
        get { return _entity; }
        set { OnEntityChange(value); }
    }

    /// <summary>
    /// Invoked when a Property on the Entity is changing
    /// </summary>
    /// <param name="entity"></param>
    protected void OnEntityChange(TEntity entity)
    {
        // Detach the property change event handler from the previous entity?
        if (_entity is INotifyPropertyChanged)
            (entity as INotifyPropertyChanged).PropertyChanged -= OnPropertyChange;

        // Set backing field
        _entity = entity;

        // Implements INotifyPropertyChanged?
        if (entity is INotifyPropertyChanged)
            (entity as INotifyPropertyChanged).PropertyChanged += OnPropertyChange;

        // Set the Sync State
        SyncState = EntitySyncState.Unchanged;
    }



    /// <summary>
    /// Fired when a property in the entity changes
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void OnPropertyChange(object sender, PropertyChangedEventArgs e)
    {
        // If a delete or create is already pending, don't worry about the update!
        if (SyncState == EntitySyncState.Unchanged)
            SyncState = EntitySyncState.UpdatePending;
    }

    #region Sync Framework Members

    [XmlIgnore]
    [SoapIgnore]
    public EntitySyncState SyncState
    {
        get;
        set;
    }

    public void DeleteOnSync()
    {
        SyncState = EntitySyncState.DeletePending;
    }

    public void CreateOnSync()
    {
        SyncState = EntitySyncState.CreatePending;
    }

    #endregion
}

Extension Method:

public static SyncEntity<TEntity> ToSyncEntity<TEntity>(this TEntity source)
{
    if (source == null)
        throw new ArgumentException("Source cannot be null");

    return new SyncEntity<TEntity>()
    {
        Entity = source
    };
}
Was it helpful?

Solution

The client proxy classes generated through Visual Studio's Web References feature are built with the .Net Framework's wsdl.exe utility. When generated, the output produces public partial classes. Instead of attempting to modify the auto-generated output, you can provide additional class code in another file that adds the event code you're trying to implement.

Of course, this leaves with implementing similar code for each object. Depending on your source service, you could look into extending SoapHttpClientProtocol (or whichever protocol base class represents your objects) to provide a single implementation for all your inheriting objects. This may not be possible without the use of AOP. As such, your mileage may vary.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top