views:

286

answers:

1

Working with Linq2Sql as a driver for a Wcf Service. Lets go bottom up....

Down at the bottom, we have the method that hits Linq2Sql...

public virtual void UpdateCmsDealer(CmsDealer currentCmsDealer)
{
    this.Context.CmsDealers.Attach(currentCmsDealer, 
             this.ChangeSet.GetOriginal(currentCmsDealer));
}

That gets used by my Wcf service as such...

public bool UpdateDealer(CmsDealer dealer)
{
    try
    {
     domainservice.UpdateCmsDealer(dealer);
     return true;
    }
    catch
    {
     return false;
    }
}

And called from my Wpf client code thus (pseudocode below)...

[...pull the coreDealer object from Wcf, it is a CmsDealer...]
[...update the coreDealer object with new data, not touchign the relation fields...]
try
{
    contextCore.UpdateDealer(coreDealer);
}
catch (Exception ex)
{
    [...handle the error...]
}

Now, the CmsDealer type has >1< foriegn key relationship, it uses a "StateId" field to link to a CmsItemStates table. So yes, in the above coreDealer.StateId is filled, and I can access data on coreDealer.CmsItemState.Title does show me the tile of the appropriate state.

Now, here is the thing... if you comment out the line...

domainservice.UpdateCmsDealer(dealer);

In the Wcf service it STILL bombs with the exception below, which indicates to me that it isn't really a Linq2Sql problem but rather a Linq2Sql over Wcf issue.

"System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException was unhandled by user code
  Message="Operation is not valid due to the current state of the object."

InnerException is NULL. The end result of it all when it bubles up to the error handler (the Catch ex bloc) the exception message will complain about the deserializer. When I can snatch a debug, the actual code throwing the error is this snippit from the CmsDealer model code built by Linq2Sql.

[Column(Storage="_StateId", DbType="UniqueIdentifier NOT NULL")]
public System.Guid StateId
{
    get
    {
     return this._StateId;
    }
    set
    {
     if ((this._StateId != value))
     {
      if (this._CmsItemState.HasLoadedOrAssignedValue)
      {
       throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
      }
      this.OnStateIdChanging(value);
      this.SendPropertyChanging();
      this._StateId = value;
      this.SendPropertyChanged("StateId");
      this.OnStateIdChanged();
     }
    }
}

In short, it would appear that some stuff is happening "under the covers" which is fine but the documentation is nonexistent. Hell googleing for "ForeignKeyReferenceAlreadyHasValueException" turns up almost nothing :)

I would prefer to continue working with the Linq2Sql objects directly over Wcf. I could, if needed, create a flat proxy class that had no association, ship it up the wire to Wcf then use it as a data source for a server side update... but that seems like a lot of effort when clearly this is an intended scenario... right?

Thanks!

+1  A: 

The error is likely due to something changing the fk value after it has been initially set - are you sure you don't have some custom initialisation code somewhere that might be initially setting the value?

You could breakpoint the set (where it's throwing), and step out each time it's set (skipping the exception if you need to) which should hopefully point you in the right direction.

Steven Robbins
Thanks for the help. In the issue seems to be deep in the Wcf serializer / deserializer not liking to send (clinet -> server )Linq objects with relational data, I had to go with proxy objects.
Soulhuntre
That scenario definately works (or at least I don't remember having issues with it) - would be interesting to see what code was setting/altering the FK value, but if you got it working a different way then that's the main thing :-)
Steven Robbins
Yeah, I never did figure it out, I'll revisit it again in the futre!
Soulhuntre