将派生属性映射到数据库中的序列化列

时间:2014-01-08 17:18:13

标签: c# sql entity-framework nhibernate orm

我有以下课程:

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等)有关吗?

2 个答案:

答案 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. Interceptors12.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