Web API OData V4开放类型 - 如何配置控制器和数据上下文

sno*_*FFF 17 c# asp.net entity-framework asp.net-web-api

我有一个包含Web API OData服务层的多租户应用程序.我有一个新的要求来支持自定义字段,这对每个租户都是唯一的,并且在我的表中添加通用的"customfield01","customfield02"列不够灵活.

我已经探索了许多方法来描述和保留后端的自定义数据,但更具挑战性的部分似乎是扩展我的odata服务,以不同的方式为每个租户包含自定义字段.

以下链接使用Web API描述odata v4中的"打开类型":

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/use-open-types-in-odata-v4

示例代码工作正常,并提供我的实体所需的动态属性行为.但是,代码只能使用后端的硬编码值列表.如何从实体框架数据上下文填充实体一点也不清楚.

起初,对于每个租户来说,似乎可能就像在数据库中拥有特定于租户的视图一样容易,但问题是扩展属性确实需要从列中"不显示"到键值对.因此,我想知道我是否需要一个单独的实体用于"扩展"属性.所以,我可以为我的POCO提供类似的东西:

public class Item
{
    [Key]
    public Guid ItemId { get; set; }

    public Guid TenantId { get; set; }

    // navigation property for the extension entity
    public virtual ItemExtension ItemExtension { get; set; }
}

public class ItemExtension
{
    [Key]
    public Guid ItemId { get; set; }    

    // dynamic properties for the open type
    public IDictionary<string, object> DynamicProperties { get; set; }}
}
Run Code Online (Sandbox Code Playgroud)

但同样,问题变成了如何用我的数据上下文中的数据填充这些对象.再一次,我认为我可以有一个视图来取消列,但这不起作用,因为我可以为每个动态属性提供不同的数据类型(对我来说很重要).

所以,我真的有几个问题:

  1. 上面的POCO模型对我想要完成的事情有意义吗?
  2. 我的ItemController代码应该包含所有HTTP谓词的ItemExtension(GET,POST,PUT,PATCH,DELETE)
  3. 我的数据上下文应该为ItemExtension提供什么,以允许它访问后端的扩展列
  4. 如何在后端保留扩展列以支持此功能.

就我所尝试的而言 - 许多不起作用的东西,但我已经解决了以下问题(假设没有更好的方法):

  1. 每个"可扩展"实体的基础POCO,每个实体都有一个单独的"扩展"实体(如上面的模型)

  2. 在后端,由于我需要无限的灵活性和强大的数据类型,我计划为每个租户/实体组合设置一个单独的扩展表(将命名为[TenantId].[ItemExtension],每个列都命名并根据需要键入) .

我缺少的是我的数据和模型之间的所有内容.任何帮助将不胜感激.

Fre*_*imb 0

现在,在他的缓存数据错误之后,我不再使用实体框架。看看 Fluent NHibernate。在 ORM 中,您可以调整 OData v4 动态属性到用户类型的映射。使用 nuget 包 Newtonsoft.Json。

\n\n

你的班:

\n\n
public class Item\n{\n    [Key]\n    public Guid ItemId { get; set; }\n\n    // dynamic properties for the open type\n    public IDictionary<string, object> DynamicProperties { get; set; }\n\n    ... \n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

Fluent NHibernate 类的 StoreDynamicProperties 自定义类型:

\n\n
using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Data;\nusing System.Data.Common;\nusing Newtonsoft.Json;\nusing NHibernate.UserTypes;\nusing NHibernate.SqlTypes;\n\n[Serializable]\npublic class StoreDynamicProperties : IUserType\n{\n    private JsonSerializerSettings _settings = new JsonSerializerSettings(); // { TypeNameHandling = TypeNameHandling.All };\n\n    public new bool Equals(object x, object y)\n    {\n        if (x == null && y == null)\n            return true;\n\n        if (x == null || y == null)\n            return false;\n\n        var xdocX = JsonConvert.SerializeObject((IDictionary<string, object>)x, _settings);\n        var xdocY = JsonConvert.SerializeObject((IDictionary<string, object>)y, _settings);\n\n        return xdocY == xdocX;\n    }\n\n    public int GetHashCode(object x)\n    {\n        if (x == null)\n            return 0;\n\n        return x.GetHashCode();\n    }\n\n    public object NullSafeGet(IDataReader rs, string[] names, object owner)\n    {\n        if (names.Length != 1)\n            throw new InvalidOperationException("Only expecting one column\xe2\x80\xa6");\n\n        var val = rs[names[0]] as string;\n\n        if (val != null && !string.IsNullOrWhiteSpace(val))\n        {\n            return JsonConvert.DeserializeObject<IDictionary<string, object>>(val, _settings);\n        }\n\n        return null;\n    }\n\n    public void NullSafeSet(IDbCommand cmd, object value, int index)\n    {\n        var parameter = (DbParameter)cmd.Parameters[index];\n\n        if (value == null)\n        {\n            parameter.Value = DBNull.Value;\n        }\n        else\n        {\n            parameter.Value = JsonConvert.SerializeObject((IDictionary<string, object>)value, _settings);\n        }\n    }\n\n    public object DeepCopy(object value)\n    {\n        if (value == null)\n            return null;\n\n        //Serialized and Deserialized using json.net so that I don\'t\n        //have to mark the class as serializable. Most likely slower\n        //but only done for convenience. \n\n        var serialized = JsonConvert.SerializeObject((IDictionary<string, object>)value, _settings);\n\n        return JsonConvert.DeserializeObject<IDictionary<string, object>>(serialized, _settings);\n    }\n\n    public object Replace(object original, object target, object owner)\n    {\n        return original;\n    }\n\n    public object Assemble(object cached, object owner)\n    {\n        var str = cached as string;\n\n        if (string.IsNullOrWhiteSpace(str))\n            return null;\n\n        return JsonConvert.DeserializeObject<IDictionary<string, object>>(str, _settings);\n    }\n\n    public object Disassemble(object value)\n    {\n        if (value == null)\n            return null;\n\n        return JsonConvert.SerializeObject((IDictionary<string, object>)value);\n    }\n\n    public SqlType[] SqlTypes\n    {\n        get\n        {\n            return new SqlType[] { new StringSqlType(8000) };\n        }\n    }\n\n    public Type ReturnedType\n    {\n        get { return typeof(IDictionary<string, object>); }\n    }\n\n    public bool IsMutable\n    {\n        get { return true; }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 ItemMap 类中:

\n\n
using FluentNHibernate.Mapping;\n\npublic class ItemMap : ClassMap<Item>\n{\n    public ItemMap()\n    {\n        Table("Items");\n\n        Id(item => item.ItemId)\n            .GeneratedBy\n            .GuidComb();\n\n        Map(item => item.DynamicProperties)\n            .CustomType<StoreDynamicProperties>()\n            .Column("Properties")\n            .CustomSqlType("varchar(8000)")\n            .Length(8000);\n        ...\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n