OData 服务不返回自定义对象
这是我想要做的:
我有一个现有的数据库,我需要从其中获取数据。
我使用实体框架创建了数据协定以将它们映射到相应的表。在我的示例中,我有三个表 - tblOrder、tblProduct 和 tblCustomer。因此,我创建了三个数据契约来映射到这些表。实体框架注释已添加到数据协定中。我编写了一个单元测试来测试我的数据契约。通过直接实体框架调用,一切都按预期工作。请参阅下面的单元测试 1。
为每个数据协定添加了 DataServiceKey 注释。并通过添加订单上下文和数据服务 svc 用 OData 服务包装它们。请参阅下文。
编写了一个单元测试以通过 OData 服务访问一些现有订单数据。问题:OData 服务仅返回 Order 对象上的非自定义数据类型。它在客户数据类型字段(例如 CustomerInfo 和 ProductList)上返回 null。
我做错了什么吗?为了使 OData 调用能够处理使用 EF 进行检索的对象,我需要做些什么特别的事情吗?
[DataServiceKey("ID")]
[Table("tblOrder", Schema = "schOnlineSale")]
public class Order
{
[Column("OrderId"), Key]
public int ID { get; set; }
public string OrderName { get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer CustomerInfo { get; set; }
[ForeignKey("OrderId")]
public virtual ICollection<Product> ProductList{ get; set; }
}
[DataServiceKey("CustomerId")]
[Table("tblCustomer", Schema = "schOnlineSale")]
public class Customer
{
[Key]
public int CustomerId{ get; set; }
[Column("FullName")]
public string Name { get; set; }
public string MailingAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZIPCode { get; set; }
}
[DataServiceKey("ProductId")]
[Table("tblProduct", Schema = "schOnlineSale")]
public class Product
{
[Key]
public int ProductId{ get; set; }
public int OrderId{ get; set; }
public string ProductName { get; set; }
public decimal SalePrice { get; set; }
}
//OData Service wrapper classes
public class OrderContext: DbContext
{
public DbSet<Order> Orders {get; set;}
public DbSet<Product> Products {get; set;}
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class OrderDataService : DataService<OrderContext>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Orders", EntitySetRights.All);
config.SetEntitySetAccessRule("Products", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.UseVerboseErrors = true;
}
protected override void HandleException(HandleExceptionArgs args)
{
try
{
args.UseVerboseErrors = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
protected override OrderContext CreateDataSource()
{
var dataSource = new OrderContext();
dataSource.Configuration.ProxyCreationEnabled = false;
return dataSource;
}
}
//Unit Test 1 - Querying via data context directly
public void EFDataContext_GetOrderTest()
{
var context = new OrderContext();
var list = (from a in context.Orders
where a.OrderId == 10 ||
a.OrderId == 15 ||
a.OrderId== 20 select a).ToList();
//CustomerInfo and ProductList values are populated properly
}
//Unit test 2 - Querying via OData Service
public void OData_GetOrderTest()
{
string uriString = "http://localhost/OrderServices/OrderDataService.svc/Orders(10)";
Uri serviceUri = new Uri(uriString);
OrderContext context = new OrderContext(serviceUri);
context.IgnoreResourceNotFoundException = true;
var orderList = context.Orders;
foreach (Order s in orderList)
{
TestContext.WriteLine("OrderId=" + s.ID);
Assert.IsNotNull(s.CustomerInfo); //CustomerInfo is not returned with OData call
//but it's returned properly with entity framework call.
foreach (Product p in s.ProductList)
{
//ProductList is also not returned with OData Service call,
//but it is returned properly when accessing through Entity Framework calls
//directly
}
}
}
//app.config
<configuration>
<connectionStrings>
<add name="OrderContext" connectionString="a valid connection string" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Here is what I want to do:
I have an existing database from where I need to get data.
I created data contracts to map them to respective tables by using Entity Framework. In my example, I have three tables - tblOrder, tblProduct and tblCustomer. So I created three data contracts to map to these tables. Entity Framework annotations were added to data contratcs. I wrote a unit test to test out my data contracts. Everything works as expected with direct Entity Framework calls. See unit test 1 below.
Added DataServiceKey annotation to each data contract. And wrap them with OData service by adding an order context and data service svc. See below.
Wrote a unit test to access some existing order data via OData sevice. Problem: OData service only returns non-custom data types on Order object. It returns null on custome data type fields, such as CustomerInfo and ProductList.
Am I doing something wrong? Is there anything special that I have to do for OData calls to work on objects using EF for retrieval?
[DataServiceKey("ID")]
[Table("tblOrder", Schema = "schOnlineSale")]
public class Order
{
[Column("OrderId"), Key]
public int ID { get; set; }
public string OrderName { get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer CustomerInfo { get; set; }
[ForeignKey("OrderId")]
public virtual ICollection<Product> ProductList{ get; set; }
}
[DataServiceKey("CustomerId")]
[Table("tblCustomer", Schema = "schOnlineSale")]
public class Customer
{
[Key]
public int CustomerId{ get; set; }
[Column("FullName")]
public string Name { get; set; }
public string MailingAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZIPCode { get; set; }
}
[DataServiceKey("ProductId")]
[Table("tblProduct", Schema = "schOnlineSale")]
public class Product
{
[Key]
public int ProductId{ get; set; }
public int OrderId{ get; set; }
public string ProductName { get; set; }
public decimal SalePrice { get; set; }
}
//OData Service wrapper classes
public class OrderContext: DbContext
{
public DbSet<Order> Orders {get; set;}
public DbSet<Product> Products {get; set;}
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class OrderDataService : DataService<OrderContext>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Orders", EntitySetRights.All);
config.SetEntitySetAccessRule("Products", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.UseVerboseErrors = true;
}
protected override void HandleException(HandleExceptionArgs args)
{
try
{
args.UseVerboseErrors = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
protected override OrderContext CreateDataSource()
{
var dataSource = new OrderContext();
dataSource.Configuration.ProxyCreationEnabled = false;
return dataSource;
}
}
//Unit Test 1 - Querying via data context directly
public void EFDataContext_GetOrderTest()
{
var context = new OrderContext();
var list = (from a in context.Orders
where a.OrderId == 10 ||
a.OrderId == 15 ||
a.OrderId== 20 select a).ToList();
//CustomerInfo and ProductList values are populated properly
}
//Unit test 2 - Querying via OData Service
public void OData_GetOrderTest()
{
string uriString = "http://localhost/OrderServices/OrderDataService.svc/Orders(10)";
Uri serviceUri = new Uri(uriString);
OrderContext context = new OrderContext(serviceUri);
context.IgnoreResourceNotFoundException = true;
var orderList = context.Orders;
foreach (Order s in orderList)
{
TestContext.WriteLine("OrderId=" + s.ID);
Assert.IsNotNull(s.CustomerInfo); //CustomerInfo is not returned with OData call
//but it's returned properly with entity framework call.
foreach (Product p in s.ProductList)
{
//ProductList is also not returned with OData Service call,
//but it is returned properly when accessing through Entity Framework calls
//directly
}
}
}
//app.config
<configuration>
<connectionStrings>
<add name="OrderContext" connectionString="a valid connection string" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
OData(或在本例中为 WCF 数据服务客户端)不会延迟加载导航属性(这就是从关系创建的属性的名称)。你必须明确要求。
您可以通过 Expand 预先执行此操作,在其中您可以要求原始查询包含导航属性另一侧的实体。只需将 .Expand("Orders") 添加到您的查询中即可获取订单实体以及客户。
您也可以稍后通过 context.LoadProperty 方法执行此操作。
OData (or in this case WCF Data Services client) doesn't lazy load navigation properties (that's what the properties created from relationships are called). You have to ask for it explicitely.
You can do this either up front, through Expand, where you can ask the original query to include the entities on the other side of navigation property. Just add .Expand("Orders") to your query to get the order entities as well as the customers.
You can also do this later through the context.LoadProperty method.