I have some services at the moment that return a dto with the following fields:
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
and I want to add more to this service by adding the following properties:
[DataMember]
public virtual DateTime StartDate { get; set; }
I'm not in a position where i can update the consumers of these services though - the client does that themselves.
My question is - will the old clients be able to just skip these new properties? and the new ones take advantage of them or will the serialization be an issue with the new properties?
w://
As long as the old properties do not change (and the new one is marked as optional) you should be alright.
Said so, you should publish the new contract and get the clients to regenerate the service reference - or deploy the new version to a different endpoint so that when they're ready to switch they are forced to point to the new one.
From what I have seen, the DataContractSerializer just puts null in for properties not found when deserializing. Makes tracking down some bugs quite tricky - sometimes I would prefer if it were more strict and gave an exception.
Another option to consider is subclassing the original DTO to create a new derived class.
In order for serialization to work properly, you need to specify the available derived classes for the supertype with an attribute:
[DataContract]
[KnownType(typeof(DerivedDTO))]
public class OriginalDTO
In code where you use the additional property, you will need to cast the object to a DerivedDTO to get access to the property (I use the as keyword for this and check whether the resulting reference is null before using it)
As long as the new member StartDate is not declared a required field - so this would not work:
[DataMember(IsRequired="True")]
public virtual DateTime StartDate { get; set; }
But as long as you leave out the IsRequired=True, you should be fine.
Related
I am currently in the middle of writing unit tests for my domain models.
To lay a bit of context I have a Role Group class which has a list of Roles, it also has a list of Users that currently have this Role Group assigned to them.
A Role Group is assigned to a User so all the methods to do this are in the User domain model. This means the Users property on the Role Group side is basically pulled in from the database.
Both Role Group and User are Aggregate Roots and they can both exist on their own.
Unit Testing a Domain Model Containing Lists Populated From The Database
What I am struggling with is I can not test the CanArchive method below because I have no way off adding in a User to the property. Apart from the bad was of using the Add method which I don't want to use as it break the whole idea of Domain Models controlling their own Data.
So I am not sure if my Domain Models are wrong or if this logic should be placed in a Service as it is an interaction between two Aggregate Roots.
The Role Group Class:
public bool Archived { get; private set; }
public int Id { get; private set; }
public string Name { get; private set; }
public virtual IList<Role> Roles { get; private set; }
public virtual IList<User> Users { get; private set; }
Updating Archived Method:
private void UpdateArchived(bool archived)
{
if (archived && !CanArchive())
{
throw new InvalidOperationException("Role Group can not be archvied.");
}
Archived = archived;
}
Method to check if Role Group can be Archived
private bool CanArchive()
{
if (Users.Count > 0)
{
return false;
}
return true;
}
Method that sets the User's Role Group in the User class
This is called when a user is created or update in the user interface.
private void UpdateRoleGroup(RoleGroup roleGroup)
{
if (roleGroup == null)
{
throw new ArgumentNullException("roleGroup", "Role Group can not be null.");
}
RoleGroup = roleGroup;
}
A few thoughts :
Unit testing a domain object should not rely upon persistence layer stuff. As soon as you do that, you have an integration test.
Integrating changes between two aggregates through the database is theoretically not a good idea in DDD. Changes caused by User.UpdateRoleGroup() should either stay in the User aggregate, or trigger public domain methods on other aggregates to mutate them (in an eventually consistent way). If those methods are public, they should be accessible from tests as well.
With Entity Framework, none of that matters really since it is not good at modelling read-only collections and you'll most likely have a mutable Users list. I don't see calling roleGroup.Users.Add(...) to set up your data in a test as a big problem, even though you should not do it in production code. Maybe making the list internal with internalsVisibleTo your test project - as #NikolaiDante suggests - and wrapping it into a public readonly collection would make that a bit less dangerous.
What I am struggling with is I can not test the CanArchive method below because I have no way off adding in a User to the property.
How does the framework that loads the RoleGroup instances from the database populate the users? This is the question you must ask yourself to find the solution for your unit tests. Just do it the same way.
I don't know what language you use. In Java for example you can use the reflection api to set private fields. There are also a lot of test frameworks that provide convenience methods for this job, e.g. Deencapsulation or Whitebox.
I have a WCF service with yet only one method:
[OperationContract]
void SaveDocument (InwardDocument doc);
[DataContract]
public class InwardDocument{
[DataMember]
public Citizen Citizen {get;set;}
....
}
[DataContract]
public class Citizen{
[DataMember]
public string LastName {get;set;}
....
}
I've tested the service with both WCF test client and a separate .NET console application. In both cases the service works as expected. But when a java client tries to consume it, a deserialization problem occurs. I've put some markers inside the SaveDocument method to see what causes the problem:
public void SaveDocument(InwardDocument doc){
if(doc==null)
throw new ArgumentnullException("InwardDocument");
if(doc.Citizen==null)
throw new ArgumentnullException("InwardDocument.Citizen");//This exception is thrown when consumed by java client
}
As you can see the first exception is skipped which means doc argument itself is not null but for some reason, the Citizen property is null. The guy who generates the request in java client confirms that the InwardDocument.Citizen property is not null by debugging the code. In fact we've had a problem generating the proxy class in that java client which I describe in this SO thread. So I'm assuming it has something to do with that same problem.Maybe I need to add some more attributes to my classes and their members to care of any such problems that might occur in other platforms? Any suggestions are appreciated.
Have you tried to add Know Type attribute in your InwardDocument class. See link here.
[DataContract]
[KnownType(typeof(Citizen))]
public class InwardDocument{
[DataMember]
public Citizen Citizen {get;set;}
....
}
The problem was caused by incorrect creation of the corresponding JAXBelement instances. The solution to the problem is in this SO thread answer
I have a method that is receiving more than one parameter. The method signature with attributes look like the following:
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
public int AddUser(string firstName, string lastName, string emailaddress) { // actions here }
However, when I use this method, I get the following exception:
The HttpOperationHandlerFactory is unable to determine the input
parameter that should be associated with the request message content
for service operation 'Initiate'. If the operation does not expect
content in the request message use the HTTP GET method with the
operation. Otherwise, ensure that one input parameter either has it's
IsContentParameter property set to 'True' or is a type that is
assignable to one of the following: HttpContent, ObjectContent`1,
So, I've created a custom object (such as below) to be passed in.
[DataContract]
public class UserToAdd {
[DataMember] public string firstName { get; set; }
[DataMember] public string lastName { get; set; }
[DataMember] public string emailAddress { get; set; }
}
Using this new signature:
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
public int AddUser(UserToAdd user) { // actions here }
When I do that, I get a 404. It seems I can't win. Any suggestions?
If you want to create routes declaratively you can. I had to do this as I had inherited a bunch of non-restful URIs that had to be supported for backwards compatibility reasons. I created my own attribute to describe URIs, constraints and HTTP methods. This is effectively a replacement for WebInvoke/WebGet. I reflect over my service methods on start-up to discover routes and call MapHttpRoute() as appropriate. Each of my routes specifies the controller and action explicitly. This approach is good for RPC style APIs, but it required quite a lot of heavy lifting. The nice part is that it keeps the definition of routes with the methods - this is not something web api gives you explicitly.
So whilst RPC style is possible, it's not idiomatic. Web API is strongly biased out of the box towards RESTful APIs with convention driven mapping of routes to services and methods - in this way very generic routes are registered and conventions do the rest. That largely avoids the issues of defining routes away from their corresponding actions. If you can I would convert to the RESTful/convention approach of Web API, as you are otherwise fighting the framework a little.
I have an existing web service I need to expand, but it has not gone into production yet. So, I am free to change the contracts as I see fit. But I am not sure of the best way to define the methods.
I am leaning towards Method 2 for no other reason than I cannot think of good names to give the parameters classes!
Are there any major disadvantages to using Method 2 over Method 1?
Method 1
[DataContract(Namespace = Constants.ServiceNamespace)]
public class MyParameters
{
[DataMember(Order = 1, IsRequired = true)]
public int CompanyID { get; set; }
[DataMember(Order = 2, IsRequired = true)]
public string Filter { get; set; }
}
[ServiceContract(Namespace = Constants.ServiceNamespace)]
public interface IMyService
{
[OperationContract, FaultContract(MyServiceFault)]
MyResult MyMethod(MyParameters params);
}
Method 2
public interface IMyService
{
[OperationContract, FaultContract(MyServiceFault)]
MyResult MyMethod(int companyID, string filter);
}
Assuming you are using WCF and the WS-I Basic Profile, the biggest disadvantage of Method 2 over Method 1 is that it makes evolution of the contract more difficult in the future. Parameters classes allow the addition of new fields without creating a new version of the contract, whereas a straight method call does not (because in WS-I Basic, overloaded methods are not allowed). In WCF, there are some hoops you can jump through to get around this restriction but it all lends towards a less readable, more configuration-heavy solution.
For naming parameters classes, I find it helps to think of the method in terms of the underlying message that it represents - the method name is an action, and the parameters are the message associated with that action. If you tell WCF to generate message contracts (when you add a service reference) you'll get to see all of that stuff, and it can sometimes help understand how it hangs together, although it does make the API more verbose and is unnecessary most of the time.
i have e.g. a class like:
public class Person
{
public string name { get; set; }
public int age { get; set; }
}
now i have to access a list of type List<Person> in several pages. so i implemented a singleton class which stores this informations.
is there a better solution, or does i make it right?
As with most questions of this type, it depends on the app.
A common practice for objects/lists which are required by multiple pages it to have an application level view model and reference this from each page.