PCOO类未从WCF返回

时间:2014-03-23 04:27:05

标签: c# wcf entity-framework poco entity-framework-6

我有3个项目:

  1. 我正在使用EF 6.1的DAL类库
  2. WCF服务库
  3. WPF客户端应用
  4. WPF应用程序具有对WCF项目的服务引用。我遇到的问题是当我尝试这样的事情时:

    public List<DbTable> GetItems()
    {
        try
        {
            IQueryable<DbTable> items;
            using (var dbContext = new MyEntities())
            {                                        
                items = dbContext.Items.Select(a => a);
                return items.ToList();  
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
            return null;
        }
    }
    

    WCF服务未返回任何结果,而我收到的通用HTTP未正确设置错误消息。

    所以在阅读完之后我决定在WCF服务中创建POCO类,这很有用。

    using (var dbContext = new MyEntities())
    {
       var items = dbContext.Items.Select(a =>
           new MyPocoClass
           {
               ItemId = a.ItemId,
               Name = a.Name
           });
       return items.ToList();
    }
    

    我的问题是,我是否可以不返回实体框架对象而不创建POCO类来来回传送数据?如果我可以,我错过了一步或什么?

    当我尝试返回任何EF对象时,这是消息

    喜欢return dbContext.Items.ToList();

    我能让它发挥作用的唯一方法是使用POCO类。

    enter image description here

    更新

    这是我的服务配置文件:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.serviceModel>
        <services>
          <service name="MyProject.WebService.MyWebService">
            <endpoint address="" binding="basicHttpBinding" contract="MyProject.WebService.IMyWebService">
              <identity>
                <dns value="localhost" />
              </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8733/Design_Time_Addresses/MyProject.WebService/MyWebService/" />
              </baseAddresses>
            </host>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" />
              <serviceDebug includeExceptionDetailInFaults="True" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    </configuration>
    

    我的客户:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.serviceModel>
        <bindings>
          <basicHttpBinding>
            <binding name="BasicHttpBinding_IMyWebService" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"/>
          </basicHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:8733/Design_Time_Addresses/MyProject.WebService/MyWebService/" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyWebService" contract="ServiceReference.IMyWebService" name="BasicHttpBinding_IMyWebService" />
        </client>
      </system.serviceModel>
    </configuration>
    

    重新更新 -

    Inner Exception:
    Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
       at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
       at System.Net.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
    

3 个答案:

答案 0 :(得分:3)

我认为您应该保留DTO层(POCO),因为当您的模型变得更复杂时,如果您尝试直接返回实体(根据您的数据结构),延迟加载会导致各种问题。例如,如果您有一个相当标准的关系设置,如:

  • 一个客户有很多联系人
  • 一个联系人有很多订单
  • 一个订单包含多个订单项
  • 单行项目有许多商店库存水平记录
  • One Line Item有很多照片
  • public class Client
    {
      public int ClientId { get; set; }
      public string ClientName { get; set; }
      public IList<Contact> Contacts { get; set; }
    }
    
    public class Contacts
    {
      public int ContactId { get; set; }
      public string ContactName { get; set; }
      public IList<Order> Order { get; set; }
    }
    

当您尝试通过WCF边界传递Client对象时,序列化程序将触摸实体中的每个属性并加载相关实体(联系人,订单,行项目,存储库存,Photograhs)。

这可能导致大量数据加载,或由于循环引用而失败。

如果禁用Lazy Loading,那么您的对象就会出现空值,但是还有很多其他原因不能传递实体,例如在更改数据库结构等时不需要重建客户端应用程序。

如果您发现制作DTO - &gt;实体 - &gt; DTO映射耗时,看看AutoMapper,它可以做到这一点。

我发现使用AutoFixture和DeepEqual为此映射编写单元测试很有帮助。

答案 1 :(得分:2)

发生此问题是因为实体框架返回的动态代理对象具有从原始类型派生并在运行时创建的类型。要解决此问题,您应该创建 ProxyDataContractResolver 属性:

public class ApplyProxyDataContractResolverAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(
                  OperationDescription description,
                  BindingParameterCollection parameters)
    {
    }

    public void ApplyClientBehavior(
                  OperationDescription description,
                  ClientOperation proxy)
    {

        var dataContractSerializerOperationBehavior =
            description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
    }

    public void ApplyDispatchBehavior(
                   OperationDescription description,
                   DispatchOperation dispatch)
    {
        var dataContractSerializerOperationBehavior =
            description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        dataContractSerializerOperationBehavior.DataContractResolver =
            new ProxyDataContractResolver();
    }

    public void Validate(OperationDescription description)
    {
    }
}

然后将此属性应用于您的服务方法,例如:

[ApplyProxyDataContractResolver]
public List<DbTable> GetItems()
{
// query to database with EF here
}

但是,我的建议是使用DTO概念。使用DTO,您可以将胖模型拆分为一些小型模型,仅传输客户需要的信息。这使您的架构更易于管理,并减少了通过网络传输的数据量。

答案 2 :(得分:1)

为什么你必须完成所有配置?您只需创建一个不同类型的新Dto,并在那里分配所有数据。然后返回你的新类型的dto,它完美地工作。

public class MyFirstTypeDto
{
public string Surname { get;set; }
public string Name{ get;set; }
public string FullName {get { Name + " " +Surname }}

这样做:

public class MySecondTypeDto
{
public string Surname { get;set; }
public string Name{ get;set; }
public string FullName  {get; set;}

var NonReturningPocoDto = new MyFirstTypeDto();
NonReturningPocoDto.Name = "Robbie";
NonReturningPocoDto.Surname = "Williams";

 var ReturningPocoDto = new MySecondTypeDto();
ReturningPocoDto.Name = NonReturningPocoDto.Name;
ReturningPocoDto.Surname = NonReturningPocoDto.Surname;
ReturningPocoDto.FullName  = NonReturningPocoDto.FullName;

现在您可以返回ReturningPocoDto,它将完美运行!希望它有所帮助。