WCF: recursive calls to service - c#

I have a WCF service.
[ServiceContract(ConfigurationName="WebEntityService")]
[ServiceKnownType(typeof(WcfEntityService.TH.Category))]
public interface IEntityService {
[OperationContract(IsOneWay=true)]
void ScanEntity(ScanInfo scanInfo, WebEntityBase entity);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class EntityService : IEntityService {
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void ScanEntity(ScanInfo scanInfo, WebEntityBase entity) {
try {
WebScanner webScanner = new WebScanner(entity);
webScanner.Scan(scanInfo);
}
catch (Exception) {
throw;
}
}
}
The WcfEntityService.TH.Category class is inherited from WebEntityBase and placed in a different assembly. This class has the service reference to the WCF service and it calls ScanEntity recursively.
The problem is following.
When the WcfEntityService.TH.Category class calls ScanEntity I occur a serialization error.
Type 'WcfEntityService.TH.Category' with data contract name 'Category:http://schemas.datacontract.org/2004/07/WcfEntityService.TH' 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.
Can anybody say there is any way to solve this issue?

Related

How to properly set attributes to both WCF interface and class in VS2019?

I'm trying to upgrade an existing WCF service from VS2015 to VS2019, but there seems a problem with interface/class service attributes in VS2019.
I've created the following short code example to demonstrate the problem here:
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace ConsoleApp25
{
[ServiceContract]
public interface ISOAAdd
{
[OperationContract]
double Add(double a, double b);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class SOAAddImpl: ISOAAdd
{
public double Add(double a, double b)
{
return a + b;
}
}
class Program
{
private static ServiceHost Host;
private const ushort Port = 23456;
static void Main(string[] args)
{
Type ServiceType = typeof(SOAAddImpl);
Host = new ServiceHost(ServiceType, new Uri[] { new Uri(string.Format("http://localhost:{0}/", Port)) });
Host.AddServiceEndpoint(ServiceType, new BasicHttpBinding(), "SOAAddImpl");
ServiceMetadataBehavior Behavior = new ServiceMetadataBehavior();
Behavior.HttpGetEnabled = true;
Host.Description.Behaviors.Add(Behavior);
Host.Open();
Console.ReadKey();
}
}
}
When this code is run, I get the following exception:
The contract type ConsoleApp25.SOAAddImpl is not attributed with ServiceContractAttribute. In order to define a valid contract, the specified type (either contract interface or service class) must be attributed with ServiceContractAttribute.
If I add the attribute to the class, I get the following exception:
The service class of type ConsoleApp25.SOAAddImpl both defines a
ServiceContract and inherits a ServiceContract from type
ConsoleApp25.ISOAAdd. Contract inheritance can only be used among
interface types. If a class is marked with ServiceContractAttribute,
it must be the only type in the hierarchy with
ServiceContractAttribute. Consider moving the ServiceContractAttribute
on type ConsoleApp25.ISOAAdd to a separate interface that type
ConsoleApp25.ISOAAdd implements.
I. e. for some reason the app cannot see that the interface is already decorated with ServiceContract, but when I decorate both the class and the interface it CAN see the decoration (both), which results in the error above. How do I solve this?
As Selvin pointed in the comment section, you need to send parameter service interface(typeof(ISOAAdd)) instead of service object(typeof(SOAAddImpl)) to ServiceHost.AddServiceEndpoint method.
Host.AddServiceEndpoint(typeof(ISOAAdd), new BasicHttpBinding(), "SOAAddImpl");

How to initialize abstract base constructor when client calls new on DataContract?

I'm trying to come up with a workaround that would accommodate an abstract base class's constructor being initialized when a client consuming my WCF service performs a new() over a DataContract object. I'm aware that the DataContract objects are created as raw, uninitialized objects thus no constructors are called. I ran across the user of the [OnSerializing], [OnSerialized], [OnDeserializing], and [OnDeserialized] attributes, and I've discovered that they are not honored by the serialization engine of WCF unless you explicitly force it to use XML, which is not desired in this specific case. Here's a very simplified coding example of what I'm trying to use.
[DataContract(Namespace = "http://somenamespace/Data/ContractBase/v1")]
public abstract ContractBase
{
[DataMember(IsRequired = true)]
public SomeDataContract BaseClassObject { get; set; }
public string Name { get; set; }
public ContractBase()
{
BaseClassObject = new SomeDataContract("randomConstructorArgument");
Name = "Ezra";
}
}
[DataContract(Namespace = "http://somenamespace/Data/TheClass/v1")]
[KnownType(typeof(ContractBase))]
public sealed class TheClass : ContractBase
{
[DataMember]
public PetDataContract MyPet { get; set; }
[DataMember]
public int SomeIntProperty { get; set; }
public TheClass()
: base()
{
MyPet = new PetDataContract ("Fido");
SomeIntProperty = -1;
}
}
I'm aware that the client performing TheClass myClass = new TheClass(); will not initialize the base constructor since the constructor of TheClass is never called. I attempted to add in methods such as the following to trigger when serialization occurs without any success.
private void Initialize()
{
MyPet = new PetDataContract ("Fido");
SomeIntProperty = -1;
base.Initialize();
}
[OnSerializing]
private void OnSerializing(StreamingContext c)
{
Initialize();
}
The base class would have the Initialize method as well so that the "constructors" would be chained. The constructors themselves would be updated to include the Initialize(); call to use the same common source of code.
Is there a way to handle this without forcing the serialization to be done through the XmlSerializer? My current workaround is to provide a method in the WCF service to create the object on the server and return the post-constructor version.
public TheClass CreateTheClass(TheClass contract)
{
// Calls the constructor of TheClass and its base constructor.
return new TheClass();
}
This does work as expected, but it's an extra service call that I'd rather avoid because of the network I/O cost. Any help would be extremely appreciated.
Thanks!
According to this article the attributes you mentioned should work nicely with DataContractSerializer. Your last example is a little bit strange - you are trying to use OnSerializing attribute while saying that constructors are not called during deserialization by WCF.
I would suggest to use your approach with Initialize methods marked by OnDeserializing (or OnDeserialized if you wish to call your code after deserialization was completed) attribute.

WCF service throws "The type [...] was not expected. Use the XmlInclude [...]" even if XmlInclude is already being used

I have a WCF service (started from code) that uses an object not defined in the service definition. Because of that I have to use the [XmlInclude] attribute to make WCF understand how to serialize it.
For some reason this doesn't work and WCF still complains (I found the exception using tracing) that I have to use [XmlInclude] for the type already defined.
What am I missing here?
Code to start WCF service
ServiceHost host = new ServiceHost(typeof(MyService), "http://localhost/myservice");
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy12;
host.Description.Behaviors.Add(smb);
host.Open();
Service implementation
[WebService(Namespace = "http://services.mysite.com/MyService")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class MyService : WebService, IMyService {
[WebMethod]
[XmlInclude(typeof(InnerObject))]
public MyReturnObject Test() {
return new MyReturnObject(new InnerObject());
}
}
Service definition / interface
[ServiceContract]
public interface IMyService {
[OperationContract, XmlSerializerFormat(Style = OperationFormatStyle.Document)]
MyReturnObject Test();
}
Return types
The MyReturnObject class contains a generic object that can contain whatever I want. For this example I include a InnerObject type as defined above, and the type definitions look like so.
[KnownType(typeof(InnerObject))]
public class MyReturnObject {
public object Content { get; set; }
public MyReturnObject(object content) {
Content = content;
}
}
public class InnerObject {
public int Foo;
public string Bar;
// And some other properties
}
Complete exception
System.InvalidOperationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
The type InnerObject was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
You should use [ServiceKnownType] on your wcf service

Error when calling Polymorphic method on WCF Client

This error comes as result of This previous question. I'm trying to call a polymorphic method from WCF client. This is my contract:
public interface IOhmioService
{
[OperationContract]
IEnumerable<Enumerador> GetEnumerador<T>() where T : IEnumerador, new();
}
This is my class implementation:
public class OhmioService : IOhmioService
{
public IEnumerable<Enumerador> GetEnumerador<T>() where T : IEnumerador, new()
{
T _obj = new T();
return _obj.Enumerar();
}
}
And call it from client like this:
public IEnumerable<Enumerador> Clients { get; set; }
Clients = this.serviceClient.GetEnumerador<Clientes>();
If i call this method from within the class everything works fine. But if i call it from WCF client a get this error:
The non generic Method 'Ohmio.Client.OhmioService.OhmioServiceClient.GetEnumerador()'
cannot be used with types arguments
What i'm i doing wrong? Thanks!
UPDATE
Ok. I try suggested solution, and get this Horrible error:
Type 'System.RuntimeType' wasn't spected with the contract name RuntimeType:http://schemas.datacontract.org/2004/07/System'. Trye to use DataContractResolver or add the unknown types staticaly to the list of known types (for instance, using the attribute KnownTypeAttribute or adding them to the list of known types to pass to DataContractSerializer)
Maybe using generic types over wcf is not such a good idea after all. I was trying to reduce repetitive code on the WCF service.
You cannot have generic OperationContract methods in a WCF ServiceContract. See here for further details: WCF. Service generic methods
You need to pass the type as a method parameter:
public interface IOhmioService
{
[OperationContract]
IEnumerable<Enumerador> GetEnumerador(string typeName);
}
public class OhmioService : IOhmioService
{
public IEnumerable<Enumerador> GetEnumerador(string typeName)
{
var type = Type.GetType(typeName);
var _obj = (IEnumerador)Activator.CreateInstance(type);
return _obj.Enumerar();
}
}
UPDATE
See update above; pass the fully qualified name of the type. That won't cause a serialization issue.

Interface(s) inheriting other interface(s) in WCF services

In my solution there's a few WCF services, each of them implementing it's own callback interface. Let's say they are called: Subscribe1, with ISubscribe1 and ICallback1, etc.
It happens there are a few methods shared among ICallbacks, so I made a following interface:
interface ICallback
{
[OperationContract]
void CommonlyUsedMethod();
}
and i inherited it in all: ICallback1 : ICallback, ICallback2 : ICallback, etc. And deleted the CommonlyUsedMethod() from all callback interfaces.
Now, on the service-side code, everything compiles fine and services can start working as usual. But, when I updated the service references for the client, CommonlyUsedMethod() dissapeared from the reference.cs file (the ISubscribeCallback part), and could no longer be used to send data to back to the client.
try to set the ServiceContract attribute on the base interface also.
Ok, this is the exact code, I condensed it as much as I can. Just start a new console application and copy/paste it. Start it and add a service reference to it. CommonlyUsedMethod() is not present in the reference, while the other methods are. Could it be framework 4?
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace TestService
{
class Program
{
static void Main()
{
var serviceHost=new ServiceHost(typeof(Subscribe1), new Uri("net.tcp://localhost:8888"));
serviceHost.Description.Behaviors.Add(new ServiceMetadataBehavior());
serviceHost.AddServiceEndpoint(typeof(ISubscribe1), new NetTcpBinding(SecurityMode.None), string.Empty);
serviceHost.AddServiceEndpoint("IMetadataExchange", MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
serviceHost.Open();
Console.WriteLine("Working!");
while(Console.ReadKey(true).Key!=ConsoleKey.Escape) { }
}
}
[ServiceContract]
interface ICallbackBase
{
[OperationContract]
void CommonlyUsedMethod();
}
[ServiceContract]
interface ICallback1 : ICallbackBase
{
[OperationContract]
void SpecificMethod();
}
[ServiceContract(CallbackContract=typeof(ICallback1))]
interface ISubscribe1
{
[OperationContract]
void TestMethod();
}
[ServiceBehavior]
class Subscribe1 : ISubscribe1
{
[OperationBehavior]
public void TestMethod()
{
}
}
}
Does this reflect what you have in your code?
[ServiceContract]
public interface ICallbackBase
{
[OperationContract]
void CommonlyUsedMethod();
}
[ServiceContract]
public interface ICallback1 : ICallbackBase
{
[OperationContract]
void SpecificMethod();
}
This is essentially the structure I have in my production solution, and then I use the proxies that are generated when I add a Service Reference to access my methods from the client.
For reference, the generated interface then looks like this:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="MyNamespace.ICallback1")]
public interface ICallback1 {
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ICallbackBase/CommonlyUsedMethod", ReplyAction="http://tempuri.org/ICallbackBase/CommonlyUsedMethodResponse")]
void CommonlyUsedMethod();
}
Note the "ICallbackBase" in the OperationContractAttribute - it really does know where the method came from.
I'm not sure that what you are trying to do using WCF is possible. When you use inheritance in WCF you need to apply the KnownType attribute to the DataContract so that the DataContractSerializer will know to serialize it and make it available on the other end. Since you cannot put the KnownType attribute on interfaces, there is no way to tell the serializer that this is needed on the other end. Thus is does not show up when you implement it on the client.

Categories

Resources