C# 征求意见:对于静态值,使用枚举还是实体更好?

C# 征求意见:对于静态值,使用枚举还是实体更好?,c#,nhibernate,enumeration,entities,C#,Nhibernate,Enumeration,Entities,我正试图解决过去几个月一直困扰着我的一个难题 我的同事和我在一个技术问题上完全不同意,我想听听我们敬爱的社区对这个问题的看法 简言之: 使用枚举(可能带有“描述”等属性)还是使用实体(具有名称和描述属性)更可取 详情如下: 在我们的域模型中,我们有许多只包含Id、名称和描述的小型实体。 95%的情况下,描述与名称相等 为了便于解释,我将举一个例子:在我们的安全实体中,我们有一个AssetClass属性。 AssetClass有一个静态值列表(“权益”、“债券”等),并且不会从接口或任何其他内容更

我正试图解决过去几个月一直困扰着我的一个难题

我的同事和我在一个技术问题上完全不同意,我想听听我们敬爱的社区对这个问题的看法

简言之: 使用枚举(可能带有“描述”等属性)还是使用实体(具有名称和描述属性)更可取

详情如下: 在我们的域模型中,我们有许多只包含Id、名称和描述的小型实体。 95%的情况下,描述与名称相等

为了便于解释,我将举一个例子:在我们的安全实体中,我们有一个AssetClass属性。 AssetClass有一个静态值列表(“权益”、“债券”等),并且不会从接口或任何其他内容更改

问题是,当你想获得所有资产类别为“债券”的证券时,比如说,NHibernate必须加入AssetClass表中。。。考虑到AssetClass不是这样的唯一属性,您可以想象所有这些连接对性能的影响

我们目前的解决方案:(我不同意): 我们的代码中有一些资产类别的硬编码实例及其各自的值和Id(即Id为1的股权、Id为2的债券等),它们与数据库中的内容相匹配:

public partial class AssetClass
{
    static public AssetClass Equity = new AssetClass(1, "Equity", "Equity");
    static public AssetClass Bond = new AssetClass(2, "Bond", "Bond");
    static public AssetClass Future = new AssetClass(3, "Future", "Future");
    static public AssetClass SomethingElse = new AssetClass(4, "Something else", "This is something else");
}
我们还制作了一种特殊的NHibernate类型(如果您感兴趣,请参见下面的代码),它允许我们通过加载硬编码实例而不是去数据库获取实例来避免NHibernate进行连接:

using System;
using System.Data;
using System.Data.Common;
using NHibernate.Dialect;
using NHibernate.SqlTypes;
using NHibernate.Type;

namespace MyCompany.Utilities.DomainObjects
{
    public abstract class PrimitiveTypeBase<T> : PrimitiveType where T : class, IUniquelyNamed, IIdentifiable
    {
        private readonly PrimitiveTypeFactory<T> _factory;

        public PrimitiveTypeBase() : base(SqlTypeFactory.Int32)
        {
            _factory = new PrimitiveTypeFactory<T>();
        }

        public override string Name
        {
            get { return typeof(T).Name; }
        }

        public override Type ReturnedClass
        {
            get { return typeof(T); }
        }

        public override Type PrimitiveClass
        {
            get { return typeof (int); }
        }

        public override object DefaultValue
        {
            get { return null; }
        }

        public override void Set(IDbCommand cmd, object value, int index)
        {
            var type = value as T;
            var param = cmd.Parameters[index] as DbParameter;
            param.Value = type.Id;
        }

        public override object Get(IDataReader rs, int index)
        {
            return GetStaticValue(rs[index]);
        }

        public override object Get(IDataReader rs, string name)
        {
            return GetStaticValue(rs[name]);
        }

        private T GetStaticValue(object val)
        {
            if (val == null)
            {
                return (T)DefaultValue;
            }

            int id = int.Parse(val.ToString());
            T entity = _factory.GetById(id); // that returns, by reflection and based on the type T, the static value with the given Id

            if (entity == null)
            {
                throw new InvalidOperationException(string.Format("Could not determine {0} for id {1}", typeof (T).Name, id));
            }
            return entity;
        }

        public override object FromStringValue(string xml)
        {
            return GetStaticValue(xml);
        }

        public override string ObjectToSQLString(object value, Dialect dialect)
        {
            var type = value as T;
            return type.Id.ToString();
        }
    }
}
使用系统;
使用系统数据;
使用System.Data.Common;
使用NHibernate方言;
使用NHibernate.SqlTypes;
使用NHibernate.Type;
命名空间MyCompany.Utilities.DomainObjects
{
公共抽象类原语类型库:原语类型,其中T:类,IUNIQUELNAMED,IIIdentifiable
{
私有只读原语类型工厂\u工厂;
public PrimitiveTypeBase():base(SqlTypeFactory.Int32)
{
_工厂=新的原语类型工厂();
}
公共重写字符串名
{
获取{return typeof(T).Name;}
}
公共重写类型ReturnedClass
{
获取{return typeof(T);}
}
公共重写类型基元类
{
获取{return typeof(int);}
}
公共覆盖对象默认值
{
获取{return null;}
}
公共覆盖无效集(IDbCommand cmd,对象值,int索引)
{
var类型=作为T的值;
var param=cmd.Parameters[index]作为DbParameter;
参数值=类型Id;
}
公共重写对象Get(IDataReader rs,int索引)
{
返回GetStaticValue(rs[index]);
}
公共重写对象Get(IDataReader rs,字符串名称)
{
返回GetStaticValue(rs[name]);
}
私有T GetStaticValue(对象值)
{
if(val==null)
{
返回(T)默认值;
}
int id=int.Parse(val.ToString());
T entity=_factory.GetById(id);//通过反射并基于类型T返回具有给定id的静态值
if(实体==null)
{
抛出新的InvalidOperationException(string.Format(“无法确定id{1}的{0}”,typeof(T.Name,id));
}
返回实体;
}
公共重写对象FromStringValue(字符串xml)
{
返回GetStaticValue(xml);
}
公共重写字符串ObjectToSQLString(对象值,方言)
{
var类型=作为T的值;
返回类型.Id.ToString();
}
}
}
我的解决方案:(我同意:-) 用枚举替换这些实体,如果需要描述字段,请使用属性。 我们还可以对数据库进行约束,以确保不能存储与枚举不匹配的随机值

他们反对使用枚举的理由:
  • 这不是一个对象,所以你不能扩展它,它不是面向对象的,等等
  • 您无法轻松获得描述,也无法使用“专有英语”名称(带有空格或符号),例如“我的值”(在枚举中,它将是“我的值”)
  • 枚举很糟糕
  • 属性很糟糕
我对我们当前解决方案的理性思考:
  • 代码中的ID和数据库中的ID可能不匹配
  • 维护起来要困难得多(我们需要绝对确保我们所有的硬编码值都存在于数据库中)
  • 如果正确地使用属性和枚举,并且对于这样的静态值,则属性和枚举不会很糟糕
  • 对于“地道英语”名称,我们还可以使用属性,并使用一些扩展方法来使用它

现在,您认为如何?

我个人更喜欢第一种解决方案,可能有一个额外的属性返回所有值

更重要的是OO-枚举基本上是“命名的数字”,仅此而已。在代码的其他地方,状态都存储在属性中——那么为什么要使用属性呢?正如Eric Lippert在一篇文章中所写,“属性是关于机制的事实”。你基本上是用它们来提供关于价值观的数据,我觉得这是不对的

您对使用POCOs的前两个反对意见,即代码和数据库之间的潜在不匹配,听起来也不正确——因为它们对于枚举也是完全相同的,不是吗?此外,编写一个验证数据的测试非常容易——如果您愿意,您甚至可以在启动时这样做

尚不清楚
AssetClass
的其余部分是做什么的,但是如果它只有一个私有构造函数,那么就有限的、众所周知的se而言,您可以从Enum中获得很多好处
FooEnum invalid = (FooEnum) 12345;
if(obj.EnumValue == Enum.Value)
if(obj.Value == "SomeValues")