【问题标题】:Map derived properties to serialized column in database将派生属性映射到数据库中的序列化列
【发布时间】:2014-01-08 17:18:12
【问题描述】:

我有以下课程:

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 等)都可以做到这一点吗?

【问题讨论】:

标签: c# sql entity-framework nhibernate orm


【解决方案1】:

通过 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);
    }
}

【讨论】:

    【解决方案2】:

    我想说,解决方案应该分成两部分。 1)序列化的东西的映射和2)序列化。我正在使用 NHibernate。 这种情况实际上与存储 1)文件路径 2)在共享上查找文件的更常见问题非常相似。

    映射。这是微不足道的。只需介绍新的属性ExtraInfo,可以在基础级别

    public virtual string ExtranInfo { get; set; }
    

    不管使用什么 ORM 工具,我们都会简单地将其映射到一个 nvarchar 列 ExtraInfo。

    继承也很简单,对于NHibernate我们可以使用8.1.1. Table per class hierarchy(我忍不住,xml映射更具可读性,但fluent也会这样做)

    <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世界中的Intercepting综合讲解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/拦截器来管理它。

    阅读“动态列/属性”如何在 NHibernate 中工作可能会很有趣:NHibernate Dynamic Columns Number

    【讨论】:

    • 感谢您的回答 - 非常全面。我认为我将使用的最终解决方案将是您和 Mika 的答案的混合。感谢您的帮助。
    • 如果有帮助就太好了 ;) 祝 EF 好运(至少开始考虑 NHiberante;)
    • 我想我在这之后是一个皈依者;)
    猜你喜欢
    • 2011-06-30
    • 2023-04-07
    • 1970-01-01
    • 2021-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-13
    相关资源
    最近更新 更多