【问题标题】:Hydrating an enumeration class with Dapper使用 Dapper 为枚举类补水
【发布时间】:2019-08-21 16:14:49
【问题描述】:

我正在使用 Dapper 来补充 C# 类。我最近从字符串常量集合转移到此处定义的“枚举类”:https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/enumeration-classes-over-enum-types

我的枚举如下所示:

public abstract class Enumeration : IComparable
    {
        public string Name { get; }

        protected Enumeration(string name)
        {
            Name = name;
        }

        public static IEnumerable<T> GetAll<T>() where T : Enumeration
        {
            var fields = typeof(T).GetFields(BindingFlags.Public |
                                             BindingFlags.Static |
                                             BindingFlags.DeclaredOnly);

            return fields.Select(f => f.GetValue(null)).Cast<T>();
        }

        public static IEnumerable<T> ToSortedEnumerable<T>() where T : Enumeration
        {
            List<T> values = GetAll<T>().ToList();
            values.Sort();
            return values;
        }

        public int CompareTo(object other) =>
            string.Compare(Name, ((Enumeration) other).Name, StringComparison.Ordinal);

        public static implicit operator string(Enumeration enumeration)
        {
            return enumeration?.ToString();
        }

        public static bool operator ==(Enumeration e1, Enumeration e2)
        {
            return Equals(e1, e2);
        }

        public static bool operator !=(Enumeration e1, Enumeration e2)
        {
            return !Equals(e1, e2);
        }

        public static bool HasValue<T>(string valueToCheck) where T : Enumeration
        {
            return Enumeration.GetAll<T>().Any(x => x.Name.Equals(valueToCheck, StringComparison.OrdinalIgnoreCase));
        }

        public static bool TryGetEnumeration<T>(string valueToCheck, out T result) where T : Enumeration
        {
            result = Enumeration.GetAll<T>()
                                .FirstOrDefault(
                                    x => x.Name.Equals(valueToCheck, StringComparison.OrdinalIgnoreCase));

            return result != null;
        }

        public static T GetEnumeration<T>(string valueToCheck) where T : Enumeration
        {
            var result = Enumeration.GetAll<T>()
                                .FirstOrDefault(
                                    x => x.Name.Equals(valueToCheck, StringComparison.OrdinalIgnoreCase));

            if (result == null)
            {
                throw new ArgumentException($"Invalid {typeof(T).Name}: {valueToCheck}");
            }

            return result;
        }

        public override bool Equals(object obj)
        {
            var otherValue = obj as Enumeration;

            if (otherValue == null)
                return false;

            bool typeMatches = this.GetType() == obj.GetType();
            bool valueMatches = this.Name.Equals(otherValue.Name);

            return typeMatches && valueMatches;
        }

        public override int GetHashCode()
        {
            return 539060726 + EqualityComparer<string>.Default.GetHashCode(this.Name);
        }

        public override string ToString() => this.Name;
    }

我的 Race 课程如下所示:

public class Race : Enumeration
    {
        public static Race White = new Race("White");
        public static Race Hawaiian = new Race("Native Hawaiian");
        public static Race Filipino = new Race("Filipino");
        public static Race Black = new Race("Black / African American");
        public static Race Chinese = new Race("Chinese");
        public static Race Japanese = new Race("Japanese");
        public static Race Korean = new Race("Korean");
        public static Race Vietnamese = new Race("Vietnamese");
        public static Race AsianIndian = new Race("Asian Indian");
        public static Race OtherAsian = new Race("Other Asian");
        public static Race Samoan = new Race("Samoan");
        public static Race AmericanIndian = new Race("American Indian");
        public static Race AlaskaNative = new Race("Alaska Native");
        public static Race Guamanian = new Race("Guamanian");
        public static Race Chamorro = new Race("Chamorro");
        public static Race OtherPacificIslander = new Race("Other Pacific Islander");
        public static Race Other = new Race("Other");

        public Race(string name) : base(name)
        { }
    }

我简化的 Person 对象如下所示:

public class Person
{
    public Person(Guid personId, Race race){
        PersonId = personId;
        Race = race;
    }
    public Race Race {get;}
    public Guid PersonId {get;}
}

这是一个简化的 Dapper 命令(与 postgresql 对话)有效(PersonId 已正确补充),但 Race 始终为 NULL。

return connection.Query<Person>(sql: @"
    SELECT person_id as PersonId
    ,race
    FROM public.people");

我已尝试将我的 SQL 调整为:

return connection.Query<Person>(sql: @"
    SELECT person_id as PersonId
    ,race as Name
    FROM public.people");

但这也会导致 Race 为空值。

我正在尝试的可能吗?我必须为此做一个 splitOn 吗?我避免了这种情况,因为我的真实班级有几十个这样的属性,它们都必须是 Name 和 . . .好吧,如果我在这里错过了一些愚蠢的事情,我只是不想去那里。老实说,我认为

public static implicit operator string(Enumeration enumeration)

会为我解决这个问题。

有人想吗?我们总是感谢您的帮助。

【问题讨论】:

  • public.people 表的架构是什么?
  • 史蒂夫说了什么:)!乍一看(没有阅读代码的每一行),这看起来问题出在您的 SELECT 语句中。如果你在 Dapper 之外运行 SELECT,你会得到你想要的结果吗?

标签: c# dapper


【解决方案1】:

也许这太简单了,但是,您选择的列名需要匹配您要映射到的类中的属性,否则 Dapper 将不知道如何使映射匹配。

如果你的班级是:

public class Person
{
    public Race Race {get;}
    public Guid PersonId {get;}
}

那么您的查询需要匹配:

return connection.Query<Person>(sql: @"
    SELECT 
        Race
       , person_id as PersonId
FROM public.people");

注意 Race 中的大写 R。 (为了更好的衡量,我也喜欢让它们保持相同的顺序,尽管我不确定这是否重要。)

除此之外,如果您直接对数据库执行查询,您会得到预期的结果吗?

【讨论】:

    【解决方案2】:

    好的,想通了。两件事:

    首先,splitOn 是执行此操作的方法。一个不同但相关的最终版本如下所示:

    return connection.Query<Program,
        AssistanceProgramCategory,
        AssistanceProgramType,
        AssistanceProgramLegalType,
        ProgramAuthority,
        Program>(sql: Constants.SqlStatements.SELECT_PROGRAMS_SQL,
        (program, category, programType, legalType, authority) =>
        {
            program.AssistanceCategory = category;
            program.ProgramType = programType;
            program.ProgramLegalType = legalType;
            program.Authority = authority;
            return program;
        }, splitOn: "Name,Jurisdiction");
    

    这里的 AssistanceProgramCategory、AssistanceProgramType 和 AssistanceProgramLegalType 都是 Enumeration 的子代。

    其次,SQL 确实必须使用 Name 来传递列,如下所示:

    SELECT global_id as GlobalId
    ,tier
    ,program_description as Name
    ,program_type as Name
    ,program_legal_type as Name
    ,jurisdiction as Jurisdiction
    ,customer_id as CustomerId
    ,program_name as ProgramNameForJurisdiction
    ,program_description as ProgramName
    FROM public.assistance_programs
    

    第三,我只需将“Name”放入 splitOn 一次 - 每个 Name 实例都会导致创建一个新对象。

    最后,我不得不交换 Jurisdiction 和 CustomerId,因为 CustomerId 可以为 null,而当为 NULL 时,它不会触发最终的水合到 ProgramAuthority。管辖权始终存在,因此通过交换 SQL 中的列来解决问题。

    希望这对某人有所帮助。

    一切顺利,

    V

    【讨论】:

      猜你喜欢
      • 2015-06-26
      • 1970-01-01
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-05
      相关资源
      最近更新 更多