5

I have a service with multiple contracts like so.

[ServiceContract]
public partial interface IBusinessFunctionDAO {

    [OperationContract]
    BusinessFunction GetBusinessFunction(Int32 businessFunctionRefID);

    [OperationContract]
    IEnumerable<Project> GetProjects(Int32 businessFunctionRefID);
}

[ServiceContract]
public partial interface IBusinessUnitDAO {

    [OperationContract]
    BusinessUnit GetBusinessUnit(Int32 businessUnitRefID);

    [OperationContract]
    IEnumerable<Project> GetProjects(Int32 businessUnitRefID);
}

I then explicitly implemented each one of the interfaces like so.

public class TrackingTool : IBusinessFunctionDAO, IBusinessUnitDAO {

    BusinessFunction IBusinessFunctionDAO.GetBusinessFunction(Int32 businessFunctionRefID) {
      // implementation
    }
    IEnumerable<Project> IBusinessFunctionDAO.GetProjects(Int32 businessFunctionRefID) {
      // implementation
    }

    BusinessUnit IBusinessUnitDAO.GetBusinessUnit(Int32 businessUnitRefID) {
      // implementation
    }
    IEnumerable<Project> IBusinessUnitDAO.GetProjects(Int32 businessUnitRefID) {
      // implementation
    }
}

As you can see I have two GetProjects(int) methods, but each one is implemented explicitly so this compiles just fine and is perfectly valid. The problem arises when I actually start this as a service. It gives me an error staying that TrackingTool already contains a definition GetProject. While it is true, it is part of a different service contract. Does WCF not distinguish between service contracts when generating the method names ? Is there a way to get it to distinguish between the service contracts ?

My App.Config looks like this

<service name="TrackingTool">
  <endpoint address="BusinessUnit" contract="IBusinessUnitDAO" />
  <endpoint address="BusinessFunction" contract="IBusinessFunctionDAO" />
</service>

Any help would be appreciated.

Thanks, Raul

HaxElit
  • 3,983
  • 7
  • 34
  • 43
  • as @monO mentions in his answer - the actual individual method names must be unique in your service class. The WSDL doesn't know anything about your interfaces and all and it doesn't allow method overloading with different parameters; you need to make those method names unique over the entire service class! – marc_s Mar 09 '10 at 18:07
  • Ah, I guess I gotta read up on how the WSDL is actually created. I thought that each contract has it's own namespace inside the WSDL. – HaxElit Mar 09 '10 at 18:49
  • ...actually I find that pretty confusing, too. @marc_s: the WSDL does know about the different interfaces. They are exposed as dedicated ports with dedicated bindings. – Alex Mar 09 '10 at 22:51
  • @Alex: Check my answer I just answered why this happens even though the interfaces are exposed as dedicated ports. – HaxElit Mar 10 '10 at 16:41

3 Answers3

10

I think I found the reason for it. In the WSDL the function gets exposed as the following:

<wsdl:message name="IBusinessUnitDAO_GetBusinessUnitProjects_InputMessage">
  <wsdl:part name="parameters" element="tns:GetBusinessUnitProjects" />
</wsdl:message>
<wsdl:message name="IBusinessFunctionDAO_GetBusinessFunctionProjects_InputMessage">
  <wsdl:part name="parameters" element="tns:GetBusinessFunctionProjects" />
</wsdl:message>

Then in the xsd that defines the tns: namespace we have the following:

<xs:element name="GetBusinessUnitProjects">
  <xs:complexType>
    <xs:sequence>
      <xs:element minOccurs="0" name="businessUnitRefID" type="xs:int" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

<xs:element name="GetBusinessFunctionProjects">
  <xs:complexType>
    <xs:sequence>
      <xs:element minOccurs="0" name="businessFunctionRefID" type="xs:int" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

So the reason for the collision even though the the service is exposing two different contracts is because all of the wsdl part elements are in the same namespace. So when you create two function names that are identical you get duplicate elements with the same name which causes the problem. So the solution to the problem is to add a namespace attribute to each service contract. If we take our original service contract and modify it like so.

[ServiceContract(Namespace="Tracking/BusinessFunction")]
public partial interface IBusinessFunctionDAO {

    [OperationContract]
    BusinessFunction GetBusinessFunction(Int32 businessFunctionRefID);

    [OperationContract]
    IEnumerable<Project> GetProjects(Int32 businessFunctionRefID);
}

[ServiceContract(Namespace="Tracking/BusinessUnit")]
public partial interface IBusinessUnitDAO {

    [OperationContract]
    BusinessUnit GetBusinessUnit(Int32 businessUnitRefID);

    [OperationContract]
    IEnumerable<Project> GetProjects(Int32 businessUnitRefID);
}

When we generate the WSDL we get a WSDL for each namespace we create. This namespace has each port identified with all its operations and elements. So inside each of our seperate WSDL's we get the following:

//File: Tracking.BusinessFunction.wsdl
<wsdl:message name="IBusinessFunctionDAO_GetProjects_InputMessage">
  <wsdl:part name="parameters" element="tns:GetProjects" />
</wsdl:message>

//File: Tracking.BusinessUnit.wsdl
<wsdl:message name="IBusinessUnitDAO_GetProjects_InputMessage">
  <wsdl:part name="parameters" element="tns:GetProjects" />
</wsdl:message>

as you can see they both have the same element name, but because they are in different namespaces the elements no longer conflict with each other. If we take a look at the xsd they now have the same elements defined but with different parameters:

//File: Tracking.BusinessFunction.xsd
<xs:element name="GetProjects">
  <xs:complexType>
    <xs:sequence>
      <xs:element minOccurs="0" name="businessFunctionRefID" type="xs:int" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

//File: Tracking.BusinessUnit.xsd
<xs:element name="GetProjects">
  <xs:complexType>
    <xs:sequence>
      <xs:element minOccurs="0" name="businessUnitRefID" type="xs:int" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

So the answer to my original question is to make each service contract live in a separate namespace so you don't have conflicting port elements. This also gives you the flexibility of having your contracts in separate WSDL's which are easier to manage if you are distributing parts of them.

HaxElit
  • 3,983
  • 7
  • 34
  • 43
  • Just a follow up, the way I think it should work is that if an interface is defined explicitly then the element name that gets generated should be . this would then eliminate the collision with message part elements. Any thoughts ? – HaxElit Mar 10 '10 at 16:49
4

You could try using an alias perhaps:

[OperationContract(Name = "YourMethodNameHere")]
IEnumerable GetProjects(Int32 businessUnitRefID);

See: http://jeffbarnes.net/blog/post/2006/09/21/Overloading-Methods-in-WCF.aspx

mint
  • 3,341
  • 11
  • 38
  • 55
0

Try setting the Action property on the OperationContract attribute for the two methods that have the same name to remove the conflict, like so:

[ServiceContract]
public partial interface IBusinessFunctionDAO {

[OperationContract]
BusinessFunction GetBusinessFunction(Int32 businessFunctionRefID);

[OperationContract(Action="GetBusinessFunctionProjects")]
IEnumerable<Project> GetProjects(Int32 businessFunctionRefID);
}

[ServiceContract]
public partial interface IBusinessUnitDAO {

[OperationContract]
BusinessUnit GetBusinessUnit(Int32 businessUnitRefID);

[OperationContract(Action="GetBusinessUnitProjects")]
IEnumerable<Project> GetProjects(Int32 businessUnitRefID);
}
lee-m
  • 2,269
  • 17
  • 29