Reusing types with message contracts - c#

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.

Related

Why can't I create a Service Reference in Visual Studio?

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.

Implementing wcf with mvc4 entity framework fails invoking method

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;

Async WCF: wait for another call

We have an old Silverlight UserControl + WCF component in our framework and we would like to increase the reusability of this feature. The component should work with basic functionality by default, but we would like to extend it based on the current project (without modifying the original, so more of this control can appear in the full system with different functionality).
So we made a plan, where everything looks great, except one thing. Here is a short summary:
Silverlight UserControl can be extended and manipulated via ContentPresenter at the UI and ViewModel inheritance, events and messaging in the client logic.
Back-end business logic can be manipulated with module loading.
This gonna be okay I think. For example you can disable/remove fields from the UI with overriden ViewModel properties, and at the back-end you can avoid some action with custom modules.
The interesting part is when you add new fields via the ContentPresenter. Ok, you add new properties to the inherited ViewModel, then you can bind to them. You have the additional data. When you save base data, you know it's succeeded, then you can start saving your additional data (additional data can be anything, in a different table at back-end for example). Fine, we extended our UserControl and the back-end logic and the original userControl still doesn't know anything about our extension.
But we lost transaction. For example we can save base data, but additional data saving throws an exception, we have the updated base data but nothing in the additional table. We really doesn't want this possibility, so I came up with this idea:
One WCF call should wait for the other at the back-end, and if both arrived, we can begin cross thread communication between them, and of course, we can handle the base and the additional data in the same transaction, and the base component still doesn't know anything about the other (it just provide a feature to do something with it, but it doesn't know who gonna do it).
I made a very simplified proof of concept solution, this is the output:
1 send begins
Press return to send the second piece
2 send begins
2 send completed, returned: 1
1 send completed, returned: 2
Service
namespace MyService
{
[ServiceContract]
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service1
{
protected bool _sameArrived;
protected Piece _same;
[OperationContract]
public Piece SendPiece(Piece piece)
{
_sameArrived = false;
Mediator.Instance.WaitFor(piece, sameArrived);
while (!_sameArrived)
{
Thread.Sleep(100);
}
return _same;
}
protected void sameArrived(Piece piece)
{
_same = piece;
_sameArrived = true;
}
}
}
Piece (entity)
namespace MyService
{
[DataContract]
public class Piece
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string SameIdentifier { get; set; }
}
}
Mediator
namespace MyService
{
public sealed class Mediator
{
private static Mediator _instance;
private static object syncRoot = new Object();
private List<Tuple<Piece, Action<Piece>>> _waitsFor;
private Mediator()
{
_waitsFor = new List<Tuple<Piece, Action<Piece>>>();
}
public static Mediator Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
_instance = new Mediator();
}
}
return _instance;
}
}
public void WaitFor(Piece piece, Action<Piece> callback)
{
lock (_waitsFor)
{
var waiter = _waitsFor.Where(i => i.Item1.SameIdentifier == piece.SameIdentifier).FirstOrDefault();
if (waiter != null)
{
_waitsFor.Remove(waiter);
waiter.Item2(piece);
callback(waiter.Item1);
}
else
{
_waitsFor.Add(new Tuple<Piece, Action<Piece>>(piece, callback));
}
}
}
}
}
And the client side code
namespace MyClient
{
class Program
{
static void Main(string[] args)
{
Client c1 = new Client(new Piece()
{
ID = 1,
SameIdentifier = "customIdentifier"
});
Client c2 = new Client(new Piece()
{
ID = 2,
SameIdentifier = "customIdentifier"
});
c1.SendPiece();
Console.WriteLine("Press return to send the second piece");
Console.ReadLine();
c2.SendPiece();
Console.ReadLine();
}
}
class Client
{
protected Piece _piece;
protected Service1Client _service;
public Client(Piece piece)
{
_piece = piece;
_service = new Service1Client();
}
public void SendPiece()
{
Console.WriteLine("{0} send begins", _piece.ID);
_service.BeginSendPiece(_piece, new AsyncCallback(sendPieceCallback), null);
}
protected void sendPieceCallback(IAsyncResult result)
{
Piece returnedPiece = _service.EndSendPiece(result);
Console.WriteLine("{0} send completed, returned: {1}", _piece.ID, returnedPiece.ID);
}
}
}
So is it a good idea to wait for another WCF call (which may or may not be invoked, so in a real example it would be more complex), and process them together with cross threading communication? Or not and I should look for another solution?
Thanks in advance,
negra
If you want to extend your application without changing any existing code, you can use MEF that is Microsoft Extensibility Framework.
For using MEF with silverlight see: http://development-guides.silverbaylabs.org/Video/Silverlight-MEF
I would not wait for 2 WCF calls from Silverlight, for the following reasons:
You are making your code more complex and less maintainable
You are storing business knowledge, that two services should be called together, in the client
I would call a single service that aggreagated the two services.
It doesn't feel like a great idea to me, to be honest. I think it would be neater if you could package up both "partial" requests in a single "full" request, and wait for that. Unfortunately I don't know the best way of doing that within WCF. It's possible that there's a generalized mechanism for this, but I don't know about it. Basically you'd need some loosely typed service layer where you could represent a generalized request and a generalized response, routing the requests appropriately in the server. You could then represent a collection of requests and responses easily.
That's the approach I'd look at, personally - but I don't know how neatly it will turn out in WCF.

WCF OperationContract property forgets value

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 ...
}

Can I use WCF duplex binding to relay message?

I have a Client Application, a server and another client, lets call it third party. I have a callback interface as part of my contract that is implemented both by the third party and the client.
The third party will call a server operation(method) then the server will trigger a callback but instead of calling the callback of the third party, it will call the callback implementation of the client.
Yes, you can absolutely do that.
The easiest way is to implement your service as a PerSession service, and capture the callback context on initialization/construction. Typically I will add the service object (which really represents a connection at that point) to an internal core object.
Then, when you get in a message from a client, you can make a call to any of the service objects (not through the contract), and internally forward the data to the associated client.
This is a pretty minimal implementation of the concept, without exception handling, and some pretty bad design (static class BAD!). I haven't tested this, but the principles should hold even if I missed crossing an i or dotting a t. This example also forwards the calls to all clients, but selecting an individual client follows the same basic pattern.
Trying to do this with a singleton service will be more difficult, and a per-call service obviously won't work :)
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface IContract
{
[OperationContract(IsOneWay = true)]
void SendTheData(string s);
}
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void ForwardTheData(string s);
}
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.PerSession)]
public class ServiceConnection : IContract
{
private ICallback m_callback;
public ServiceConnection()
{
m_callback = OperationContext.Current.GetCallbackChannel<ICallback>();
ServiceCore.Add(this);
}
public void SendTheData(string s)
{
ServiceCore.DataArrived(s);
}
public void SendToClient(string s)
{
m_callback.ForwardTheData(s);
}
}
static public class ServiceCore
{
static private List<ServiceConnection> m_connections = new List<ServiceConnection>();
public static void DataArrived(string s)
{
foreach(ServiceConnection conn in m_connections)
{
conn.SendTheData(s);
}
}
public static void Add(ServiceConnection connection)
{
m_connections.Add(connection);
}
}
From a quick read of the Microsoft Duplex service documentation I don't think that will do what you want. There could be some other clever Kung Fu WCF way to do it but in my case I created a "PassThruService" for the server that implemented the same contract as the real service and sent any requests received onto the client.
This is a part of my code that explains the thrust of it.
private const int OPERATION_TIMEOUT = 5000;
private MyServiceClient m_client = new MyServiceClient();
public bool IsAlive() {
try {
logger.Debug("PassThruService IsAlive.");
bool isAlive = false;
ManualResetEvent isAliveMRE = new ManualResetEvent(false);
m_client.IsAliveComplete += (s, a) => { isAlive = a.Result; isAliveMRE.Set(); };
m_client.IsAliveAsync();
if (isAliveMRE.WaitOne(OPERATION_TIMEOUT)) {
return isAlive;
}
else {
throw new TimeoutException();
}
}
catch (Exception excp) {
logger.Error("Exception PassThruService IsAlive. " + excp.Message);
throw;
}
I don't fully see what you're really asking here.... but I'll try to give some tips anyway.
Relaying messages or routing is not very well supported in WCF in .NET 3.5 - the infrastructure is there, but it's still a lot of work to set it up manually.
The best intro I know into this topic for WCF in .NET 3.5 is a two-part article by Michele Leroux Bustamante on MSDN magazine:
Building a WCF Router, Part 1
Building a WCF Router, Part 2
Part 2 has a section on duplex routers - does that help you in your quest at all??
WCF in .NET 4.0 promises to bring additional support for routing - there will be a RoutingService base class which can be leveraged to write routing services, and it will allow for configurable, content- or metadata-based routing - whatever it is that you need.
.NET 4.0 is scheduled to be released sometime later this year (2009) - hopefully! So while this is still the future, it's looking rosy!
Marc
I think I found the solution..
Here's the link.
http://msdn.microsoft.com/en-us/magazine/cc163537.aspx
Try to look at figure 6. That's what I'm trying to achieve.

Categories