Pergunta

Eu tenho o seguinte:

[ServiceContract]
[ServiceKnownType(typeof(ActionParameters))]
[ServiceKnownType(typeof(SportProgram))]
[ServiceKnownType(typeof(ActionResult<SportProgram>))]
public interface ISportProgramBl  
{
    [OperationContract]
    IActionResult<ISportProgram> Get(IActionParameters parameters);
}

Quando executo o método Get recebo o seguinte erro:

Ocorreu um erro ao tentar serializar o parâmetro http://tempuri.org/:GetResult.A mensagem InnerException era 'Type 'PPS.Core.DomainModel.Support.Action.ActionResult`1[ [PPS.Core.DomainModel.SportProgram.ISportProgram, PPS.Core.DomainModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken= null]]' com nome de contrato de dados 'ActionResultOfanyType: http://schemas.datacontract.org/2004/07/PPS.Core.DomainModel.Support.Action' não é esperado.Adicione quaisquer tipos não conhecidos estaticamente à lista de tipos conhecidos - por exemplo, usando o atributo KnownTypeAttribute ou adicionando-os à lista de tipos conhecidos passados ​​para DataContractSerializer.'.Consulte InnerException para obter mais detalhes.

A partir deste erro, posso ver que ele pode resolver o ActionResult, mas não consegue resolver o ISportProgram, mesmo que eu tenha ServiceKnownType(typeof(ActionResult < SportProgram >)) na minha interface de serviço...

Observe que este stub de referência gerado se parece com isto, então posso ver que os tipos conhecidos estão sendo transmitidos corretamente:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="SportProgramStb.ISportProgramBl")]
public interface ISportProgramBl {

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISportProgramBl/Get", ReplyAction="http://tempuri.org/ISportProgramBl/GetResponse")]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.SportProgram.SportProgram))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionParameters))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(PPS.Core.DomainModel.Support.Action.ActionResult<PPS.Core.DomainModel.SportProgram.SportProgram>))]
    object Get(object parameters);
}

Por que isso está dando errado????Observe que ele está passando pelo serviço WCF corretamente ...mas lança a exceção quando o resultado é retornado.

Por último, ActionResult se parece com isto:

public interface IActionResult<T> 
{
    T Result { get; set; } 
}

Obrigada Anthony

Foi útil?

Solução

Bem, acho que este é outro caso de SOA vs.OOP "incompatibilidade de impedância".Os dois mundos são bastante separados.

No WCF, tudo o que está sendo passado do cliente para o servidor é passado como mensagens serializadas - nenhuma referência está sendo usada.

Isso significa:tudo o que você deseja serializar no cliente, enviá-lo para o servidor, desserializá-lo e usá-lo lá, deve ser concreto - você não pode passar interfaces, não pode usar genéricos "não resolvidos" - você precisa explicar isso.Basicamente, tudo o que está sendo passado do cliente para o servidor deve ser expresso no esquema XML.

Isso tem muitas implicações:

  • sem interfaces - você não pode passar interfaces - você precisa trabalhar com tipos concretos
  • sem herança "automática" - você não pode simplesmente definir uma classe base e transmitir classes derivadas com base nela - elas também precisam ser especificadas (é para isso que serve o atributo ServiceKnownType)
  • sem genéricos automáticos - novamente, você precisa usar tipos concretos

Isso pode parecer muitas restrições - mas é porque o WCF está usando toda a comunicação baseada em mensagens - ele não pode lidar com referências, herança, genéricos, etc.- você precisa soletrar.

Então, eu realmente não tenho uma resposta para você - só acho que você precisa repensar sua estratégia e mudar a maneira como seu cliente e servidor trocam informações pelo WCF.

Marco

PS:Fiz mais algumas pesquisas e, ao contrário de todo o meu entendimento, parece haver uma maneira de serializar qualquer coisa que seja baseada em uma interface e/ou classe base abstrata, desde que você tenha certeza de que é sempre apenas .NET em qualquer extremidade do fio (ou seja,isso é não interoperável com, por ex.Java).

Ver Postagem do blog Aaron Skonnard no NetDataContractSerializer e outro postagem no blog e Ainda outra mostrando como usar o NetDataContractSerializer para poder transmitir coisas como IPerson como parâmetros para seus métodos.

Outras dicas

Esse é um dos problemas que resolvo com ServiceStack.NET - Minha estrutura de serviços da Web .NET e MONO de código aberto.

Service Stack foi fortemente influenciado por Padrão de objeto de transferência de dados de Martin Fowlers pois permite que você simplesmente use DTOs para definir seus serviços da web - ou seja,do jeito SOA :).

Eu evito essa limitação inerente ao WCF gerando meus próprios WSDLs que se comportam como você esperaria.Como benefício de substituir a configuração complexa do WCF/modelo ServiceContract - Os serviços web SOAP também funcionam em MONO - veja a demonstração ao vivo.

É uma pergunta antiga e embora a resposta aceita esteja totalmente correta, me deparei com isso na busca por um problema semelhante e pensei que poderia compartilhar minhas experiências.Muitas vezes é uma dor de cabeça, mas é possível usar genéricos combinados com interfaces com WCF.Aqui está um exemplo prático de outra implementação (semelhante) que fiz:

[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; }
}

A chave aqui é usar a combinação de KnownType e ServiceKnownType.

Então no seu caso você pode fazer algo assim:

[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; }
}

Isso funcionará se você tiver um contrato compartilhado (acesso à interface de serviço real) e consumir o contrato com ChannelFactory<ISportProgramBl>.Não sei se funciona com referência de serviço.

No entanto, parece haver alguns problemas com a implementação, conforme mencionado aqui:

WCF Com uma interface e um modelo genérico

E outra pergunta semelhante feita e respondida aqui:

Tipos de retorno genéricos com parâmetros de tipo de interface no WCF

Você está retornando um IList de T.Talvez o sistema tenha problemas para descobrir o que é T.

Não tenho certeza se posso retornar uma interface em vez de um tipo.

Você está especificando interfaces nos objetos e não tipos concretos?

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

Editar:

O que estou dizendo é que todos os tipos concretos que você está passando nos genéricos (inclusive no subobjeto via interfaces) estão sendo passados ​​na lista de Tipos Conhecidos.Tivemos problemas de serialização em que todos os tipos não eram conhecidos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top