【问题标题】:Mapping a bit field (FlagsAttribute) to multiple columns using NHibernate?使用NHibernate将位字段(FlagsAttribute)映射到多个列?
【发布时间】:2011-07-06 19:59:24
【问题描述】:

在问题Mapping to an Enum bit flag in Nhibernate 中,接受的答案建议简单地使用整数字段来存储枚举类型的位掩码。虽然这确实有效,但我不想使用这种方法,因为它会混淆数据库中的信息。

假设我有这个枚举:

[Flags]
public enum Status
{
    None = 0,
    Foo = 1,
    Bar = 2,
    Baz = 4
}

我想用下面的模式来表示它:

CREATE TABLE Widgets
(
    Id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
    Description nvarchar(100) NOT NULL,
    -- Status fields start here
    IsFoo bit NOT NULL,
    IsBar bit NOT NULL,
    IsBaz bit NOT NULL
)

我可以使用 NHibernate 来做到这一点 - 最好没有很多丑陋的黑客吗?如果有,怎么做?

如果没有,除了在配置表中定义每个可能的标志组合之外,是否有任何替代方案可以保持数据的可发现性?

(注意:我更喜欢 Fluent NHibernate 解决方案,但我可能会混淆 XML 映射。)

【问题讨论】:

  • 你为什么要打折文章中提到的复合用户类型,我原以为它会很好地满足你的需求。
  • @Mike:你可能是对的,这可能是一个解决方案;我不可能根据那个答案来判断,它只包含一个链接,它把我带到一个专注于完全不同类型问题的页面。如果您能解释如何使这种方法适应位域,那么我很乐意接受这样的答案。

标签: .net nhibernate fluent-nhibernate


【解决方案1】:

抱歉,这里没有完整的答案我一直在努力在这台机器上设置 NHib/fluent。我采用了旧的自定义类型并对其进行了更改以满足您的状态枚举。我认为在 nHibernate 的更高版本中,一些方法接口可能已经改变,但我认为它提供了一个很好的起点。

class StatusUserType : ICompositeUserType
    {
        public object GetPropertyValue(object component, int property)
        {
            // 0 = foo
            // 1 = bar
            // 2 = baz
            Status status = (Status)component;
            if (property == 0)
            {
                return status |= Status.Foo;
            }
            else if (property == 1)
            {
                return status |= Status.Bar;
            }
            else
            {
                return status |= Status.Baz;
            }
        }

        public void SetPropertyValue(object component, int property, object value)
        {
            throw new NotImplementedException();

        }
        public new bool Equals(object x, object y)
        {
            if (x == y) return true;
            if (x == null || y == null) return false;
            return x.Equals(y);
        }


        public object NullSafeGet(System.Data.IDataReader dr, string[] names, ISessionImplementor session, object owner)
        {
            if (dr == null)
            {
                return null;
            }
            string fooColumn = names[0];
            string barColumn = names[1];
            string bazColumn = names[2];

            bool isFoo = NHibernateUtil.Boolean.NullSafeGet(dr, fooColumn, session, owner);
            bool isBar = NHibernateUtil.Boolean.NullSafeGet(dr, barColumn, session, owner);
            bool isBaz = NHibernateUtil.Boolean.NullSafeGet(dr, bazColumn, session, owner);

            Status status = (isFoo ? Status.Foo : Status.None) | (isBar ? Status.Bar : Status.None) | (isBaz ? Status.Baz : Status.None);
            return status;

        }

        public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index, ISessionImplementor session)
        {
            if (value == null)
                return;

            Status status = (Status)value;

            bool isFoo = ((status & Status.Foo) != 0);
            bool isBar = ((status & Status.Bar) != 0);
            bool isBaz = ((status & Status.Baz) != 0);


            NHibernateUtil.Boolean.NullSafeSet(cmd, isFoo, index, session);
            NHibernateUtil.Boolean.NullSafeSet(cmd, isBar, index + 1, session);
            NHibernateUtil.Boolean.NullSafeSet(cmd, isBaz, index + 2, session);
        }


        public object DeepCopy(object value)
        {
            return (Status)value;
        }


        public object Disassemble(object value, ISessionImplementor session)
        {
            return DeepCopy(value);
        }


        public object Assemble(object cached, ISessionImplementor session, object owner)
        {
            return DeepCopy(cached);
        }


        public string[] PropertyNames
        {
            get { return new string[3] { "IsFoo", "IsBar", "IsBaz" }; }
        }


        public IType[] PropertyTypes
        {
            get
            {
                return new IType[3] { NHibernateUtil.Boolean, NHibernateUtil.Boolean, NHibernateUtil.Boolean };
            }
        }

        public Type ReturnedClass
        {
            get { return typeof(Status); }
        }


        public bool IsMutable
        {
            get { return false; }
        }

【讨论】:

  • 这里有一些明确的错误,例如isXyz 测试(应该是(status & Status.Foo) != 0),但至少这看起来是正确的策略。
  • 啊,谢谢,我不经常使用 Flags,所以我只是猜测,我已经更新了答案以反映这一点。
【解决方案2】:

好吧,这至少有点难看:
您可以将这 3 列映射到实体的私有字段,然后根据这 3 个字段计算 Status 属性。
大意是:

public class Kuku
{
   private virtual bool IsFoo {get; set;}
   private virtual bool IsBar {get; set;}
   private virtual bool IsBaz {get; set;}

   public virtual Status Stat
   {
      get
      {
         Status retval = Status.None;
         if (IsFoo)
         {  
            retVal |= Status.Foo;
         }
         //etc...
      }
      set
      {
         IsFoo = value & Status.Foo;
         //etc...
      }
   }
}

【讨论】:

    猜你喜欢
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 2011-11-20
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多