我有以下课程:
public class Base
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Alpha : Base
{
public string Foo { get; set; }
public int Bar { get; set; }
}
public class Beta : Base
{
public string Baz { get; set; }
}
我希望能够将它存储在SQL数据库中,如下所示:
Id Name Type ExtraInfo
=======================================
1 Asdf Alpha {"Foo":"hello","Bar":7}
2 Qwer Beta {"Baz":"goodbye"}
能够以合理的方式检索它,例如(伪代码):
repo.Bases.First(a => a.Type == "Alpha").Magic<Alpha>().Foo; // returns hello
...其中Magic
是一种执行映射的未知方法。
我首选的ORM是实体框架,但是,我很确定这有点超出了它的范围。
这可能与任何ORM(NHibernate,甚至EF等)有关吗?
答案 0 :(得分:4)
使用NHibernate及其UserTypes
可以实现小的调整public class Base
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Alpha : Base
{
public virtual CustomData Custom { get; set; }
public class CustomData
{
public string Foo { get; set; }
public int Bar { get; set; }
}
}
// NHibernate mapping
public class AlphaMapping : SubclassMapping<Alpha>
{
public AlphaMapping()
{
Property(a => a.Custom, m =>
{
// tell NHibernate to use custom type
m.Type<JsonType<Alpha.CustomData>>();
});
}
}
// NHibernate UserType to serialize object to json and back
public class JsonType<T> : IUserType
{
public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
var json = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
}
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(value);
NHibernateUtil.String.NullSafeSet(cmd, json, index);
}
}
答案 1 :(得分:1)
我想说,解决方案应分为两部分。 1)序列化的东西的映射和2)序列化。我正在使用NHibernate。 这种情况实际上非常类似于存储1)文件路径2)在共享上查找文件的更常见问题。
映射。这是微不足道的。只需介绍新的属性ExtraInfo
,可以在基础级别
public virtual string ExtranInfo { get; set; }
无论ORM工具如何,我们都会将其映射到nvarchar列ExtraInfo。
继承也非常简单,对于NHibernate,我们可以使用8.1.1. Table per class hierarchy (我不能帮助我自己,xml映射更具可读性,但流畅也会这样做)
<class name="Base" table="BaseTable">
<!-- common id -->
<id name="Id" generator="native" />
<!-- the column keeping the subtype selector -->
<discriminator column="Type" type="String"/>
<!-- base properties
<property name="Name" />
<property name="ExtraInfo" /> <!-- the JSON -->
<subclass name="Alpha" discriminator-value="Alpha" />
<subclass name="Beta" discriminator-value="Beta" />
<!-- ... more subclasses -->
</class>
序列化。它取决于体系结构(部分依赖于ORM工具)。我们可以使用AOP(首选解决方案)挂钩读取和写入操作来创建JSON或填充其他属性。 NHibernate支持12.1. Interceptors和12.2. Event system
Hibernate / NHibernate世界中拦截的全面解释could be found here
查询如何获取我们的Alpha:
ISession session = ... // get NHSession standard way
var result = session.Query<Base>()
.Where(b => b is Alpha)
.Take(1)
.Cast<EmployeeField>()
.ToList<EmployeeField>()
因此,在这种情况下,OnLoad
方法中的 Interceptor 可以访问所有已加载的信息,并且可以为我们完成这项工作
public override bool OnLoad(object entity, object id,
object[] state, string[] propertyNames, IType[] types)
NHibernate还有另一个不错的功能:IResultTransformer
。它可以通过这种方式注入查询处理(16. QueryOver Queries):
// not linq but QueryOver
var result = session.QueryOver<Base>()
.Where(b => b is Alpha)
// here we go
.TransformUsing(new MyTransformer())
.Take(1)
.List<Alpha>()
自定义ResultTransformer implementation的示例,基本方法的签名:
public object TransformTuple(object[] tuple, string[] aliases)
{
概要。尽管如此,将JSON存储属性并不是开箱即用的,我们可以通过简单的varchar列持久性和一些AOP / Intercept来管理它。
也许有趣的阅读“动态列/属性”如何在NHibernate中工作:NHibernate Dynamic Columns Number