Data Contracts - Same Class Different Objects in Client

May 11, 2007 at 12:51 PM
Hi I have the following problem. I have one class 'Group Info' which is exposed in both the Group Service and the User Service. When I 'Add Service Reference' to these services (User.svc and Group.svc) they both contain there own version of GroupInfo even though they are the same class. I am able to copy the type from both classes in ao commn type library class and refence that but every time I want to update the reference (delete and add again) I need to do all this work again. It doesn't make sense to me that these are not interchangable.

Is this a problem that needs to be addressed or am I doing something wrong. Gregor Hohpe talked about this years ago in a talk when mentioning webservices and I thought this was resilved by adding the datacontract namespace! Should I be able to share types between services like this? And should I have to edit the clients?

//Data Contact

DataContract(Namespace = "http://ServiceManagement.Core.DataContracts/2007/04", Name = "GroupInfo")
public partial class GroupInfo
{
DataMember(IsRequired = false, Name = "GroupId", Order = 1)
public System.Int32 GroupId;
DataMember(IsRequired = false, Name = "GroupName", Order = 2)
public System.String GroupName;
DataMember(IsRequired = false, Name = "Active", Order = 3)
public System.Boolean Active;
}

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using ServiceManagement.Core.Service;
using ServiceManagement.Core.BusinessLogic.Facade;
using ServiceManagement.Core.Service.DataContracts;
using ServiceManagement.Core.Service.Implementation.Translator;

namespace ServiceManagement.Core.Service.Implementation
{
ServiceBehavior(Name = "GroupService", Namespace = "http://ServiceManagement.ServiceContracts/2007/04")
public class GroupService : ServiceManagement.Core.Service.Contracts.IGroupService
{
public ServiceManagement.Core.Service.DataContracts.GroupInfo FindGroup(DataContracts.GroupSearch request)
{
//Do the work and
return BusinessFacade.GroupInfo();
}
}
}
namespace ServiceManagement.Core.Service.Implementation
{
ServiceBehavior(Name = "UserService", Namespace = "http://ServiceManagement.ServiceContracts/2007/04")
public class UserService : ServiceManagement.Core.Service.Contracts.IUserService
{
public ServiceManagement.Core.Service.DataContracts.GroupInfo[] GetGroupsForUser(DataContracts.UserInfo request)
{
//Do the work and
List groups = BusinessFacade.User.Groups();
//Do ALL THE translations
retrun GroupInfo[];
}
}
}

//The clients when discovering the service do this

//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.1318
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace ServiceManagementClientLibrary.UserService
{
System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")
System.Runtime.Serialization.DataContractAttribute(Namespace = "http://ServiceManagement.Core.DataContracts/2007/04")
System.SerializableAttribute()
public partial class GroupInfo : object, System.Runtime.Serialization.IExtensibleDataObject
{

System.NonSerializedAttribute()
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

System.Runtime.Serialization.OptionalFieldAttribute()
private int GroupIdField;

System.Runtime.Serialization.OptionalFieldAttribute()
private string GroupNameField;

System.Runtime.Serialization.OptionalFieldAttribute()
private bool ActiveField;

public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}

System.Runtime.Serialization.DataMemberAttribute()
public int GroupId
{
get
{
return this.GroupIdField;
}
set
{
this.GroupIdField = value;
}
}

System.Runtime.Serialization.DataMemberAttribute()
public string GroupName
{
get
{
return this.GroupNameField;
}
set
{
this.GroupNameField = value;
}
}

System.Runtime.Serialization.DataMemberAttribute(Order = 2)
public bool Active
{
get
{
return this.ActiveField;
}
set
{
this.ActiveField = value;
}
}
}

}



May 11, 2007 at 3:50 PM
If you want to avoid duplicating your dependent objects (DataContracts) on the client and if you control both ends of the WCF connection, you can bypass the proxy generation step on the client and directly instantiate a proxy off the ServiceContracts and DataContracts.
For this apporach you may use raw ChannelFactories or GenericProxy<T> approach explicitly and manual configuration, but it’s still very easy to do with just a few lines of code. This removes any ambiguity by avoiding importing and translating of WSDL into a proxy altogether. For example:
ChannelFactory<IMyContract> factory = new ChannelFactory<IMyContract>("configEndpoint");
IMyContract proxy = factory.CreateChannel();
using(proxy as IDisposable)
{
    proxy.MyOperation();
}

Always remember to close the proxy by either casting it to IDisposable and calling the Dispose( ) method or to ICommunicationObject and calling the Close( ) method.

Charly
May 11, 2007 at 8:33 PM
Is there no way to do this without having to roll my own channel factory? Just a thought.... Another option I have is that I chould split out the data contracts and service interfaces into a Dlls and use those in my clients.... I have a look over the weekend.
May 12, 2007 at 8:54 PM
Please send feedback if you find any interesting.

thx
May 14, 2007 at 3:18 PM
Well, you actually don't need to create any "custom" channel factory. Just use the built in factory as I pointed out in the above example (ChannelFactory belongs to WCF API) and you should be fine.
In order to use this approach, you will also need to have your contracts in a separate asm (reused from your service) as you described.