WCF: Interfaces, Generics and ServiceKnownType

后端 未结 5 1529
隐瞒了意图╮
隐瞒了意图╮ 2020-12-16 03:59

I have the following:

[ServiceContract]
[ServiceKnownType(typeof(ActionParameters))]
[ServiceKnownType(typeof(SportProgram))]
[ServiceKnownType(typeof(Action         


        
相关标签:
5条回答
  • 2020-12-16 04:19

    Are you specifying interfaces in the objects and not concrete types?

    PPS.Core.DomainModel.Support.Action.ActionListResult<IList<PPS.Core.DomainModel.SportProgram.ISportProgram>>
    

    Edit:

    What I'm saying is that are all the concrete types that you are passing in the generics (including in sub object via interfaces) being passed in the Known Types list. We've had serialasation issues where all the types were not known.

    0 讨论(0)
  • 2020-12-16 04:29

    This is one of the problems that I solve with ServiceStack.NET - My Open Source .NET and MONO Web Services Framework.

    Service Stack was heavily influenced by Martin Fowlers Data Transfer Object Pattern as it allows you to simply use DTO's to define your web services - i.e. the SOA way :).

    I avoid this limitation that's inherent in WCF by generating my own WSDL's that behave as you would expect them to. As a benefit of replacing WCF's complex configuration / ServiceContract's model - The SOAP web services also works on MONO - see the live demo.

    0 讨论(0)
  • 2020-12-16 04:31

    You are returning a IList of T. It maybe that the system has problems finding out what T is.

    Not sure if it is ok to return an interface rather than a type.

    0 讨论(0)
  • 2020-12-16 04:36

    It's an old question and even though the accepted answer is totally correct, I stumpled upon this in the search for a similiar problem and thought I could share my experiences. It's often a headache, but it is possible to use generics combined with interfaces with WCF. Here's a working example of another (similiar) implementation that I've done:

    [ServiceContract]
    [ServiceKnownType(typeof(CollectionWrapper<IAssociation>))]
    public interface IService : 
    {
        [OperationContract]
        ICollectionWrapper<IAssociation> FindAssociation(string name, int pageSize, int page);
    }
    
    public interface ICollectionWrapper<TModel>
    {
        int TotalCount { get; set; }
        IEnumerable<TModel> Items { get; set; }
    }
    
    [KnownType(typeof(OrganizationDto))]
    [KnownType(typeof(CompanyDto))]
    public class CollectionWrapper<TModel> : ICollectionWrapper<TModel>
    {
        [DataMember]
        public int TotalCount { get; set; }
        [DataMember]
        public IEnumerable<TModel> Items { get; set; }
    }
    
    public class CompanyDto :  IAssociation
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    public class OrganizationDto :  IAssociation
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    

    The key here is to use the combination of KnownType and ServiceKnownType.

    So in your case you can do something like this:

    [ServiceContract]
    [ServiceKnownType(typeof(ActionParameters))]
    [ServiceKnownType(typeof(ActionResult<ISportProgram>))] // Actual implementation of container, but interface of generic.
    public interface ISportProgramBl  
    {
        [OperationContract]
        IActionResult<ISportProgram> Get(IActionParameters parameters);
    }
    
    [KnownType(typeof(SportProgram))] // Actual implementation here.
    public class ActionResult<T>
    {
        // Other stuff here
        T FooModel { get; set; }
    }
    

    This will work if you have a shared contract (access to the actual service interface) and consume the contract with ChannelFactory<ISportProgramBl>. I do not know if it works with a service-reference.

    However, there seems to be some issues with the implementation as mentioned here:

    WCF With a interface and a generic model

    And another similiar question asked and answered here:

    Generic return types with interface type params in WCF

    0 讨论(0)
  • 2020-12-16 04:44

    Well, I think this is another case of the SOA vs. OOP "impedance mismatch". The two world are quite separate.

    In WCF, all that is being passed from the client to the server is passed as serialized messages - no references are being used.

    This means: everything you want to serialize on the client, send it across to the server, and deserialize it and use it there, must be concrete - you cannot pass around interfaces, you cannot use "non-resolved" generics - you need to spell it out. Basically, all that's being passed from client over the wire to the server must be expressable in XML schema.

    This has lots of implications:

    • no interfaces - you cannot pass around interfaces - you need to work with concrete types
    • no "automatic" inheritance - you cannot just define a base class and pass around derived classes based on it - those need to be specificied too (that's what the ServiceKnownType attribute is for)
    • no automatic generics - again, you need to use concrete types instead

    This may sound like a lot of restrictions - but it's because WCF is using all message-based communication - it cannot deal with refereces, inheritance, generics etc. - you need to spell it out.

    So I don't really have an answer for you per se - I just think you need to rethink your strategy and change the way your client and server are exchanging information over WCF.

    Marc

    PS: I did some more research, and contrary to all my understanding, there seems to be a way to serialize anything that's based on an interface and/or abstract base class across the wire, as long as you can be sure it's always only .NET on either end of the wire (i.e. it's not interoperable with e.g. Java).

    See Aaron Skonnard blog post on the NetDataContractSerializer and another blog post and yet another showing how to use the NetDataContractSerializer to be able to pass around things like IPerson as parameters to your methods.

    0 讨论(0)
提交回复
热议问题