使用NHibernate将位字段(FlagsAttribute)映射到多个列?

时间:2011-07-06 19:59:25

标签: .net nhibernate fluent-nhibernate

在问题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映射。)

2 个答案:

答案 0 :(得分:2)

抱歉这里没有完整答案我一直在努力在这台机器上设置NHib /流利。我采用了一种旧的自定义类型,并将其更改为符合您的状态枚举。我认为在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; }
        }

答案 1 :(得分:0)

嗯,这至少有点难看:
您可以将这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...
      }
   }
}