WCF Deserialization resets DataMember value to default after OnDeserializing callback - wcf

I have developed a WCF Service Application hosted in IIS 7.5 targeting .NET 3.5 configured with only a basicHttpBinding endpoint. The OperationContract signature consists of a Composite type where one of its properties is a custom type. When this property is not initialized by the consuming client, the deserializer on the service appears to ignore the property leaving it null/nothing. I would like to initialize this custom type if it null/nothing and I realize that WCF serialization doesn't call constructors so I've used the deserialization callback. The callback executes and intializes the type but immediately after the callback completes this property returns to null/nothing. Stepping through the code, the ExtensionData property setter executes immediately after the callback and it is at this point where I notice that the property is reset to null/nothing. What am I missing? Here is my sample code
<DataContract(Name:="Request")> _
Public Class Request
Implements IExtensibleDataObject
<DataMember(Name:="MyCustomType")>
Public MyCustomType As CustomType
Private _ExtensionDataObject As ExtensionDataObject
Public Overridable Property ExtensionData() As ExtensionDataObject Implements IExtensibleDataObject.ExtensionData
Get
Return _ExtensionDataObject
End Get
Set(value As ExtensionDataObject)
_ExtensionDataObject = value
End Set
End Property
<OnDeserializing()>
Sub OnDeserializing(c As StreamingContext)
Me.myCustomType = New CustomType()
End Sub
End Class

If the client didn't initialize the property, then it's value is actually Nothing, and the fact that it is null/Nothing will be present in the serialized Request object. So prior to the deserialization happening, your OnDeserializing method is called, and it initializes the variable; but then the deserialization happens, and since there is a value for the property (which happens to be Nothing/null), it will override it.
I think what you want is to have an OnDeserializ*ed* callback, which will initialize the member after the deserialization happened, if it's value is Nothing:
<OnDeserialized()>
Sub OnDeserialized(ByVal c as StreamingContext)
If Me.myCustomType Is Nothing Then
Me.myCustomType = new CustomType()
End If
End Sub

Related

From A Method With In The Class Return An Instance Of The Class As An Interface Type That The Class Implements

what I'm trying to archive with the code below is to have the GetInstance generic function take in an interface type that SystemVars implements (say IAuthentication) then create an instance of SystemVars and return it as interface type T.
The problem I an having is that no matter what casting method I try I can't find a way to return the new instance of SystemVars as T. The line in the GetInstance method Return <CastingFunction>(New SystemVars,T) always fails to compile with the error message saying Value of type SystemVars cannot be converted to 'T'.
How do I return the instance of the class as the interface type that was passed into T?
Imports System.Drawing
Public Class SystemVars
Implements IAuthentication,
IAuthorization,
IApplicationStarting
Private Sub New()
End Sub
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(New SystemVars, T)
End Function
Public ReadOnly Property Username As String _
Implements IAuthentication.Username,
IAuthorization.Username
Get
Return _userName
End Get
End Property
Public ReadOnly Property Rolls As List(Of String) _
Implements IAuthorization.Rolls
Get
Return _rolls
End Get
End Property
Public ReadOnly Property InstallationId As Guid _
Implements IAuthentication.InstallationId,
IApplicationStarting.InstallationId
Get
Return _installationId
End Get
End Property
Public ReadOnly Property MainWindowStartUpPlacement As Rectangle _
Implements IApplicationStarting.MainWindowStartUpPlacement
Get
Return _mainWindowStartUpPlacement
End Get
End Property
'........
Private Shared _userName As String
Private Shared _rolls As List(Of String)
Private Shared _installationId As Guid
Private Shared _mainWindowStartUpPlacement As Rectangle
End Class
You can make an otherwise illegal cast work by passing through Object.
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(CObj(New SystemVars), T)
End Function
You will get a runtime error if the cast isn't possible; as noted in the comments, this strategy is chucking type safety out the window and basically telling the compiler, "Don't bother me, I know what I'm doing." The runtime will throw an InvalidCastException on failure if you don't test and throw yourself. You can test using Type.IsAssignableFrom if you want to create a more developer-friendly error message; there isn't much context available in the debugger at the point of failure, though it may be pretty obvious if you look up the call stack.
For just three interfaces, it might be better to do three separate specific functions rather than a generic version, especially considering that the functions are necessarily Shared (and thus can't themselves be part of an interface).
You might also consider a design that includes a Dependency Injection container. In this kind of design, there would be a configuration step that would associate the interfaces with SystemVars as the implementation, then the client would ask the container for an instance of the interface and receive a SystemVars object.
The rough way that the three options (the third being to cast the SystemVars object to the requested interface) would look in code is:
'Casting a received object to a requested interface
Dim asInterface = DirectCast(SystemVars.GetInstance(), IAuthorization)
'Using a custom casting function on SystemVars
Dim asInterface = SystemVars.GetInstance(Of IAuthorization)
'Using a DI container
'Behavior if the interface isn't supported depends on the container
Dim asInterface = container.GetInstance(Of IAuthorization)
Note that TryCast could be used instead of DirectCast, in which case the result would be Nothing if the interface isn't supported.

How To Access A Shared Property Of A Class Passed As A Type Parameter

I'm trying to access a shared property of a class passed as a parameter to a type-parametrised procedure. The reason why I'm doing this is so I can embed the various API call endpoints (among other class-specific things) as properties within the class itself. I've read some similar SO posts but nothing is close enough to be sure that it isn’t possible (which I think is likely).
Below is the essence of the structure - there's some pseudo code towards the end:
MustInherit Class BaseClass
Shared Property Endpoint As String
End Class
Class Person
Inherits BaseClass
Property Age As Integer
Property Name As String
Sub New()
_Endpoint = "/GetPerson"
End Sub
End Class
Class Event
Inherits BaseClass
Property When As Date
Property Type As String
Sub New()
_Endpoint = "/GetEvent"
End Sub
End Class
Function Retrieve(T As BaseClass)(Id As String) As T
Dim oResp As HttpResponse = MakeGetCall(T.Endpoint, Id) <- T.Endpoint throws a compile error
Return Deserialize(Of T)(oResp.Content)
End Function
Dim oPerson As Person = Retrieve(Of Person)("123")
Dim oEvent As Event = Retrieve(Of Event)("123")
To my tiny mind, I would have thought that, since T’s base class is BaseClass which contains the property Endpoint, I’d be ok. But seemingly not.
I've tried a fair few things from here on SO and other places to overcome this to no avail. Yes, I realize I could perform some kind of endpoint look-up based on the type of T but the above represents a very clean solution and I’d like to get it to work if possible.
Any ideas?
Assuming you want EndPoint to be different for each subclass, you should use MustOverride instead of Shared...
MustInherit Class BaseClass
Public MustOverride Property EndPoint As String
End Class
Then return a constant in each subclass
Class Person
Inherits BaseClass
Public Overrides Property EndPoint As String
Get
Return "/Person"
End Get
You might want to declare EndPoint as ReadOnly too.
The small limitation is that you'll need an instance of the class to access EndPoint (since it isn't Shared). If you have a parameterless constructor, you could use (New Person).EndPoint where needed.

Why is this Entity Framework association not loading lazily?

I'm using a Code First Entity Framework approach, and in my OnModelCreating function I have the following code:
With modelBuilder.Entity(Of FS_Item)()
.HasKey(Function(e) e.ItemKey)
.Property(Function(e) e.ItemRowVersion).IsConcurrencyToken()
.HasMany(Function(e) e.ItemInventories) _
.WithRequired(Function(e) e.Item).HasForeignKey(Function(e) e.ItemKey)
End With
Elsewhere I have a Web API Get implementation with some diagnostic code I'm looking at in a debugger:
Public Function GetValue(ByVal id As String) As FS_Item
GetValue = If(data.FS_Item.Where(Function(i) i.ItemNumber = id).SingleOrDefault(), New FS_Item())
Dim c = GetValue.ItemInventories.Count
End Function
I expect that c should get a non-zero value by looking up rows in the FS_Inventory view where ItemKey matches the retrieved FS_Item row's ItemKey. But I'm getting 0 even though there are matching rows. Am I calling .HasMany, .WithRequired and .HasForeignKey properly?
Note that .WithRequired is operating on the return value from the previous line whereas the other lines are operating on the With block expression.
Edit This model for FS_Item has been requested. Here it is:
Partial Public Class FS_Item
Public Property ItemNumber As String
Public Property ItemDescription As String
Public Property ItemUM As String
Public Property ItemRevision As String
Public Property MakeBuyCode As String
' Many many more properties
Public Property ItemRowVersion As Byte()
Public Property ItemKey As Integer
Private _ItemInventories As ICollection(Of FS_ItemInventory) = New HashSet(Of FS_ItemInventory)
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
Get
Return _ItemInventories
End Get
Friend Set(value As ICollection(Of FS_ItemInventory))
_ItemInventories = value
End Set
End Property
End Class
Edit Learned something interesting. If I change Dim c = GetValue.ItemInventories.Count to this:
Dim c = data.FS_ItemInventory.ToList()
Dim correctCount = GetValue.ItemInventories.Count
Then correctCount gets the value of 3. It's like it understands the association between the objects, but not how to automatically query them as I'm used to coming from LINQ-to-SQL. Is EF different somehow in this regard?
Edit I have determined that I can make the associated objects load using this explicit loading code:
data.Entry(GetValue).Collection(Function(e) e.ItemInventories).Load()
What I want to understand now is what exactly determines whether an entity will load lazily or not? From all indications I can find, it should have loaded lazily. I even tried changing the declaration of ItemInventories to this, but then I got a NullReferenceException when trying to access it:
Public Overridable Property ItemInventories As ICollection(Of FS_ItemInventory)
It turns out that code which I thought was unrelated had disabled lazy loading. I have this in the constructor of FSDB:
DirectCast(Me, IObjectContextAdapter).ObjectContext.ContextOptions.ProxyCreationEnabled = False
Thanks to EF 4 - Lazy Loading Without Proxies I see that this will also disable lazy loading. The reason that code had been added was due to another error:
Type
'System.Data.Entity.DynamicProxies.FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418'
with data contract name
'FS_Item_64115A45C642902D6044AFA1AFD239E7DCB82FD000A10FE4F8DE6EA26A2AB418:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
And according to Serialization of Entity Framework objects with One to Many Relationship, the easy solution for that was to disable proxies.

Only first DataAnnotation validation being applied

I'm currently working on a Winforms application written in VB.NET and implementing the Entity Framework (4.4). I want to add validation attributes to my entities so that I can validate them on the UI - just as I do in MVC.
I have created my 'Buddy Class' which contains an IsValid method and points to a 'MetaData' class that contains the data annotations.
Imports System.ComponentModel.DataAnnotations
Imports System.Runtime.Serialization
Imports System.ComponentModel
<MetadataTypeAttribute(GetType(ProductMetadata))>
Public Class Product
Private _validationResults As New List(Of ValidationResult)
Public ReadOnly Property ValidationResults() As List(Of ValidationResult)
Get
Return _validationResults
End Get
End Property
Public Function IsValid() As Boolean
TypeDescriptor.AddProviderTransparent(New AssociatedMetadataTypeTypeDescriptionProvider(GetType(Product), GetType(ProductMetadata)), GetType(Product))
Dim result As Boolean = True
Dim context = New ValidationContext(Me, Nothing, Nothing)
Dim validation = Validator.TryValidateObject(Me, context, _validationResults)
If Not validation Then
result = False
End If
Return result
End Function
End Class
Friend NotInheritable Class ProductMetadata
<Required(ErrorMessage:="Product Name is Required", AllowEmptyStrings:=False)>
<MaxLength(50, ErrorMessage:="Too Long")>
Public Property ProductName() As Global.System.String
<Required(ErrorMessage:="Description is Required")>
<MinLength(20, ErrorMessage:="Description must be at least 20 characters")>
<MaxLength(60, ErrorMessage:="Description must not exceed 60 characters")>
Public Property ShortDescription As Global.System.String
<Required(ErrorMessage:="Notes are Required")>
<MinLength(20, ErrorMessage:="Notes must be at least 20 characters")>
<MaxLength(1000, ErrorMessage:="Notes must not exceed 1000 characters")>
Public Property Notes As Global.System.String
End Class
The first line in the IsValid method registers the MetaData class (only way I could find that actually worked - otherwise no annotations were honored!). I then use the System.ComponentModel.Validator.TryValidateObject method to perform the validation.
When I call the IsValid method on an instance with an empty (null/nothing) ProductName the validation fails and the ValidationResults collection is populated with the correct error message. So far so good.....
However, if I call IsValid on an instance with a ProductName which is longer than 50 characters the validation passes despite the MaxLength attribute!
Also, if I call IsValid on an instance with a valid ProductName (not empty and not more than 50 characters) but without a ShortDescription the validation passes even though there is a Required annotation on that property.
What am I doing wrong here?
Try the other method signature for TryValidateObject() and explicitly set validateAllProperties to true:
Dim validation = Validator.TryValidateObject(
Me, context, _validationResults, true)

Adding Properties to WCF DataContract

I've added a service reference to my WCF webservice which generated all of my datacontract objects. I'm using BasicHttpBinding. Using a partial class, I've made one of these objects inherit from another class that adds some properties to it. Now it throws an error when making a call to the service:
Test method CP.Exg2010.Tests.UnitTest1.TestWCF threw exception:
System.ServiceModel.Dispatcher.NetDispatcherFaultException: The
formatter threw an exception while trying to deserialize the message:
There was an error while trying to deserialize parameter
http://tempuri.org/:RunResult. The InnerException message was 'Error
in line 1 position 283. 'Element' 'CommandResult' from namespace
'uri://mycomp.corp/line/exg2010' is not expected. Expecting element
'_EngineTracingData'.'. Please see InnerException for more details.
---> System.Runtime.Serialization.SerializationException: Error in
line 1 position 283. 'Element' 'CommandResult' from namespace
'uri://mycomp.corp/line/exg2010' is not expected. Expecting element
'_EngineTracingData'.
CommandResult is a property that is part of the WSDL. _EngineTracingData is the private field used by a property in a base class.
<XmlIgnore()> <SoapIgnore()> <Newtonsoft.Json.JsonIgnore()> _
Private _EngineTracingData As String = String.Empty
<XmlIgnore()> <SoapIgnore()> <Newtonsoft.Json.JsonIgnore()>
Public Property EngineTracingData As String Implements Interfaces.ICPMasterBaseInfo.EngineTracingData
Get
Return Me._EngineTracingData
End Get
Set(ByVal value As String)
Me._EngineTracingData = value
End Set
End Property
I read something about the deserialization happening in alphabetic order, which would explain why _EngineTracingData is first. But, that field/property shouldn't even be used in deserialization!
Any help would be appreciated!
Ah, I found it!
Adding
<NonSerialized()>
to the private fields in the base class fixed my issue!