I have several WCF services hosted in one console application. All of them are configured in code to use NetTcpBinding with binding.TransferMode = TransferMode.Streamed
Message contracts are used to define their operations (see code below for details)
RequestMsgContract1,
ResponseMsgContract1,
ResponseMsgContract2
For some mysterious reason I can not create a service reference for a service that uses
ResponseMsgContract1 and ResponceMsgContract2 message contracts simultaneously (see IMyService1 defenition below). The message I get is
The URI prefix is not recognized.
Metadata contains a reference that cannot be resolved: 'net.tcp://localhost:8890/MyService1/mex'.
Metadata contains a reference that cannot be resolved: 'net.tcp://localhost:8890/MyService1/mex'.
If the service is defined in the current solution, try building the solution and adding the service reference again.
Service references for two other services that use only RequestMsgContract1 and ResponseMsgContract2 (see IMyService2) or only RequestMsgContract1, ResponseMsgContract1 (see IMyService3) are created without any problems.
My question is What's wrong with my message contracts or where else should I look to find some clue?
I didn't paste my service configuration code here (as I said I do not use xml config file) because it works Ok for two of three services. I do not think the reason of error is there but you can find full code of service host console application here http://pastebin.com/1THhc9mU
// Can't create service reference for this service
[ServiceContract]
interface IMyService1
{
[OperationContract]
ResponseMsgContract1 Operation1(RequestMsgContract1 arguments);
[OperationContract]
ResponceMsgContract2 Operation2();
}
// No problems with service reference creation for this one
[ServiceContract]
interface IMyService2
{
[OperationContract]
ResponceMsgContract2 Operation1();
[OperationContract]
ResponceMsgContract2 Operation2(RequestMsgContract1 arguments);
}
// No problems with service reference creation for this one
[ServiceContract]
interface IMyService3
{
[OperationContract]
ResponseMsgContract1 Operation1();
[OperationContract]
ResponseMsgContract1 Operation2(RequestMsgContract1 arguments);
}
They use these three message contracts
[Serializable]
[MessageContract]
public class RequestMsgContract1
{
[MessageHeader(MustUnderstand = true)]
public Guid arg1;
}
[Serializable]
[MessageContract]
public class ResponseMsgContract1 : IDisposable
{
[MessageHeader(MustUnderstand = true)]
public long Length;
[MessageBodyMember(Order = 1)]
public System.IO.Stream stream;
public void Dispose()
{
if (stream != null)
{
stream.Close();
stream = null;
}
}
}
[Serializable]
[MessageContract]
public class ResponceMsgContract2 : IDisposable
{
[MessageHeader(MustUnderstand = true)]
public int Length { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Str1 { get; set; }
[MessageBodyMember(Order = 1)]
public System.IO.Stream stream { get; set; }
public void Dispose()
{
if (stream != null)
{
stream.Close();
stream = null;
}
}
}
EDIT:
In case it is important to reproduce the problem here are my Visual Studio, .Net Framework and OS versions
Visual Studio 2012 (11.0.61030.00 Update 4)
.Net Framework Version 4.5.50709
Windows 8 Pro
I do not understand why the error I described in question happens but
no one has provided a proper answer yet so I'll tell about workarounds I came up with. Maybe it will help somebody.
I found two ways to make Visual Studio create Service References I need while preserving all the operations I want.
Error disappears if I rename ResponceMsgContract2.Length to
ResponceMsgContract2.Length2 so that ResponceMsgContract2 and
ResponseMsgContract1 do not have message headers with same name.
Why this helps is still a mystery to me. Maybe it's WCF bug.
Error disappears if I split IMyService1 to two interfaces:
[ServiceContract]
interface IMyService1_1
{
[OperationContract]
ResponseMsgContract1 Operation1(RequestMsgContract1 arguments);
}
[ServiceContract]
interface IMyService1_2
{
[OperationContract]
ResponceMsgContract2 Operation2();
}
Both variants are not good solutions and there may be situations when you can't apply any of them. But at least it's something.
Related
I have a very strange and obscure issue with WCF services that I was hoping to get some insight on:
I am working a WCF service that we are building to replace one that we no longer have source code for. For some reason, in the new WCF service, everything is forced through a single paramater called "request". Using the WCF test client, this is what it looks like
On the "correct" service, this is what it looks like:
Is there any reason why this would be happening? I've defined all of the requests as follows:
[ServiceContract]
public interface IMyService
{
[OperationContract]
string SomeRequest();
}
Which seems correct, but there may be something I've overlooked that is causing this.
In your original WCF service, there is a request function parameter, and it has a definition similar to the following:
[ServiceContract]
public interface IMyService
{
[OperationContract]
Request SomeRequest(Request request);
}
[DataContract]
public class Request
{
string documentId;
[DataMember]
public string DocumentId
{
get { return documentId; }
set { documentId = value; }
}
}
In the new wcf service:
[ServiceContract]
public interface IMyService
{
[OperationContract]
string SomeRequest(string documentId);
}
So this is because the function parameters are different. Originally your parameter was class, but later changed to string, so the display in WCFTestClient is different.
I have a WCF service and a client and I want both to share the same class library so they both have access to the same types. My issue is that one of the classes is a MessageContract because it is an object that is used to upload files to the server via streaming. The class is as follows:
[MessageContract]
public class RemoteFileInfo : IDisposable
{
private string fileName;
private long length;
private System.IO.Stream fileByteStream;
public string FileName
{
set { this.fileName = value; }
get { return this.fileName; }
}
public long Length
{
set { this.length = value; }
get { return this.length; }
}
[MessageBodyMember(Order = 1)]
public System.IO.Stream FileByteStream
{
set { this.fileByteStream = value; }
get { return this.fileByteStream; }
}
public void Dispose()
{
if (fileByteStream != null)
{
fileByteStream.Dispose();
fileByteStream = null;
}
}
}
This class is contained in a library that is shared between the server and the client. If I comment out the line that says [MessageContract] and then update the service reference, I am able to successfully share the type with the client and the service reference does not try to re-implement the type on its own. However, in order for streaming to work I need to make sure that this class is indeed a MessageContract so that the WCF service knows to only expect a single body member in the message and to deal with it appropriately.
If I uncomment the line that says [MessageContract] and update the service reference on the client side, it tries to re-implement RemoteFileInfo via the service reference instead of reusing the RemoteFileInfo that already exists in the library that both the service and the client are sharing. This means I end up with two of the same classes, MyClientProject.Shared.RemoteFileInfo and ServiceReference.RemoteFileInfo, which is ambiguous and causes the code to throw tons of errors.
I can get around it (sloppily) by commenting out the [MessageContract] line, updating the service reference, and then uncommenting the line on the service side before starting the service, so the client side thinks that it is just a normal class but the WCF service thinks its a MessageContract. This seems very silly to have to do and I am convinced theres a better way to do it. Any ideas?
Since you're already sharing all your data contracts, what's the point of not sharing your service contract interface as well, and simply avoid doing code generation at all? That would make far more sense.
I have a wcf application in which i have used Entitity Framework and have implemented dbContext for querying the database.
When I view the svc file in browser it exposes the operations.
I have interface class like this:
[ServiceContract]
public interface IService1
{
[OperationContract]
List<BooksModels> GetBooksList();
[OperationContract]
BooksModels GetBook(int id);
}
I have the implementation in the svc.cs file like this
public List<BooksModels> GetBooksList()
{
MVCEntity en = new MVCEntity();
return en.book.ToList();
}
public int GetBookId(int id)
{
//return db.book.Find(id);
return 1;
}
and the BooksModels class is like this
[DataContract]
public class BooksModels
{
[Key]
[DataMember]
public int BookId { get; set; }
[DataMember]
public string BookName{get;set;}
}
and have the config file the default one as created when creating wcf service application .
but when i invoke GetBooksList the service from MVC wcf client it gives me the following error:
Failed to invoke the service. Possible causes: The service is offline
or inaccessible; the client-side configuration does not match the
proxy; the existing proxy is invalid. Refer to the stack trace for
more detail. You can try to recover by starting a new proxy, restoring
to default configuration, or refreshing the service.
but when i invoke the second method that returns 1.
i examined that when the service uses the dbContext to return data it gives error and
is fine when not.
I have gone through various blogs and also the questions in stackoverflow but didn't help.
so how can this problem be addressed.
Thanks
I think it's a problem of serialization. Make a serialisation test from you're business layer that take List from entity framework and serialize then deserialize it and compare before and after serialization.
Use DataContractSerializer :
DataContractSerializer serialiser = new DataContractSerializer(typeof(List<BooksModels>));
List<BooksModels> expected = business.GetBooksList();
Stream stream = new MemoryStream();
serialiser.WriteObject(stream, expected);
stream.Position = 0;
List<BooksModels> actual = serialiser.ReadObject(stream) as List<BooksModels>;
Assert.IsNotNull(actual);
Assert.AreEqual(expected.Prop1, actual.Prop1);
Assert.AreEqual(expected.Prop2, actual.Prop2);
// ... //
If it doesn't work you probably use proxy in entity framework. turn off proxy :
context.ContextOptions.ProxyCreationEnabled = false;
I had the same problem. Thanks to Brice2Paris, I solved it by turning off the proxy of EF:
context.Configuration.ProxyCreationEnabled = false;
I have the following WCF interface that is exposed via net.tcp:
[ServiceContract]
public interface IMyWCFService
{
[OperationContract]
Response ProcessRequest(Request request);
}
This is driven by the following classes (much simplified for the purposes of this question):
[Serializable]
public abstract class Message
{
[XmlAttribute]
public string Sender { get; set; }
[XmlAttribute]
public string Recevier { get; set; }
}
[Serializable]
public abstract class Response : Message
{
[XmlAttribute]
public int EventCode { get; set; }
}
[Serializable]
public abstract class Request : Message
{
[XmlAttribute]
public string SourceSystem { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringRequest : Request
{
[XmlElement]
public string Payload { get; set; }
}
[XmlRoot(Namespace="http://blah.blah.com/blah/")]
public class StringResponse : Response
{
[XmlElement]
public string Payload { get; set; }
}
Note : We use XMLSerializer rather than DataContractSerializer as these classes have to be compatible with legacy systems that are .NET 2 based.
As the interface uses the abstract Request/Response classes in the ProcessRequest method we have to declare StringResponse / StringRequest as ServiceKnownType, for example:
[ServiceContract]
[ServiceKnownType(typeof(StringRequest))]
[ServiceKnownType(typeof(StringResponse))]
public interface IMyWCFService
{
[OperationContract]
ResponseMessage ProcessRequest(RequestMessage request);
}
This works perfectly and all is good in the world, however.....
The WCF listener is just one component of a much larger framework and the classes described above are used throughout. We have also designed the framework to allow us to add new types of Request/Response messages with relative ease. For example I might add:
public class CustomRequest : Request
{
public MyCustomXmlSerialisableRequestObject Payload { get; set; }
}
public class CustomResponse: Response
{
public MyCustomXmlSerialisableResponseObject Payload { get; set; }
}
Which also works fine until I get the the WCF service interface. When we add a new custom request/response pair we also need to update the ServiceKnownType on the interface to include them. Which then means I have to redeploy the service. So the question is - is there any way I can avoid having to update the interface?
As an example when we used remoting we could pass through any objects we liked as long as they were serialisable so I assume/hope that there is a similar solution in WCF.
EDIT : Update
Following the guidance found here:
http://ashgeek.blogspot.com/2011/02/wcf-serialization-dynamically-add.html
I seem to be on the right track. However when I update the client service reference it pulls in all the dynamically types into the service reference. Which is undesirable as not all clients need to, or should, know about all messages that derive from Request/Response
More importantly I seem to lose the the ServiceClient class that is used to push messages, e.g:
// Client proxy class goes AWOL after service reference update
var client = new MyServiceReference.Client();
var responseMessage = client.ProcessRequest(requestMessage)
At the beginning you are mentioning that you need compatibility with .NET 2.0 services but in the same time you are complaining that something which worked in .NET remoting doesn't work in WCF - you are limited by features possible with .NET 2.0 web services where both server and client must know about transferred types on the service layer = types must be in service description and WSDL. Moreover because you decided to use XmlSerializer you generally lost most of the ways how to achieve that:
ServiceKnowType can load known types from static method
KnownTypes defined in configuration (requires DataContractSerializer)
DataContractResolver (only WCF 4) and loading all derived types on startup (requires DataContractSerializer)
Passing .NET type information in messages (requires NetDataContractSerializer and custom behavior) = generally this is the same functionality as in remoting and it demands sharing types between service and client and both service and client must be .NET application using WCF stuff.
With XmlSerializer you have one option
Return XElement and receive XElement in your service operation and deal with XML by yourselves - doesn't work in .NET 2.0
Edit:
There is no dynamic behavior in service description. It is created only once when the host starts and after that doesn't change until you restart the host. If you need subset of WSDL per client you need separate endpoint for each client and you must define exactly which data contracts should be exposed on each endpoint.
recently have been successful getting my IIS hosted WCF service to work with basic authentication.
Since successfully implementing that. I have noticed that property values are not remembered.
Here is some code:
[ServiceContract]
public interface IEcho
{
string Message { [OperationContract]get; [OperationContract]set; }
[OperationContract]
string SendEcho();
}
public class EchoProxy : IEcho
{
public string Message { get; set; }
public string SendEcho()
{
return string.Concat("You said: ", Message);
}
}
public class EchoService : System.ServiceModel.ClientBase<IEcho>, IEcho
{
//-- ..... CONSTRUCTORS OMITTED ....
public string Message
{
get { return base.Channel.Message; }
set { base.Channel.Message = value; }
}
public string SendEcho()
{
return base.Channel.SendEcho();
}
}
Here is the console and the result:
EchoService client = new EchoService("SecureEndpoint");
client.ClientCredentials.UserName.UserName = "test";
client.ClientCredentials.UserName.Password = "P#ssword1";
client.Message = "Hello World";
Console.WriteLine(client.SendEcho());
Expected Result: You said: Hello World
Actual Result: You said:
I have Uploaded the sandbox project to my skydrive. I have included a SETUP.txt in the API project.
Click here to download.
How can I get properties to work?
thank you
I have never seen WCF contract used with a property to transfer data. i.e. the Message property. AFAIK its just not possible.
My recommendation would be to keep the concerns that are part of the contract separate, i.e. Operation and Data.
[ServiceContract]
public interface IEcho
{
[OperationContract]
string SendEcho(string Message);
}
Or
[ServiceContract]
public interface IEcho
{
[OperationContract]
string SendEcho(Message message);
}
[DataContract]
public class Message
{
[DataMember]
public string Message {get; set;}
}
At some later point you may wish to change the Message Object.
[DataContract]
public class MessageV2 : Message
{
[DataMember]
public DateTime Sent {get; set;}
}
While this changes the contract, changes like this can be backwardly compatible if managed carefully.
To understand what's happening, you need to know how the lifetime of the service object you're connecting to is configured. A good starting point is the MSDN article on Sessions, Instancing, and Concurrency.
For example, with InstanceContextMode.PerCall, a new service object will be created for each call, so no properties of the service object will be remembered between calls.
At the other end of the scale, InstanceContextMode.Single means a single instance handles all client requests for the lifetime of the application. In this case properties set by one client will be visible to all clients, not usually desirable.
In general, I would recommend using a stateless service object. But if you want a stateful service object (e.g. one with properties), you should use InstanceContextMode.PerSession, and (important) use a binding that supports sessions.
While I agree with #JTew that you shouldn't generally expose operations as properties, you will have the same problem if you try to use an object that stores state between calls in another way (such as a private field). I.e. the following would have exactly the same problem:
[ServiceContract]
public interface IEcho
{
[OperationContract]
void SetMessage(string message);
[OperationContract]
string GetMessage();
... etc ...
}