EF& Web API 2多次往返数据库以填充对象模型

时间:2015-10-25 17:05:38

标签: c# entity-framework

似乎在我面前遇到了这个问题,但我没有在网上找到太多帮助,可能是因为我真的不知道要搜索什么。

我的问题很简单,就是我有一个数据库表。该表有5个键到其他表。

然后我有一个代表EF中这个表的模型。当然,表示db表的此对象具有List<T>属性,这些属性是db中外键的表示形式。这似乎与具有此表格表示的EF模型以及其他模型的List<T>属性一样多。

我遇到的问题是,调用存储过程来填充主模型会强制对db进行额外调用以填充相关的List<T>模型。

我希望通过消除多次通话来提高性能。

我唯一想到的是修改存储过程以返回多个记录集,并将每个List<T>属性与其对应的记录集匹配。

我的消毒结构是这样的。

DB:

sql_Id           Int            PK
sql_Status       Int            FK
sql_Reason       Int            FK
sql_GuestId      Int
sql_Name         varchar
sql_Created      DateTime
sql_Original     Int            FK

EF:

public class OrderHeader : ClassBase
{
    public OrderHeader()
    {
        TaskCodeAssignments = new List<OrderHeaderTaskCodeAssignment>();
        StatusReasonCode = new OrderHeaderStatusReasonCode();
        StatusCode = new OrderHeaderStatusCode();
        Links = new OrderHeaderLinks();
    }

    public int OrderHeaderID { get; set; }
    public short OrderHeaderStatusCodeID { get; set; }
    public short? OrderHeaderStatusReasonCodeID { get; set; }
    public short? OriginatingApplicationId { get; set; }
    public string CustomerFirstName { get; set; }
    public string CustomerLastName { get; set; }
    public OrderHeaderStatusCode StatusCode { get; set; }
    public OrderHeaderStatusReasonCode StatusReasonCode { get; set; }
    public CustomerStatusCode CustomerStatusCode { get; set; }
    public ICollection<OrderHeaderTaskCodeAssignment> TaskCodeAssignments { get; set; }
}

public class OrderHeaderStatusCode
{
    public OrderHeaderStatusCode()
    {
        OrderHeaderStatusReasonCodes = new List<OrderHeaderStatusReasonCode>();
    }

    public ICollection<OrderHeaderStatusReasonCode> OrderHeaderStatusReasonCodes { get; set; }
    public virtual ICollection<OrderHeader> OrderHeader { get; set; }
}

其他自定义类型如OrderHeaderStatusReasonCode在设计上非常相似,所以我为了简洁而遗漏了。

C#Web API

public async Task<IHttpActionResult>GetOrdersHistory([FromUri]GetOrderRequestParameters orderParams)
{
    ....removed for brevity....

    var query = await TheOrderRepository.GetOrderHistory(getOrder);

}

订单存储库:

public async Task<IQueryable<OrderHeader>> GetOrderHistory(GetOrderParameters orderParams)
{
   // this is the call to stored procedure that I would modify to return multiple recordsets
   var storedProcedure = StoredProcedure.Name.MyStoredProc.ToString();

   var ordersHistory = await dbctx.Database.SqlQuery<OrderHeader>(...), storedProcParam).ToListAsync();

   // now I jump off to fill in the other properties and their data has to come from the db
   await GetOrdersData(ordersHistory, orderParams.Include);
}

private async Task GetOrdersData(List<OrderHeader> ordersHistory)
{
   if (ordersHistory != null)
   {
     await LoadOrderStatusCodeForList(ordersHistory);
     await LoadOrderStatusReasonCodeForList(ordersHistory);
     await LoadCustomerStatusCodeForList(ordersHistory);
     await LoadOrderHeaderTaskCodeAssignmentsForList(ordersHistory);
     await LoadOrderHeaderTaskCodeForList(ordersHistory);
   }
}

这些等待的大部分都是相似的,所以我只是举一个例子...

private async Task LoadOrderStatusCodeForList()
{
   ....snipped for brevity...
   await LoadOrderStatusCode(order.OrderHeaderStatusCodeID));
}

private async Task<OrderHeaderStatusCode> LoadOrderStatusCode(short orderHeaderStatusCodeId)
{
  ....snipped brevity....
  var storedProcedure = StoredProcedure.Name.MySprocStatusCode.ToString();

  return await _dbctx.Database.SqlQuery<OrderHeaderStatusCode>(...), ...).FirstOrDefaultAsync();
}

修改

关键是这个。 OrderHeader具有自定义类型的属性,基本上这些自定义类型必须填充List<T>。我目前的设计是这样的,我反复点击数据库来填充那些自定义类型列表属性。

有没有办法让一次访问db来获取我的所有信息。如前所述,我能想到的唯一方法是修改存储过程以返回多个记录集,然后将它们匹配。

BTW架构可能是个缺陷......在这种情况下,请教我如何正确填充像这样的复杂对象。

TIA

1 个答案:

答案 0 :(得分:2)

根本问题是存储过程不可组合。在SQL中,您无法使用任何内容(数据库表或其他存储过程)加入存储过程调用。因此,EF也无法做到这一点。

如果您想从数据库中获取已加载集合的数据,通常您必须使用Include。 EF会将其转换为适当的join,并找出如何从一个大结果集加载实体及其集合。但是,如上所述,联接在这里别无选择。

有一种方法可以加载multiple result sets from one stored procedure。 IMO它非常混乱,非常程序化。如果你想继续使用存储过程,我会像现在一样单独加载数据。其他人可能会建议您通过延迟加载来加载其他数据。不幸的是not as straightforward as it should beSqlQuery

另一种选择当然是开始使用常规DbSet s(Include s),但我无法判断您是否可以使用。