Error when sending an Linq-to-Sql object to the se

2019-07-17 07:56发布

问题:

I am trying to develop a system that has 2 tiers: a mobile client and a server that uses LINQ to SQL to store information in the database. I want to create a WCF server that stores a task in the server, so it will receive a Task from the client and will use LINQ to SQL to store it.

To do this service, I created the dbml file, so I can use the datacontext.

This is my service method:

public Task SaveTask(string token, Task task)
    {
        TrackingDataContext dataConext = new TrackingDataContext();


        //Saves/Updates the task
        dataConext.Tasks.InsertOnSubmit(task);
        dataConext.SubmitChanges();

        return task;
    }

the token will be used in the future

And this is my client method, which will call the service:

private void btnSave_Click(object sender, EventArgs e)
    {
        //Populate the object with the info
        Task task = new Task();
        task.Title = tbTitle.Text;
        task.Description = tbDescription.Text;
        //this is a FK to another table
        task.Severity = ((Severity)lbSeverities.SelectedItem);
        //the first state: "open"
        task.StateID = 1;

        //Save the task
        client.SaveTaskCompleted += new EventHandler<SaveTaskCompletedEventArgs>(client_SaveTaskCompleted);
        client.SaveTaskAsync(App.Token, task);
    }

As you can see, I am creating a new object, populating it and sending it to my service, which will get it and store it. Theoretically it would work ok.

The client is able to create the object and send to the server. When the server receives the request, it tried to build the object again, but then I get a System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException in this method (created automatically from VS2010):

 [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_SeverityID", DbType="Int NOT NULL")]
        public int SeverityID
        {
            get
            {
                return this._SeverityID;
            }
            set
            {
                if ((this._SeverityID != value))
                {
                    if (this._Severity.HasLoadedOrAssignedValue)
                    {
                        throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
                    }
                    this.OnSeverityIDChanging(value);
                                    //The exception happens in this line                    
                                    this.SendPropertyChanging();
                    this._SeverityID = value;
                    this.SendPropertyChanged("SeverityID");
                    this.OnSeverityIDChanged();
                }
            }
        }

It can't be any error in my server method because: 1- it is very simple and has no logic inside 2- I can't even debug it, the error happens before my methos is called, probably when unmarshaling the request.

What could happen something like this? Where can I take a look? Could that be a configuration issue?

Thank you, Oscar

Edit: found this link Serializing Linq2Sql over Wcf - bug or misunderstanding?, where they tell it is a bug, but it is from 1 year ago. Does anyone know if it was solved?

回答1:

So basically what's happening here is that the client is sending a materialized object graph. The Task object has a Severity property and a corresponding SeverityID property. When the server deserializes the client's request, it creates a Task object, then probably deserializes the Severity property first. Now when SeverityID is deserialized, a limitation of LINQ to SQL is being hit...

if (this._Severity.HasLoadedOrAssignedValue) {
    throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}

You can't set a foreign key property (SeverityID) if the related object's property (Severity) is already set.

I suspect that this is why LINQ to SQL calls its serialization mode "unidirectional" because passing a materialized collection back will result in this kind of problem.

Instead of setting the Severity property on the client, try setting the SeverityID property instead. You could also try disabling deferred loading on the server but I don't know if this will have any effect.