Reputation: 15053
In a project I'm using LINQ to SQL and WCF.
I have the following single-table inheritance:
When I call a WCF method (GetMediaForItem
) which return's a list of ItemMedia
(the base type) objects, I get the exception A first chance exception of type 'System.Runtime.Serialization.SerializationException' occurred in System.Runtime.Serialization.dll
.
But when I call a WCF method (GetYouTubeVideosForItem
) which returns a list of YouTubeVideo
's (one of the derived types), it works fine.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MediaService : IMediaService
{
...
// this throws a System.Runtime.Serialization.SerializationException
public List<ItemMedia> GetMediaForItem(int itemId)
{
using (var context = _db.CreateContext())
{
context.DeferredLoadingEnabled = false;
return (from i in context.ItemMedias
where i.ItemID == itemId
orderby i.Order
select i).ToList();
}
}
// this works fine
public List<YouTubeVideo> GetYouTubeVideosForItem(int itemId)
{
using (var context = _db.CreateContext())
{
context.DeferredLoadingEnabled = false;
return (from i in context.ItemMedias.OfType<YouTubeVideo>()
where i.ItemID == itemId
orderby i.Order
select i).ToList();
}
}
}
The service interface:
[ServiceContract]
public interface IMediaService
{
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "Media?itemId={itemId}",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare
)]
List<ItemMedia> GetMediaForItem(int itemId);
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "YouTubeVideos?itemId={itemId}",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare
)]
List<YouTubeVideo> GetYouTubeVideosForItem(int itemId);
}
And here are the generated models:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.ItemMedia")]
[global::System.Data.Linq.Mapping.InheritanceMappingAttribute(Code="media", Type=typeof(ItemMedia), IsDefault=true)]
[global::System.Data.Linq.Mapping.InheritanceMappingAttribute(Code="video", Type=typeof(YouTubeVideo))]
[global::System.Data.Linq.Mapping.InheritanceMappingAttribute(Code="image", Type=typeof(Image))]
public partial class ItemMedia : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _ID;
private int _ItemID;
private string _MediaType;
private int _Order;
private System.Nullable<System.DateTime> _AddedOn;
private System.Nullable<int> _AddedBy;
private System.Nullable<System.DateTime> _ChangedOn;
private System.Nullable<int> _ChangedBy;
private EntityRef<Item> _Item;
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnIDChanging(int value);
partial void OnIDChanged();
partial void OnItemIDChanging(int value);
partial void OnItemIDChanged();
partial void OnMediaTypeChanging(string value);
partial void OnMediaTypeChanged();
partial void OnOrderChanging(int value);
partial void OnOrderChanged();
partial void OnAddedOnChanging(System.Nullable<System.DateTime> value);
partial void OnAddedOnChanged();
partial void OnAddedByChanging(System.Nullable<int> value);
partial void OnAddedByChanged();
partial void OnChangedOnChanging(System.Nullable<System.DateTime> value);
partial void OnChangedOnChanged();
partial void OnChangedByChanging(System.Nullable<int> value);
partial void OnChangedByChanged();
#endregion
public ItemMedia()
{
this._Item = default(EntityRef<Item>);
OnCreated();
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int ID
{
get
{
return this._ID;
}
set
{
if ((this._ID != value))
{
this.OnIDChanging(value);
this.SendPropertyChanging();
this._ID = value;
this.SendPropertyChanged("ID");
this.OnIDChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ItemID", DbType="Int")]
public int ItemID
{
get
{
return this._ItemID;
}
set
{
if ((this._ItemID != value))
{
if (this._Item.HasLoadedOrAssignedValue)
{
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
this.OnItemIDChanging(value);
this.SendPropertyChanging();
this._ItemID = value;
this.SendPropertyChanged("ItemID");
this.OnItemIDChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_MediaType", DbType="VarChar(10)", IsDiscriminator=true)]
public string MediaType
{
get
{
return this._MediaType;
}
set
{
if ((this._MediaType != value))
{
this.OnMediaTypeChanging(value);
this.SendPropertyChanging();
this._MediaType = value;
this.SendPropertyChanged("MediaType");
this.OnMediaTypeChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Name="[Order]", Storage="_Order", DbType="Int NOT NULL")]
public int Order
{
get
{
return this._Order;
}
set
{
if ((this._Order != value))
{
this.OnOrderChanging(value);
this.SendPropertyChanging();
this._Order = value;
this.SendPropertyChanged("Order");
this.OnOrderChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_AddedOn", DbType="DateTime")]
public System.Nullable<System.DateTime> AddedOn
{
get
{
return this._AddedOn;
}
set
{
if ((this._AddedOn != value))
{
this.OnAddedOnChanging(value);
this.SendPropertyChanging();
this._AddedOn = value;
this.SendPropertyChanged("AddedOn");
this.OnAddedOnChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_AddedBy", DbType="Int")]
public System.Nullable<int> AddedBy
{
get
{
return this._AddedBy;
}
set
{
if ((this._AddedBy != value))
{
this.OnAddedByChanging(value);
this.SendPropertyChanging();
this._AddedBy = value;
this.SendPropertyChanged("AddedBy");
this.OnAddedByChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ChangedOn", DbType="DateTime")]
public System.Nullable<System.DateTime> ChangedOn
{
get
{
return this._ChangedOn;
}
set
{
if ((this._ChangedOn != value))
{
this.OnChangedOnChanging(value);
this.SendPropertyChanging();
this._ChangedOn = value;
this.SendPropertyChanged("ChangedOn");
this.OnChangedOnChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ChangedBy", DbType="Int")]
public System.Nullable<int> ChangedBy
{
get
{
return this._ChangedBy;
}
set
{
if ((this._ChangedBy != value))
{
this.OnChangedByChanging(value);
this.SendPropertyChanging();
this._ChangedBy = value;
this.SendPropertyChanged("ChangedBy");
this.OnChangedByChanged();
}
}
}
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Item_ItemMedia", Storage="_Item", ThisKey="ItemID", OtherKey="ID", IsForeignKey=true)]
public Item Item
{
get
{
return this._Item.Entity;
}
set
{
Item previousValue = this._Item.Entity;
if (((previousValue != value)
|| (this._Item.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Item.Entity = null;
previousValue.ItemMedias.Remove(this);
}
this._Item.Entity = value;
if ((value != null))
{
value.ItemMedias.Add(this);
this._ItemID = value.ID;
}
else
{
this._ItemID = default(int);
}
this.SendPropertyChanged("Item");
}
}
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SendPropertyChanging()
{
if ((this.PropertyChanging != null))
{
this.PropertyChanging(this, emptyChangingEventArgs);
}
}
protected virtual void SendPropertyChanged(String propertyName)
{
if ((this.PropertyChanged != null))
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public partial class YouTubeVideo : ItemMedia
{
private string _YouTubeVideoID;
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnYouTubeVideoIDChanging(string value);
partial void OnYouTubeVideoIDChanged();
#endregion
public YouTubeVideo()
{
OnCreated();
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_YouTubeVideoID", DbType="NVarChar(15)")]
public string YouTubeVideoID
{
get
{
return this._YouTubeVideoID;
}
set
{
if ((this._YouTubeVideoID != value))
{
this.OnYouTubeVideoIDChanging(value);
this.SendPropertyChanging();
this._YouTubeVideoID = value;
this.SendPropertyChanged("YouTubeVideoID");
this.OnYouTubeVideoIDChanged();
}
}
}
}
public partial class Image : ItemMedia
{
private string _ImageName;
private string _ImageData;
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnImageNameChanging(string value);
partial void OnImageNameChanged();
partial void OnImageDataChanging(string value);
partial void OnImageDataChanged();
#endregion
public Image()
{
OnCreated();
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ImageName", DbType="NVarChar(MAX)")]
public string ImageName
{
get
{
return this._ImageName;
}
set
{
if ((this._ImageName != value))
{
this.OnImageNameChanging(value);
this.SendPropertyChanging();
this._ImageName = value;
this.SendPropertyChanged("ImageName");
this.OnImageNameChanged();
}
}
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ImageData", DbType="NVarChar(MAX)")]
public string ImageData
{
get
{
return this._ImageData;
}
set
{
if ((this._ImageData != value))
{
this.OnImageDataChanging(value);
this.SendPropertyChanging();
this._ImageData = value;
this.SendPropertyChanged("ImageData");
this.OnImageDataChanged();
}
}
}
}
Upvotes: 1
Views: 819
Reputation: 28738
WCF, and all serialization process in .NET, has some tricks involved with objects that implement multiple classes. I believe you'd find similar a exception working with just a single object in this case.
The reason is that the YouTubeVideo
is an ItemMedia
item - i.e. it is valid to return a YouTubeVideo
from your method - but if we were to call GetType
on the item it would give a System.Type
of YouTubeVideo
. The types don't match, which confuses the deserialization process.
When an object is serialized in .NET the result includes the given type of the object - think of it as an instruction for the framework to rehydrate the object into the correct type - and in this case the type will be YouTubeVideo
(as that's what GetType
returns). Your deserialization code is expecting an ItemMedia
type object so it's complains with an exception like the one you are seeing.
You can fix this problem using the KnownTypeAttribute
. In this case you would want to add the attribute to the ItemMedia
class, so that the deserializer will understand that YouTubeVideo
is known to be an extension of that type.
[KnownType(typeof(YouTubeVideo))]
public partial class ItemMedia
{
Put this in a partial class rather than your generated code, as it's likely to be destroyed by future code generation if kept in the generated class.
Upvotes: 1