I would like to use a base message class like:
[Serializable]
public abstract class MessageBase : IMessage
{
public Guid MessageID { get; private set; }
public DateTime UtcDateTime { get; private set; }
protected MessageBase()
{
UtcDateTime = DateTime.UtcNow;
MessageID = Guid.NewGuid();
}
public override string ToString()
{
return string.Format("{0} MessageID={1}, UtcDate={2}", GetType().FullName, MessageID, UtcDateTime);
}
}
New messages will be created by subclassing from this base class. Here is the problem I observed. When I publish a message, I see that the message id and datetime is different when it is handled.
What am I missing?
I know you want to declare MessageID and UtcDateTime with private setters so that someone down the line can't change it, but in doing so, you prevent the serializer from re-applying those values when the message is reconstructed at the receiver.
What is happening is that the serializer instantiates a new instance of your message type, and your two properties are initialized to UtcNow and NewGuid(), and then aren't overridden from the message. This is why they appear different.
If you remove the private keyword from the property declaration, you should get the behavior you are expecting.
However, instead of baking your own tracking mechanisms like this, you should at least (assuming you have injected an IBus into your handler) take a look at Bus.CurrentMessageContext, which contains an "Id" property for the message being handled (string, not Guid) and a Headers collection. I'm not 100% certain, but if you inspect the headers there is probably some indication of the original send time in there.
Related
I have the following class I'd like to send from my WCF (C#) service to my client (WPF):
[DataContract]
public class OutputAvailableEventArgs
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Message { get; private set; }
[DataMember]
public bool IsError { get; private set; }
public OutputAvailableEventArgs(int id) : this(id, false, "") { }
public OutputAvailableEventArgs(int id, string output) : this(id, false, output) { }
public OutputAvailableEventArgs(int id, bool isError, string output)
{
ID = id;
IsError = isError;
Message = output;
}
}
It's used by the service as follows:
var channel = OperationContext.Current.GetCallbackChannel<IClientCallback>();
channel.OutputAvailable(new OutputAvailableEventArgs(1, false, "some message"));
At the client side, the members get their default values.
I tried marking them with IsRequired attribute but now the OutputAvailable at the client is not called. The code at the service side seems to run smoothly (I didn't notice anything with the debugger).
How can I transfer a DataContract class with WCF while maintaining the members' values?
(I saw solutions that suggested to use OnSerialized and OnDeserialized but I don't need just a default constructor.)
I saw many different solutions for this problem. For other people's sake I'll write some of them down + what worked for me:
It seems that in some cases specifying the items' order solves the problem. Please see this SO question for full details.
If it's some default initialization you're after, you can use OnSerialized and OnDeserialized methods to call your initialization methods.
I also tried using the IsRequired attribute on my DataMembers but still didn't get my objects.
What worked for me was adding NameSpace property in the DataContract attribute. Apparently, In order to have the contracts be considered equal, you must set the Namespace property on the DataContract to the same value on both sides.
This is one of the classes in Interface file.
[DataContract]
public class ClassX
{
public ClassX()
{
ClassXParameters = new List<ClassXParameter>();
}
public void Add(string name, string value)
{
ClassXParameters.Add(new ClassXParameter() { Name = name, Value = value });
}
[DataMember]
public List<ClassXParameter> ClassXParameters { get; set; }
}
[DataContract]
public class ClassXParameter
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
}
on the client I'm trying to do something like this
ClassX classx = new ClassX();
classx.Add("testname", "testvalue");
But this .Add method is not even visible.
currently I'm doing
ClassX classx = new ClassX();
List<ClassXParameter> params = new List<ClassXParameter()>;
params.add(new ClassXParameter() {Name="testname", Value="testvalue"});
classx.ClassXParameters = params;
Is there anyway I can do what I'm trying to do?
Note: I am not sure why some of the text above are in bold.
If you autogenerate the client code from scratch, it will generate a new class, which contains those members and properties that are marked with DataContract.
If you have methods that you want available on the client, you can accomplish this by putting the DataContract types in an own assembly, which you reference from both the server and the client. When you generate the service reference you have to choose the option to reuse existing classes instead of generating new ones.
Often it is suitable to put data validation rules in the data contract classes property setters. Reusing the data contract assembly in the client will cause the data validation to occur directly on the client, without the need for a roundtrip. It also causes the error in a place where it is much easier to spot than if it is reported as deserialization error.
Data Contracts are for data only. Any methods will not be visible on the client.
The bold was because of the "-----".
Are handlers reused to proceed another message?
public abstract class SomeHandler : IHandleMessages<MyEvent>
{
public IBus Bus { get; set; }
public String Message { get; set; }
public void Handle(T message)
{
Message = "Test";
SomeInstanceMethod();
}
public void SomeInstanceMethod()
{
if (Message = ...) // Can i use Message here?
return;
}
}
By default, message handlers are configured as ComponentCallModelEnum.Singlecall, which means that each call on the component will be performed on a new instance.
So, two messages will be processed by different instances of the class and cannot share state.
However, what you have here is setting a class property and then calling another method in the class that retrieves that property. That would work fine. However, in my opinion, that is kind of confusing, and if that is what you're after, you're probably better off passing values to another method as a parameter.
The book Domain Driven Design by Eric Evans describes pattern called value object. One of the important characteristics of a value object is that it is immutable.
As an example I have a value object "Clinic" which must have a name and an id. To make it a value object I do not provide setters on name and id. Also to make sure that there is not invalid instance I take name and id in a constructor and do not provide at parameter less constructor.
public class Clinic
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public string Name{get; private set;}
public string Id{get; private set;}
}
The problem is that when I try to return this object from a WCF Service I get an exception that the object does not have parameter less constructor and the properties do not have public setters. I want to avoid adding parameter less constructor and public setters because then my domain model pattern goes for a toss. How can I get around this problem?
Regards,
Unmesh
I had a similar problem with serializing immutable types before, in the end I decided to implement the ISerializable interface and use the SerializationInfo to store & retrieve the private variables at both ends of the serialization/deserialization process:
http://theburningmonk.com/2010/04/net-tips-making-a-serializable-immutable-struct/
I just built and run a test app using the same technique and it seems to work for me. So in terms of changes to your Clinic class you could change it to:
[Serializable]
public class Clinic : ISerializable
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public Clinic(SerializationInfo info, StreamingContext context)
{
Name= info.GetString("Name");
Id= info.GetString("Id");
}
public string Name{get; private set;}
public string Id{get; private set;}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", Name);
info.AddValue("Id", Id);
}
}
This will solve the problem you're having passing the data back from WCF. But from a design point of view, I agree with what Ladislav is saying and typically you will want to separate your domain objects with objects purely intended for message passing (DataTransferObjects), and in that case here's an example of how you MIGHT approach it:
// the domain object (NOT EXPOSED through the WCF service)
public class Clinic
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public string Name{ get; private set;}
public string Id{ get; private set;}
// other methods encapsulating some business logic, etc.
...
}
// the corresponding DTO object for the domain object Clinic
// this is the type exposed through the WCF layer, that the client knows about
[DataContract]
public class ClinicDTO
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Id { get; set; }
}
// WCF service contract, NOTE it returns ClinicDTO instead of Clinic
[ServiceContract]
public interface IClinicService
{
[OperationContract]
ClinicDTO GetClinicById(string id);
}
To ease the pain of converting from Clinic to ClinicDTO, you could either add a method on Clinic to do this or implement an implicit/explicit converter. I've got an example on how to do this here:
http://theburningmonk.com/2010/02/controlling-type-conversion-in-c/
Hope this helps!
The problem is that your value object is not serializable. How do you plan to use the service? Do you plan to share domain objects / value objects with your clients? If yes than it IMO violates your domain driven desing - only business layer should be able to work with domain objects and call their methods. If you don't want to share objects you will probably create proxy by add service reference which will generate data contrats for the client. These contrats will have public parameterless constructor and all properties settable (and no domain methods).
If you want to have real Domain driven design you should not expose your domain objects in WCF. Instead you should create set of DTO and expose those DTOs. Service layer will be responsible of converting those DTOs to Domain objects / value objects and vice-versa.
I'm passing an object to a WCF service and wasn't getting anything back. I checked the variable as it gets passed to the method that actually does the work and noticed that none of the values are set on the object at that point. Here's the object:
[DataContract]
public class Section {
[DataMember]
public long SectionID { get; set; }
[DataMember]
public string Title { get; set; }
[DataMember]
public string Text { get; set; }
[DataMember]
public int Order { get; set; }
}
Here's the service code for the method:
[OperationContract]
public List<Section> LoadAllSections(Section s) {
return SectionRepository.Instance().LoadAll(s);
}
The code that actually calls this method is this and is located in a Silverlight XAML file:
SectionServiceClient proxy = new SectionServiceClient();
proxy.LoadAllSectionsCompleted += new EventHandler<LoadAllSectionsCompletedEventArgs>(proxy_LoadAllSectionsCompleted);
Section s = new Section();
s.SectionID = 4;
proxy.LoadAllSectionsAsync(s);
When the code finally gets into the method LoadAllSections(Section s), the parameter's SectionID is not set. I stepped through the code and when it goes into the generated code that returns an IAsyncResult object, the object's properties are set. But when it actually calls the method, LoadAllSections, the parameter received is completely blank. Is there something I have to set to make the proeprty stick between method calls?
Works just fine for me - could it be a silly typo??
In your OperationContract, you define LoadAllSections but in your client code, you attach an event handler to the proxy.GetAllSectionsCompleted event - maybe that's just the wrong handler? Shouldn't it be proxy.LoadAllSectionsCompleted ??
Marc
This seems odd, but it's what happens. I had another method on the service that returned a DataTable. Whenever a method tries to return a DataTable, the parameters passed in lose their values. Take out the method, and everything works. Odd.