F#,使用 WebAPI 序列化动态生成的对象
F#, Serialize dynamically generated objects with WebAPI
我正在尝试在 F# 中创建 Web API 控制器,其中 returns 对象来自 Entity Framework。 SharpObject 和 SharpContext 分别是我在一个c#项目中定义的对象和DbContext。
/// Retrieves values.
[<RoutePrefix("api2/values")>]
type ValuesController() =
inherit ApiController()
let values = [| "value1"; "value2" |]
/// Gets all values.
[<Route("")>]
member x.Get() : IEnumerable<SharpObject> =
use context = new SharpContext()
context.SharpObjects.ToList() :> IEnumerable<SharpObject>
这是带有 SerializableAttribute 的 SharpObject。
[Serializable]
public class SharpObject
{
[Key]
public virtual int Id { get; set; }
public virtual string Description { get; set; }
}
我收到的错误是:
The type System.Data.Entity.DynamicProxies.SharpObject_3A697B5C46C0BF76858FEAFC93BFED36DD8D4CA2CEACBB178D2D3C38BB2D2052 was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
当我使用 ILSpy 反编译它时,它看起来像这样:
[Route("")]
public IEnumerable<SharpObject> Get()
{
SharpContext context = new SharpContext();
IEnumerable<SharpObject> result;
try
{
result = (IEnumerable<SharpObject>)context.SharpObjects.ToList<SharpObject>();
}
finally
{
IDisposable disposable = context as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
return result;
}
让我的列表在 f# 中显示的最佳方法是什么?
发生这种情况是因为您从 EF 获得的对象 不是 ,实际上是 SharpObject
类型,而是那种可怕命名的类型,从SharpObject
继承。这种类型称为"proxy",由EF动态生成,以提供某些服务(例如延迟加载,见下文)。
因为您的操作被声明为返回 IEnumerable<SharpObject>
,默认 WebAPI 的 XML 序列化程序期望找到该类型的对象,因此在找到不同类型的对象时正确地抱怨。
您可以尝试的一种临时的创可贴式修复方法是从您的实体中删除 virtual
关键字(无论如何,为什么要将它们放在那里?)。 virtual
关键字的存在导致 EF 生成代理类型。缺少 virtual
,将不会生成任何代理,从而使 XML 序列化器快乐。
但是,一旦您扩展模型以包含具有延迟加载的导航属性,这将不起作用。这些属性,你必须设为虚拟,否则延迟加载将无法工作。
所以正确的修复是不要对面向数据库的 DTO 和面向客户端的 DTO 使用相同的类型。使用不同的类型。
为这两个目的使用相同的类型乍一看似乎 "convenient",但这条路很快会导致许多问题。您已经发现的小技术问题之一。但即使没有这些,从概念上讲,您几乎永远也不想直接将您的数据库记录提供给不受信任的用户。一些可能的后果包括安全漏洞、分解错误的 UI 代码、分解错误的数据库结构、性能问题等等。
坏主意。别这样。
P.S。这实际上与 F# 没有任何关系。
我正在尝试在 F# 中创建 Web API 控制器,其中 returns 对象来自 Entity Framework。 SharpObject 和 SharpContext 分别是我在一个c#项目中定义的对象和DbContext。
/// Retrieves values.
[<RoutePrefix("api2/values")>]
type ValuesController() =
inherit ApiController()
let values = [| "value1"; "value2" |]
/// Gets all values.
[<Route("")>]
member x.Get() : IEnumerable<SharpObject> =
use context = new SharpContext()
context.SharpObjects.ToList() :> IEnumerable<SharpObject>
这是带有 SerializableAttribute 的 SharpObject。
[Serializable]
public class SharpObject
{
[Key]
public virtual int Id { get; set; }
public virtual string Description { get; set; }
}
我收到的错误是:
The type System.Data.Entity.DynamicProxies.SharpObject_3A697B5C46C0BF76858FEAFC93BFED36DD8D4CA2CEACBB178D2D3C38BB2D2052 was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
当我使用 ILSpy 反编译它时,它看起来像这样:
[Route("")]
public IEnumerable<SharpObject> Get()
{
SharpContext context = new SharpContext();
IEnumerable<SharpObject> result;
try
{
result = (IEnumerable<SharpObject>)context.SharpObjects.ToList<SharpObject>();
}
finally
{
IDisposable disposable = context as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
return result;
}
让我的列表在 f# 中显示的最佳方法是什么?
发生这种情况是因为您从 EF 获得的对象 不是 ,实际上是 SharpObject
类型,而是那种可怕命名的类型,从SharpObject
继承。这种类型称为"proxy",由EF动态生成,以提供某些服务(例如延迟加载,见下文)。
因为您的操作被声明为返回 IEnumerable<SharpObject>
,默认 WebAPI 的 XML 序列化程序期望找到该类型的对象,因此在找到不同类型的对象时正确地抱怨。
您可以尝试的一种临时的创可贴式修复方法是从您的实体中删除 virtual
关键字(无论如何,为什么要将它们放在那里?)。 virtual
关键字的存在导致 EF 生成代理类型。缺少 virtual
,将不会生成任何代理,从而使 XML 序列化器快乐。
但是,一旦您扩展模型以包含具有延迟加载的导航属性,这将不起作用。这些属性,你必须设为虚拟,否则延迟加载将无法工作。
所以正确的修复是不要对面向数据库的 DTO 和面向客户端的 DTO 使用相同的类型。使用不同的类型。
为这两个目的使用相同的类型乍一看似乎 "convenient",但这条路很快会导致许多问题。您已经发现的小技术问题之一。但即使没有这些,从概念上讲,您几乎永远也不想直接将您的数据库记录提供给不受信任的用户。一些可能的后果包括安全漏洞、分解错误的 UI 代码、分解错误的数据库结构、性能问题等等。
坏主意。别这样。
P.S。这实际上与 F# 没有任何关系。