[ServiceContract]
public interface ISecurities<T> : IPolicyProvider where T: EntityObject
{
[OperationContract(Name="GetAllSecurities")]
IEnumerable<T> GetSecurities();
[OperationContract]
IEnumerable<T> GetSecurities<T1>(List<T1> lstIdentifiers) where T1 : FI_CusipMaster;
[OperationContract]
T GetSecurity<T1>(T1 lstIdentifiers) where T1 : FI_CusipMaster;
}
//Host
///CADIS Contract
ServiceHost dmHost = new System.ServiceModel.ServiceHost(typeof(GlobalInvestors.FIPA.BLL.UDI.CADISSecurities));
Uri baseAddress = dmHost.BaseAddresses[0];
Uri policyAddress = new Uri(baseAddress.AbsoluteUri.Replace(baseAddress.AbsolutePath, ""));
dmHost.AddServiceEndpoint(
typeof(GlobalInvestors.FIPA.BLL.IPolicyProvider),
new System.ServiceModel.WebHttpBinding(),
policyAddress).Behaviors.Add(new System.ServiceModel.Description.WebHttpBehavior());
dmHost.Open();
//App.Config
<service behaviorConfiguration="UDIBehaviour" name="GlobalInvestors.FIPA.BLL.UDI.CADISSecurities">
<endpoint binding="basicHttpBinding" contract="GlobalInvestors.FIPA.BLL.UDI.ICADISSecurities" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:1667/CADIS" />
</baseAddresses>
</host>
</service>
<behavior name="UDIBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
[ServiceContract]
[ServiceKnownType(typeof(SecurityMasterAdapter))]
public interface ICADISSecurities :ISecurities<SecurityMasterAdapter>
{
}
I get "InvalidDataContractException Type 'System.Collections.Generic.List`1[T1]' cannot be exported as a schema type because it is an open generic type. You can only export a generic type if all its generic parameter types are actual types." if I host this contract.
I have read that it is good to avoid generics in ServiceContract. but is it possible to use T?
Your problem in this case is not T in ServiceContract but T1 used as DataContract. You can use T in service contract if you replace T with specific type during service contract implementation. For data contracts (operation parameters and return types) you can't use T at all. You always have to specify concrete type. Your service contract can be rewritten with usage of ServiceKnownTypeAttribute so that T1 is replaced with FI_CusipMaster and ServiceKnownType specifies all possible classes derived from FI_CusipMaster.
Edit: Another way is not to use ServiceKnownType and use KnownTypeAttribute which has to be defined on FI_CusipMaster type.
Best regards, Ladislav
As the error says, no you cannot use T. Service contracts need to be able to write out serialization information that deals with definitive types. It can't handle open generics in the exported functions
In your example T is a generic type. You cannot use a generic type in a ServiceContract unless it is used with a defined type parameter- as in class Foo : List<int> { }.
Related
Getting an error "System.InvalidOperationException: 'Attempted to get contract type for ITC2DataService,
but that type is not a ServiceContract, nor does it inherit a ServiceContract.".
Note : I have already decorated the interface "ITC2DataService" with "ServiceContract" attribute. But still error occurs.
In addition to adding ServiceContract to ITC2DataService, the service class also inherits derived contracts, such as the following :
public class Service1 : ITC2DataService
{
...
}
The corresponding endpoint binding in the Web.config file needs to specify the correct contract (like the endpoint address "ws" below).
<services>
<service name="WcfService.Service1" behaviorConfiguration="WcfService.Service1Behavior">
<!-- ITestCallback needs to be the contract specified -->
<endpoint address="ws" binding="wsHttpBinding" contract="WcfService.ITestCallback">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
I am referencing System::ServiceModel in my C++/CLI dll project (VS2012 Express). The following code fails with the following error and I can't find how to fix it.
error C2337: 'ServiceMetadataBehavior' : attribute not found
[System::ServiceModel::ServiceContractAttribute]
[System::ServiceModel::Description::ServiceMetadataBehavior]
public ref class PlaybackManager
{
public:
~PlaybackManager() { this->!PlaybackManager(); }
!PlaybackManager() { }
// Playback action methods
[System::ServiceModel::OperationContractAttribute]
void Play();
[System::ServiceModel::OperationContractAttribute]
void Stop();
[System::ServiceModel::OperationContractAttribute]
void Pause();
[System::ServiceModel::OperationContractAttribute]
void Previous();
[System::ServiceModel::OperationContractAttribute]
void Next();
[System::ServiceModel::OperationContractAttribute]
void Random();
};
EDIT1:
The caveat to this is that it is not possible to write a wcf service entirely with code, i.e without an app.config file. While the Service has the ServiceMetadataBehavior helper to create a metadata exchange behavior implementation, there is no such thing for the Endpoint. Is this "by design"?
How to: Publish Metadata for a Service Using Code
EDIT2:
OK, so the caveat above does not seem to be, necessarily, correct. Below is the app.config representing what I am trying to do in code and I get the same error if I remove the ServiceMetatdataBehavior attribute to the endpoint class implementation.
<configuration>
<system.serviceModel>
<services>
<service name="Engine.PlaybackManager">
<endpoint
address="net.tcp://localhost:7008/PlaybackManager"
binding="mexTcpBinding"
contract="IMetadataExchange"
/>
<endpoint
address="net.tcp://localhost:7008/PlaybackManager"
binding="netTcpBinding"
contract="Engine.PlaybackManager"
/>
</service>
</services>
</system.serviceModel>
</configuration>
The error is:
The contract name 'IMetadataExchange' could not be found in the list
of contracts implemented by the service PlaybackManager. Add a
ServiceMetadataBehavior to the configuration file or to the
ServiceHost directly to enable support for this contract.
The problem is, if I add the ServiceMetadataBehavior attribute to the PlaybackManager class I get the original error above, that it is not recognized. Any ideas?
I understand why nobody responded to this, "where do I begin" was the only possible response. So, in case someone falls on this with an equal amount of confusion that I had, here are some tips:
My main issue was with the mapping the xml config nomenclature (found in most examples online) with the code equivalents:
<services> maps to System::ServiceModel::ServiceHost
<behaviors> maps to "your instance of ServiceHost"->Description->Behaviors
<behavior> is type specific, the type being a nested element in the xml, thus:
<behavior> <serviceMetadata /> </behavior> maps to ServiceMetadataBehavior
<endpoint> maps to ServiceEndpoint
and finally:
the mex endpoint (the one with the ServiceMetadataBehavior added) needs it's own namespace, so add "/mex" to the end of your implementation endpoint uri address.
example:
implementation address = "net.tcp://localhost:5000/Engine"
mex address = "net.tcp://localhost:5000/Engine/mex"
Obviously these tips are not an explanation but I hope they might help someone as confused as I was when I asked the question.
I have a Generic Service Interface:
[ServiceContract]
public interface IService<T> where T : Class1
{
[OperationContract]
void DoWork(T class1);
}
Then I have a Concrete Service that inherits from that:
public class Service : IService<Class1>
{
public void DoWork(Class1 class1)
{
}
}
Everything works fine until I add a webHttpEndpoint to expose a JSON WS:
<service name="Service">
<endpoint
address=""
binding="webHttpBinding"
behaviorConfiguration="webHttpBehavior"
contract="IService" />
</service>
<behavior name="webHttpBehavior">
<enableWebScript />
</behavior>
In fact, I receive this error:
The contract name 'IService' could not
be found in the list of contracts
implemented by the service 'Service'.
That's beacuse of the generic definition of the interface.
Any solution?
In my opinion (and based on what you said), the interface does not need to be generic. The caller just need to know that there is a DoWork operation.
So basically, change the concrete class to be generic instead of the interface.
public class Service<T> : IService where T : Class1
{
public void DoWork()
{
}
}
EDIT after clarifying the question: You need to provide the generic parameter in the config file as well:
contract="YourAssembly.IService`1[[YourAssembly.Class1, YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"
Here is a similar question: Inheriting from a generic contract in WCF
You must correctly write the type name of your contract into configuration. IService is not the name of IService<Class1>!!!
The configuration should look like:
<service name="Service">
<endpoint
address=""
binding="webHttpBinding"
behaviorConfiguration="webHttpBehavior"
contract="IService`1[Class1]" />
</service>
<behavior name="webHttpBehavior">
<enableWebScript />
</behavior>
Be aware that if your contract or service lives in any namespace, namespaces should be included in configuration.
Or in case of full names needed:
<service name="Namespace.Service, AssemblyName">
<endpoint
address=""
binding="webHttpBinding"
behaviorConfiguration="webHttpBehavior"
contract="Namespace.IService`1[[Namespace.Class1, AssemblyName]], AssemblyName" />
</service>
<behavior name="webHttpBehavior">
<enableWebScript />
</behavior>
Where is your Class1.....Instead of specifying any specific class make it a reference type using class
Try something this...
[ServiceContract]public interface IService<T> where T : class
{
[OperationContract]
void DoWork();
}
In a solution, I added a "WCF Service Library". No problem with the default method. I added one :
In the interface :
[ServiceContract]
public interface ISecurityAccessService
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
[OperationContract]
CompositeUser ListUser();
}
[DataContract]
public class CompositeUser
{
List<User> _listUser = new List<User>();
[DataMember]
public List<User> ListUser
{
get { return _listUser; }
set { _listUser = value; }
}
}
The interface implementation, the dataaccess iw working, I tested the DataService and no problem.
public class SecurityAccessService : ISecurityAccessService
{
public CompositeUser ListUser()
{
DataAccess.DataService service = new DataAccess.DataService();
CompositeUser compositeUser = new CompositeUser();
compositeUser.ListUser = service.ListUser();
return compositeUser;
}
}
When I execute and try to invoke, I receive this error message :
*An error occurred while receiving the HTTP response to http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary/ISecurityAccessService/. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.*
The App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<services>
<service name="WcfServiceLibrary.SecurityAccessService">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary/ISecurityAccessService/" />
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address ="" binding="wsHttpBinding" contract="WcfServiceLibrary.ISecurityAccessService">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<!-- Metadata Endpoints -->
<!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
<!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="True"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Update 1
I made a working sample with database access. I just don't understand something in the "PersonService" class, why I have to make this loop. Solution is welcome.
Download 40ko .rar full example
your User class needs to be marked with the DataContract attribute and its methods with the DataMember attribute. It may also need to be marked as a KnownType in the CompositeUser class so that it is included in the types for the service. You can do that like so:
[DataContract]
[KnownType(typeof(User))]
public class CompositeUser
{
...
}
you'll be able to tell what the issue is from the logs. Either you'll get a 'cannot be serialized' message, in which case you need to add the [DataContract] attribute or it will be 'type was not expected' in which case you'll also need to add the [KnownType] attribute
If you enable tracing in your service you'll be able to get more details of what the problem was. Add something like this in the config file:
<configuration>
<system.diagnostics>
<trace autoflush="true"/>
<sources>
<source name="System.ServiceModel" switchValue="Verbose">
<listeners>
<add name="sdt" type="System.Diagnostics.XmlWriterTraceListener" initializeData="D:\wcfLog.svcLog"/>
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
also setting <serviceDebug includeExceptionDetailInFaults="True" />
will allow more detail about the error to be returned in the service exception which might also help.
EDIT
From the comments below it seems the User class is a Linq to SQL generated class. I don't think you should be sending this class across the wire. WCF deals with messages not in serializing types with behaviour, so you should create a DTO which represents the data in your User class that will be needed on the client and send this DTO out from the service contract. Even if you do send the User class as it is, when it gets to the client it won't have the context to still be connected to the DB.
I faced this problem again today. A long time ago I had the same problem, but I had forgotten the cause and it took me some time to sort it out toady.
In my case, it was a looping serialization problem. One table has a column which is a foreign key to another column in the same table. So all I had to do was to click the work surface of the dbml file and change the Serialization Mode to Unidirectional.
If yours is a Linq to Sql situation, and the error message is the one shown above, you might want to check whether it is the same cause as mine.
I have two classes implementing a contract at a service which are consumed from a factory on the client like shown below.
[ServiceContract]
public interface MyInterface {
void DoSomething()
}
public class A : MyInterface {
public void DoSomething(){
"Hi I'm A"
}
}
public class B : MyInterface {
public void DoSomething(){
"Hi I'm B"
}
}
public class MyFactory <TMyInterface> {
void DoSomething(){
TMyInterface.DoSomething()
}
}
The client must remain the same. My question is how can I choose at the server side which implementation of MyInterface to use, by passing the type parameter using .config file in WCF
I read other post but I don't understand yet :(
It is possible to do it, and there are a few ways to do it.
One possibility is to create a "routing" service which will contain the "public" address which the client always talks to. This routing service can then, based on some configuration, redirect the call to the appropriate "real" service.
Another way is to actually have a process which starts both services, but their addresses are defined in config. If you use the same binding and the same contract (which is the case), then you can "flip-flop" the service address when you want to change the service which will receive the calls from the client. For example, this configuration directs the requests to the endpoint at "http://machine-name:8000/Service" to service A. Notice that, since you define service hosts for both services, you actually need to have a base address for service B as well - in this case I used named pipes, which cannot be accessed via different machines.
<system.serviceModel>
<services>
<service name="A">
<host>
<baseAddresses>
<add baseAddress="http://machine-name:8000/Service"/>
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding" contract="MyInterface" />
</service>
<service name="B">
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/ServiceBackup"/>
</baseAddresses>
</host>
<endpoint address="" binding="netNamedPipeBinding" contract="MyInterface" />
</service>
</services>
</system.serviceModel>
when you want to change the address for B, you'd then swap the addresses.
<system.serviceModel>
<services>
<service name="A">
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/ServiceBackup"/>
</baseAddresses>
</host>
<endpoint address="" binding="netNamedPipeBinding" contract="MyInterface" />
</service>
<service name="B">
<host>
<baseAddresses>
<add baseAddress="http://machine-name:8000/Service"/>
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding" contract="MyInterface" />
</service>
</services>
</system.serviceModel>
The hosting program would look like this:
public static void HostServices()
{
ServiceHost hostA = new ServiceHost(typeof(A));
ServiceHost hostB = new ServiceHost(typeof(B));
hostA.Open();
hostB.Open();
Console.WriteLine("Press ENTER to close");
Console.ReadLine();
hostA.Close();
hostB.Close();
}
Now, if your services are hosted in IIS (webhost), then it's a little harder. Since the "normal" activation requires a .svc file to be part of the endpoint address, and each .svc file is associated with a single class, the address for A would be something like http://machine-name/services/a.svc while the address for B would be something like http://machine-name/services/b.svc. So what you'd need to do in this case is to create a custom ServiceHostFactory, and use the ASP.NET Routes integration to create a .svc-less URL for your service. Then you'd use something similar to the previous example to decide which service will be activated.