使用 linq to sql 后端通过 WCF 查询 DTO 对象

Posted

技术标签:

【中文标题】使用 linq to sql 后端通过 WCF 查询 DTO 对象【英文标题】:Query DTO objects through WCF with linq to sql backend 【发布时间】:2012-04-06 01:58:37 【问题描述】:

我正在开展一个项目,我们需要针对该项目创建复杂的查询 WCF 服务。

该服务在后端使用 linq to sql,并将查询投射到数据传输对象,如下所示:

dbContext.GetQueryable() .Where(x => x.Id == formatId) .Select(x => FormatHelper.PopulateMSFormat(x)) .ToList();

我想做的是在客户端指定一个查询,假设我想查询具有某个属性或其中几个属性的所有格式。 这种风格的东西:

var assets = client.QueryForAssets().Where(x => (x.name == "Test" || x == "Arne") && x.doe == "john");

我知道我无法通过 WCF 返回 IQueryable,但可以使用 OData 服务完成类似的操作。问题是我必须返回 DTO,而 OData 让我很容易绑定到 L2S-datacontext,它公开了我的数据模型而不是 DTO。

那么有没有一种好的方法可以对 DTO 的查询进行序列化,从而有效地传播到 l2s 层?

我曾考虑编写自己的查询语言,但我发现构建正确的表达式树作为 l2s 的谓词非常困难,因为没有从 DTO 到 linq 类的映射。

【问题讨论】:

【参考方案1】:

使用OData 服务,您不必直接返回数据库实体。您可以简单地以可查询的格式返回任何DTO。然后借助 LINQ 的 Select() 方法,您可以在提供查询之前将任何数据库实体简单地转换为 DTO

public class DataModel

  public DataModel()
  
    using (var dbContext = new DatabaseContext())
    
      Employees = from e in dbContext.Employee
                  select new EmployeeDto
                  
                    ID = e.EmployeeID,
                    DepartmentID = e.DepartmentID,
                    AddressID = e.AddressID,
                    FirstName = e.FirstName,
                    LastName = e.LastName,
                    StreetNumber = e.Address.StreetNumber,
                    StreetName = e.Address.StreetName
                  ;
    
  

  /// <summary>Returns the list of employees.</summary>
  public IQueryable<EmployeeDto> Employees  get; private set; 

您现在可以像这样轻松地将其设置为 OData 服务:

public class EmployeeDataService : DataService<DataModel>

关于完整的实现细节,see this关于这个主题的优秀文章。一旦你掌握了 OData 服务,它们实际上非常非常强大。

【讨论】:

这听起来像是一个很好的解决方案,但我无法让它发挥作用。我怀疑这与我使用 L2S 而不是 EF 的事实有关 发现至少两个问题。我不能使用辅助方法,并且由于上下文包含在使用中,因此将在查询集合时释放它。 使用 L2S 或 EF 没关系。您可以汇集来自任何来源的数据。由于 OData 服务对象是“按请求”创建的,因此使用 using 语句是正确的方法,因为 DataModel 类实例将随请求一起创建和销毁。 我不同意 using 语句,一旦 datacontext 超出范围,即在调用 Get() 之前,将调用 dispose 方法。【参考方案2】:

我相信您可以使用 OData 服务返回 DTO。

看看http://www.codeproject.com/Articles/135490/Advanced-using-OData-in-NET-WCF-Data-Services。特别是“公开数据库的转换”部分。您可以将实体对象展平为 DTO,并让客户端针对此 DTO 模型运行查询。

这是你要找的东西吗?

【讨论】:

【参考方案3】:

如果您有很长的复杂实体,那么手动创建投影就是一场噩梦。 Automapper 不起作用,因为 LINQ 不能将它与 IQueryable 结合使用。

这是完美的解决方案:Stop using AutoMapper in your Data Access Code

它将为您“神奇地”生成一个投影,并使您能够根据您的 DTO(数据传输对象)类运行 oData 查询。

    [Queryable]
    public IQueryable<DatabaseProductDTO> GetDatabaseProductDTO(ODataQueryOptions<DatabaseProductDTO> options)
    
        // _db.DatabaseProducts is an EF table 
        // DatabaseProductDTO is my DTO object
        var projectedDTOs = _db.DatabaseProducts.Project().To<DatabaseProductDTO>();

        var settings = new ODataQuerySettings();
        var results = (IQueryable<DatabaseProductDTO>) options.ApplyTo(projectedDTOs, settings);

        return results.ToArray().AsQueryable();
    

我用

运行这个
/odata/DatabaseProductDTO?$filter=FreeShipping eq true

注意:这篇文章是几年前的文章,现在AutoMapper 可能已经内置了这样的功能。我只是没有时间我现在自己检查一下。上述参考文章的灵感来自 AutoMapper 本身的作者 this article - 所以现在可能包含它的一些改进版本。总体概念似乎很棒,这个版本对我来说效果很好。

【讨论】:

以上是关于使用 linq to sql 后端通过 WCF 查询 DTO 对象的主要内容,如果未能解决你的问题,请参考以下文章

在数据访问应用程序块之上使用“LINQ to SQL”和“WCF 数据服务”

无法通过 Safari 使用 Kerberos 约束委派通过 Web 应用程序访问后端 WCF 服务

winform 用linq to SQL 类实现数据库的增删改查 2016年02月18日

为啥,当我在 WCF 服务中模拟时,当我尝试运行 LINQ to SQL 查询时,我的服务不能加载 System.Transactions?

How To Easily Call WCF Services Properly z

Translate this app.config xml to code? (WCF) z