sno*_*FFF 17 c# asp.net entity-framework asp.net-web-api
我有一个包含Web API OData服务层的多租户应用程序.我有一个新的要求来支持自定义字段,这对每个租户都是唯一的,并且在我的表中添加通用的"customfield01","customfield02"列不够灵活.
我已经探索了许多方法来描述和保留后端的自定义数据,但更具挑战性的部分似乎是扩展我的odata服务,以不同的方式为每个租户包含自定义字段.
以下链接使用Web API描述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)
但同样,问题变成了如何用我的数据上下文中的数据填充这些对象.再一次,我认为我可以有一个视图来取消列,但这不起作用,因为我可以为每个动态属性提供不同的数据类型(对我来说很重要).
所以,我真的有几个问题:
就我所尝试的而言 - 许多不起作用的东西,但我已经解决了以下问题(假设没有更好的方法):
每个"可扩展"实体的基础POCO,每个实体都有一个单独的"扩展"实体(如上面的模型)
在后端,由于我需要无限的灵活性和强大的数据类型,我计划为每个租户/实体组合设置一个单独的扩展表(将命名为[TenantId].[ItemExtension],每个列都命名并根据需要键入) .
我缺少的是我的数据和模型之间的所有内容.任何帮助将不胜感激.
现在,在他的缓存数据错误之后,我不再使用实体框架。看看 Fluent NHibernate。在 ORM 中,您可以调整 OData v4 动态属性到用户类型的映射。使用 nuget 包 Newtonsoft.Json。
\n\n你的班:
\n\npublic 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\nFluent NHibernate 类的 StoreDynamicProperties 自定义类型:
\n\nusing 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\nusing 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
归档时间: |
|
查看次数: |
1328 次 |
最近记录: |