WCF/Entity Framework - How To Get a Nice DataContract? - wcf

Is there a clean way of using the auto-generated EF classes with WCF instead of having to hand-make classes for WCF DataContracts?
The classes are located in the LAIT.Entities.dll
ProductService.vb
Public Class ProductService
Implements IProductService
Public Function GetWorkOrder(ByVal WorkOrderID As Integer) As
WorkOrder Implements IProductService.GetWorkOrder
Using dc As New LAIT.Model.LAITEntities
Try
Dim _Order = (From o In dc.WorkOrder
Where o.WorkOrderID = WorkOrderID
Select o).SingleOrDefault
Return _Order
Catch ex As Exception
Return Nothing
End Try
End Using
End Function
End Class
IProductService.vb
Public Interface IProductService
<OperationContract()>
Function GetWorkOrder(ByVal WorkOrderID As Integer) As
LAIT.Entities.WorkOrder
End Interface

Yes. For that you need to edit the T4 file that VS uses to auto generate your entities. All you need to do is to make sure that all your classes are decorated with <DataContract> attribute and your properties are marked with <DataMember> attribute. I assume that you are using POCO entities because both Entity Objects and STEs are already marked as DataContract and you don't need to do anything in order to use them in your WCF services.
Having said that, I strongly discourage you from using Entity Objects across the wire with WCF. You should use either STEs or POCOs in this scenaio.

The POCO proxy type cannot be directly serialized or deserialized by the Windows Communication Foundation (WCF), because the DataContractSerializer serialization engine can only serialize and deserialize known types. The proxy type is not a known type.
If your POCO entities don't have any "Navigation Properties" you can have serialized objects of your entities through WCF Services by adding <DataContract> and <DataMember> properties in your class.
But for Entities with "Navigation Properties" In addition of adding <DataContract> and <DataMember> properties in your class you need to have some changes in your WCF Service as follows. Add the following calss in your WCF Service project.
1.
Imports System.Data.Objects
Imports System.ServiceModel.Description
Imports System.ServiceModel.Channels
Public Class ApplyDataContractResolverAttribute
Inherits Attribute
Implements IOperationBehavior
Public Sub New()
End Sub
Public Sub AddBindingParameters(ByVal operationDescription As OperationDescription, ByVal parameters As BindingParameterCollection) Implements IOperationBehavior.AddBindingParameters
End Sub
Public Sub ApplyClientBehavior(ByVal operationDescription As OperationDescription, ByVal proxy As System.ServiceModel.Dispatcher.ClientOperation) Implements IOperationBehavior.ApplyClientBehavior
Dim dataContractSerializerOperationBehavior As DataContractSerializerOperationBehavior = operationDescription.Behaviors.Find(Of DataContractSerializerOperationBehavior)()
dataContractSerializerOperationBehavior.DataContractResolver = New ProxyDataContractResolver()
End Sub
Public Sub ApplyDispatchBehavior(ByVal operationDescription As OperationDescription, ByVal dispatch As System.ServiceModel.Dispatcher.DispatchOperation) Implements IOperationBehavior.ApplyDispatchBehavior
Dim dataContractSerializerOperationBehavior As DataContractSerializerOperationBehavior = operationDescription.Behaviors.Find(Of DataContractSerializerOperationBehavior)()
dataContractSerializerOperationBehavior.DataContractResolver = New ProxyDataContractResolver()
End Sub
Public Sub Validate(ByVal operationDescription As OperationDescription) Implements IOperationBehavior.Validate
' Do validation.
End Sub
End Class
2.Open the service interface file. By default, it is called IService1.
3.Replace the code that defines the service interface file with the following code:
C#
[ServiceContract]
public interface IService1
{
[OperationContract]
[ApplyDataContractResolver]
void UpdateOrder(Order updated);
[OperationContract]
[ApplyDataContractResolver]
Order GetOrder(int OrderID);
}
VB
<ServiceContract> _
Public Interface IService1
<OperationContract> _
<ApplyDataContractResolver> _
Sub UpdateOrder(updated As Order)
<OperationContract> _
<ApplyDataContractResolver> _
Function GetOrder(OrderID As Integer) As Order
End Interface
And you are ready to go.

Related

Method 'set_Description' in type 'myAssembly.NetProduct' from assembly 'myAssembly' does not have an implementation

I have a DLL file created in VB6. It contains a class named Product and that contains the following simple code:
Option Explicit
Private sDescription As String
Public Property Get Description() As String
Description = sDescription
End Property
Public Property Let Description(Value As String)
sDescription = Value
End Property
I want to use this DLL in VB.NET, which is nothing more than registering the DLL on my system and including the DLL file in the references. Visual Studio automatically generates an interop DLL to consume the COM DLL. This interop DLL generates interfaces for all classes. In VB.NET I want to create a new class that implements the Product interface from the interop DLL. So I code:
Imports myAssembly
Public Class NetProduct
Implements myAssembly.Product
Public Property Description As String Implements _Product.Description
Get
Throw New NotImplementedException()
End Get
Set(value As String)
Throw New NotImplementedException()
End Set
End Property
End Class
The property is auto-generated because I implemented the Product interface. But here comes the problem because when I start using the NetProduct class I get an error telling me this:
Method 'set_Description' in type 'myProject.NetProduct' from
assembly 'myProject, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' does not have an implementation.
The problem is that there is no method set_Description in the interface. When I view the definition of the Product interface it shows me the following:
Imports System.Runtime.InteropServices
Namespace myAssembly
<CoClass(GetType(ProductClass))> <Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")>
Public Interface Product
Implements _Product
End Interface
End Namespace
The definition of the _Product interface is:
Imports System.Runtime.InteropServices
Namespace myAssembly
<Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")> <TypeLibTypeAttribute(4304)>
Public Interface _Product <DispId(1745027072)>
Property Description As String
End Interface
End Namespace
When I use the interface myAssembly.Product directly to create a new object then everything works as you would expect. The property does not pose a problem there. But when I implement the interface in a .NET class the problem arises.
How do I solve this?
[update 1] After creating a method Set_Description I see the following error appear:
property 'Description' implicitly defines 'set_Description', which
conflicts with a member of the same name in class 'NetProduct'.
This must have something to do with my problem, although I don't know what it is. I already tried completing the property to make sure the Throw New NotImplementedException() wouldn't be in the way but that didn't make the error go away. My code builds just fine by the way. The error I gave earlier is a runtime error. Not a build error.
Private myDescription As String
Public Property Description As String Implements Product.Description
Get
Return myDescription
End Get
Set(value As String)
myDescription = value
End Set
End Property
[update 2] I have used JetBrains DotPeek to disassemble the interop.dll that Visual Studio generates. Disassembly is coded in C#. It contains 2 interfaces and 1 class for the single Product class from VB6. Here are all details.
I'll start with the Product class itself.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace myAssembly
{
[ClassInterface(0)]
[Guid("C54B96A8-1499-4B76-8508-0B732E551326")]
[TypeLibType(2)]
[ComImport]
public class ProductClass : _Product, Product
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern ProductClass();
[DispId(1745027072)]
public virtual extern string Description { [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.BStr)] get; [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [param: MarshalAs(UnmanagedType.BStr), In, Out] set; }
}
}
The ProductClass uses 2 interfaces. I don't understand why because one of those is just an implementation of the other. This is the Product interface.
using System.Runtime.InteropServices;
namespace myAssembly
{
[CoClass(typeof (ProductClass))]
[Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")]
[ComImport]
public interface Product : _Product
{
}
}
And then we have the _Product interface. They even share the same Guid. It might have something to do with backwards compatibility.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace myAssembly
{
[Guid("49CE2F98-931C-441B-B322-9F39B6D6F212")]
[TypeLibType(4304)]
[ComImport]
public interface _Product
{
[DispId(1745027072)]
string Description { [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [return: MarshalAs(UnmanagedType.BStr)] get; [DispId(1745027072), MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] [param: MarshalAs(UnmanagedType.BStr), In, Out] set; }
}
}
This is all I could find. Still no clue where the error for Set_Description comes from.
[Update 3] Example code
The code for the VB6 class is on top of this question. Nothing fancy there. The code for testing implementation in .NET is like this:
Imports myAssembly
Public Class NetProduct
Implements myAssembly.Product
Private myDescription As String
Public Property Description As String Implements Product.Description
Get
Return myDescription
End Get
Set(value As String)
myDescription = value
End Set
End Property
End Class
To test the NetProduct class I dropped a Button on a Form and create an instance of the class when the button is being clicked.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click '<- Error happens here, so on loading the datatype!
Dim Product As New NetProduct 'Error does NOT happen here.
End Sub
The whole project compiles without errors. The project even runs without errors UNTIL you click the button. Probably because the NetProduct type is first loaded on that point.
I used a console app to do my test. Other than that, my VB.NET code is basically identical to yours in update 3. The VB.NET properties were auto-generated by VS with the stub Throw New NotImplementedException() after using the Implements statement :
Imports OurCOMDll
Class TestClass
Implements OurCOMDll.ClassInCOMDll
Dim msStringProperty As String = String.Empty
Public Property StringProperty As String Implements _ClassInCOMDll.StringProperty
Get
StringProperty= msStringProperty
End Get
Set(value As String)
msStringProperty = value
End Set
End Property
End Class
Module Module1
Sub Main()
Dim o As New OurCOMDll.ClassInCOMDll
o.StringProperty = "Hello World!"
Console.WriteLine(o.StringProperty) ' Outputs 'Hello World!' as expected
Console.ReadLine()
End Sub
End Module
Same is true for the VB6 code. The string property is implemented like yours.
Distinguishing factors so far:
VS 2019 vs. VS 2017
(Consuming) GUI vs. Console application
Different property names

Generic Collection of T for ICollection of DbEntities

The Situation-
Have EF database entities with collections of other database entities as navigation properties all of which are not exposed outside of the project. Each of these entities are nested in base classes that allow public read and protected set access to the Db entity's property values and are the classes the business layer will work with. So these base classes also need collections of their Db entity's navigation properties.
What I have now-
I am still working on how I want the data layer to interact with with the other layers that reference it. So I have been experimenting with creating collection classes that can be set equal to a collection of Db entities and create a collection of the base class that the Db entity is nested in. The collection is then exposed outside the class though an interface as well as the base class. The interface gives access to some custom methods of collection class like accepting DTOs and prebuilt quires but would hide any Add methods.
The Problem-
I would like to create a generic base class for the collections but cant seem to find a way. I have gotten close to something workable but it was ugly and confusing. Below is an outline of what I'm working with. The narrowing operators in the entity class work well. I would rather have widening operators and narrowing operators in the base class so I could keep the New method private for the base classes but I don't want to expose the actual database entities outside the Data Layer.
Interops Namespace referenced by all projects in solution-
Public Interface IGenericEntity
End Interface
Public Interface INavigationPropertyEntity : Inherits IGenericEntity
End Interface
Public Interface IDbEntityToExposedEntityCollection(Of TEntity As IGenericEntity)
'Used to hide Add method
End Interface
Public Interface IPublicEntity
ReadOnly Property NavProps As IDbEntityToExposedEntityCollection(Of INavigationPropertyEntity)
End Interface
Data Layer project-
-Database Entities
Friend Class DbEntity1
Public Shared Narrowing Operator CType(ByVal value As DbEntity1) As NavigationPropertyEntity
Return New NavigationPropertyEntity(value)
End Operator
End Class
Friend Class DbEntity2
Public Sub New()
NavigationPropertyDbEntities = New HashSet(Of DbEntity1)
End Sub
Public Property NavigationPropertyDbEntities As ICollection(Of DbEntity1)
Public Shared Narrowing Operator CType(ByVal value As DbEntity2) As PublicEntity
Return New PublicEntity(value)
End Operator
End Class
-Exposed Base Classes
Public Class NavigationPropertyEntity : Implements INavigationPropertyEntity
Private _value As DbEntity1
Friend Sub New(value As DbEntity1)
_value = value
End Sub
End Class
Public Class PublicEntity : Implements IPublicEntity
Dim _value As DbEntity2
Friend _NavProps As DbEntityToPublicEntityCollection(Of INavigationPropertyEntity, DbEntity1, NavigationPropertyEntity)
Public ReadOnly Property NavProps As IDbEntityToExposedEntityCollection(Of INavigationPropertyEntity) Implements IPublicEntity.NavProps
Get
Return _NavProps
End Get
End Property
Friend Sub New(value As DbEntity2)
_value = value
_NavProps = new DbEntityToPublicEntityCollection(Of INavigationPropertyEntity, DbEntity1, NavigationPropertyEntity)(_value.NavigationPropertyDbEntities)
End Sub
End Class
-Entity Specific Collection
Friend Class DbEntityToPublicEntityCollection(Of IEnt As INavigationPropertyEntity, DbEnt As DbEntity1, ExpEnt As NavigationPropertyEntity)
Inherits HashSet(Of IEnt)
Implements IDbEntityToExposedEntityCollection(Of IEnt)
Public Sub New(value As HashSet(Of DbEnt))
For Each ent In value
Dim exp As NavigationPropertyEntity = ent
Dim i As INavigationPropertyEntity = exp
MyBase.Add(i)
Next
End Sub
End Class
Additional Info Edit
The main issue is being able to have a generic collection declared something like,
Friend Class DbEntityToPublicEntityCollection(Of IEnt, DbEnt as class, ExpEnt As class)
because in the New sub ExpEnt cannot be converted to DbEnt and a TryCast will just result in a runtime error.
Like I said, I tried setting up classes that are inherited by the exposed and entity classes so a new method with a parameter for the exposed class could be set up but with trying to have the interfaces in there it got unmanageable pretty quick. Then again I have not worked much with making my own generic types before so I'm not sure if this is even a proper strategy.

Castle.Windsor Intercept WCF operation return type

Given this operation contract:
<ServiceContract()>
Public Interface IService1
<OperationContract()>
Function GetData(ByVal composite As CompositeType) As CompositeType
End Interface
I create a WCF Client using the Castle.Windsor WCFClientFacility like this:
container.Register(Component.
For(Of IService1).
ImplementedBy(Of Service1)().
AsWcfClient(New DefaultClientModel() With {
.Endpoint = WcfEndpoint.
BoundTo(New BasicHttpBinding()).
At(String.Format("http://localhost:50310/{0}.svc", "Service1"))
}))
This all works fine, but now I want to be able to proxy the return type of the GetData operation, CompositeType. Just registering the CompositeType in the container like this:
container.Register(Component.For(Of CompositeType).Interceptors(GetType(MyInterceptor))
did not do the trick... Is this behaviour possible? The purpose of this would be implementing INPC on the returning object automatically using a proxy/interceptors. The key would be the possibility to intercept the serializer while it activates a new instance of the CompositeType?
The reason that this does not work is that the Castle Windsor client proxy is just a wrapper round the WCF client proxy, and as the WCF client proxy creates the objects returned by service methods they aren't tracked by Castle Windsor. You can, however, intercept the methods of the Castle Windsor client proxy so that when it wants to return a CompositeType you get it to return an intercepted proxy object instead.
Note: For this to work, CompositeType must not be NotInheritable and any methods you want to intercept must be Overridable.
container.Register(Component.
For(Of IService1).
ImplementedBy(Of Service1)().
AsWcfClient(New DefaultClientModel() With {
.Endpoint = WcfEndpoint.
BoundTo(New BasicHttpBinding()).
At(String.Format("http://localhost:50310/{0}.svc", "Service1"))
})
.Interceptors(Of ServiceInterceptor)
)
Also register two interceptors, one (ServiceInterceptor) to intercept the Service method calls, and one (CompositeTypeInterceptor) to intercept the methods of the object returned:
container.Register(
Component.For(Of ServiceInterceptor)(),
Component.For(Of CompositeTypeInterceptor)()
)
Here is the code of ServiceInterceptor. It intercepts all methods and, for any which return CompositeType, returns a proxy for CompositeType instead which has all methods intercepted by CompositeTypeInterceptor:
Imports Castle.DynamicProxy
Imports System
Public Class ServiceInterceptor
Implements IInterceptor
Private _proxyGenerator As ProxyGenerator
Private _compositeTypeInterceptor As CompositeTypeInterceptor
Public Sub New(compositeTypeInterceptor As CompositeTypeInterceptor)
_proxyGenerator = New ProxyGenerator()
_compositeTypeInterceptor = compositeTypeInterceptor
End Sub
Public Sub Intercept(invocation As IInvocation) Implements IInterceptor.Intercept
invocation.Proceed()
If TypeOf invocation.ReturnValue Is CompositeType Then
invocation.ReturnValue = _proxyGenerator.CreateClassProxyWithTarget(Of CompositeType)(CType(invocation.ReturnValue, CompositeType), New IInterceptor() {_compositeTypeInterceptor})
End If
End Sub
End Class
And here is the code of CompositeTypeInterceptor. All it does is print a debug message but you could alter it to do what you want.
Imports Castle.DynamicProxy
Imports System
Imports System.Diagnostics
Public Class CompositeTypeInterceptor
Implements IInterceptor
Public Sub Intercept(invocation As IInvocation) Implements IInterceptor.Intercept
Debug.Print("Intercepted " + invocation.Method.Name)
invocation.Proceed()
End Sub
End Class

fake EF DBContext in Visual Basic.NET

I found solution for faking DBContext in C#. Creating an Interface IDbContext. For production code I create a LifeDbContext that inherit from DbContext and implement IDbContext. For unit testing I create a class FakeDbContext that implements also IDbContext and fake the Datebase with Collections ... fine.
But now I have to do it with Visual Basic.NET. Because of I have to implicitly implement each member of the interface IDbContext I can not implement the members of DbContext base class.
In c# there is no need to explicit implement a member of an interface.
Any idea?
Here my code:
Public Interface IMyDbContext
Property Orders As IDbSet(Of Oders)
...
ReadOnly Property Database() As Database
Function SaveChanges() As Integer
ReadOnly Property Configuration As Infrastructure.DbContextConfiguration
End Interface
Public Class MyDbContext
Inherits DbContext
Implements IDbContext
Public Property Orders As IDbSet(Of Orders) Implements IMyDbContext.Oders
...
Public ReadOnly Property Database() As Database Implements ILimsDbContext.Database
Get
Return MyBase.Database
End Get
End Property
Public Function SaveChanges() As Integer Implements IMyDbContext.SaveChanges
Return MyBase.SaveChanges()
End Function
Public ReadOnly Property Configuration() As Infrastructure.DbContextConfiguration Implements IMyDbContext.Configuration
Get
Return MyBase.Configuration
End Get
End Property
End Class
What I don't like is the shadowing the already in DbContext implemented members in MyDbContext only for explicit implement the interface.
You can modify the t4 template to add your interfaces and implements clauses, but that would lock you into your customized t4 implementation. I did something along these lines in a post on adding property logging a while back. See if this post is helpful.

ContractException when serialize my custom datatable

I got error when try execute this wcf code
An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension: System.ServiceModel.Description.DataContractSerializerOperationBehavior
contract: http://tempuri.org/:IService1 ----> System.Runtime.Serialization.InvalidDataContractException: Method 'MyClassLibrary.MyDataTable.GetSchema()' must return a schema with a valid Id.
at System.Runtime.Serialization.SchemaExporter.InvokeGetSchemaMethod(Type clrType, XmlSchemaSet schemas, XmlQualifiedName stableName)
at System.Runtime.Serialization.SchemaExporter.ExportXmlDataContract(XmlDataContract dataContract)
at System.Runtime.Serialization.SchemaExporter.ExportDataContract(DataContract dataContract)
at System.Runtime.Serialization.SchemaExporter.Export()
at System.Runtime.Serialization.XsdDataContractExporter.Export()
at System.Runtime.Serialization.XsdDataContractExporter.Export(Type type)
at System.ServiceModel.Description.MessageContractExporter.ExportType(Type type, String partName, String operationName, XmlSchemaType& xsdType)
at System.ServiceModel.Description.DataContractSerializerMessageContractExporter.ExportBody(Int32 messageIndex, Object state)
at System.ServiceModel.Description.MessageContractExporter.ExportMessage(Int32 messageIndex, Object state)
....
...
....
Custom datatable
Public Class MyDataTable
Inherits DataTable
Public Sub New()
End Sub
End Class
My WCF Interface
<ServiceContract()> _
Public Interface IService1
<OperationContract()> _
Function TestDatatable(ByVal MyDT As MyDataTable) As Boolean
End Interface
Implementation
Public Class Service1
Implements IService1
Public Function TestDatatable(ByVal MyDT As MyClassLibrary.MyDataTable) As Boolean Implements IService1.TestDatatable
Return MyDT.TableName = "MyTable"
End Function
End Class
Note: When just using datatable its ok but not my custom datatable.
What need to be added do I can serialize my custom datatable.
I able to serialize my other object but not this class.
Instead of manually deriving from DataTable I'd create a typed data set using Visual Studio's data set designer. They are much easier to work with.
Implementation of GetSchema method in DataTable looks like
protected virtual XmlSchema GetSchema()
{
if (this.GetType() == typeof (DataTable))
return (XmlSchema) null;
So that you may override this method in the following way
protected override XmlSchema GetSchema()
{
return null;
}
This will resolve serialization issue.