views:

18

answers:

1

I am aware of formatted DataContract names, as described here: http://msdn.microsoft.com/en-us/library/ms731045.aspx (Customizing Data Contract Names for Generic Types near the bottom).

Example:

[DataContract( Name = "SearchCriteriaFor{0}", Namespace = "http://schema.mycompany.com/MyProject/" )]
public class SearchCriteria<T> { ...

This would cause SearchCriteria<Employee> to become <xs:complexType name="SearchCriteriaForEmployee"> in the generated XSD for the service. This looks a lot nicer than SearchCriteriaOfEmployeeWkD50_Xf (generic+"Of"+types+hash).

I want to do this for ServiceContracts as well. Unfortunately using the {0} syntax doesn't work (the braces get escaped and the zero remains literal). I haven't found any examples of how to do this, but I hoped that since it works for DataContract that it would also work for ServiceContract. Is there any way to include the type arguments as part of a custom serialization name for a ServiceContract?

However, as writing this it just occurred to me that including the type name may not even be necessary for ServiceContract at all, even though the default naming implementation does so. Is it acceptable to specify a fixed name for a generic ServiceContract? I tried it and it appears to generate the XSD correctly, but would I have to worry about any future conflicts due to this? This is an internal system and I can guarantee against any name/namespace collisions for any objects that would be used as generic type arguments.

For instance if I have a IDataStore<T>, is there any problem with:

[ServiceContract( Name = "DataStore", Namespace = "http://schema.mycompany.com/MyProject/" )]
public interface IDataStore<T> where T : MyBaseObject
{ IList<T> FindAll(); }

which would cause the resulting XSD to show http://schema.mycompany.com/MyProject/DataStore/FindAll instead of http://schema.mycompany.com/MyProject/IDataStoreOf_Employee/FindAll.

Lots of rambling here, so the real questions are in bold above.

A: 

No, there is no way to include the type arguments as part of a custom serialization name. Using Reflector, you can find the code that creates the name in System.ServiceModel.Description.NamingHelper.GetContractName. It looks like this:

internal static XmlQualifiedName GetContractName(Type contractType, string name, string ns)
{
    XmlName name2 = new XmlName(name ?? TypeName(contractType));

The TypeName function has logic to create names for generic types and array types, but if you supply a Name in the contract it will just use that name exactly.

I would not recommend using a fixed name for a generic contract, but it might work. Since it's generic, I assume you have more than one instantiation of it, and IDataStore<Foo> and IDataStore<Bar> would have the same fully qualified name but would have operations of different types. As long as nothing sees both versions of the contract at once you should be okay, but if anything does see both at once it may get confused.

Can you create a concrete subclass of the generic interface for each service? You could declare something like:

[ServiceContract(Name = "EmployeeDataStore", Namespace = "http://schema.mycompany.com/MyProject/")]
public interface IEmployeeDataStore : IDataStore<Employee> { }

and have your service type implement that interface instead of just IDataStore<Employee>. Then, you could set the name explicitly for each type.

Quartermeister
Yes, I am inheriting an interface for each service. So for instance I have IFooDataStore : IDataStore<Foo>, and IBarDataStore : IDataStore<Bar>. So I am never directly using the IDataStore<T> interface as a contract. Additionally all of my contracts are implemented as separate concrete classes and use separate ServiceHosts, so there should be no conflicts in the resulting metadata. So it looks like I'll have operations in the WSDL that are IDataStore.FindAll, but since every service is separate, that shouldn't cause a problem. Thanks for the explanation!
Brad