Manuell ordnen Sie Spaltennamen mit classneigenschaften zu

Ich bin neu bei Dapper Micro ORM. Bis jetzt bin ich in der Lage, es für einfache ORM bezogene Sachen zu benutzen, aber ich bin nicht imstande, die databasespaltennamen mit den classneigenschaften zuzuordnen. Beispielsweise:

Ich habe die databasetabelle wie folgt:

Table Name: Person person_id int first_name varchar(50) last_name varchar(50) 

und ich habe die class namens Person

 public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } 

Bitte beachten Sie, dass sich meine Spaltennamen in der Tabelle von dem Eigenschaftsnamen der class unterscheiden, der ich die Daten zuordnen möchte, die ich aus dem Abfrageergebnis erhalten habe.

 var sql = @"select top 1 PersonId,FirstName,LastName from Person"; using (var conn = ConnectionFactory.GetConnection()) { var person = conn.Query(sql).ToList(); return person; } 

Der obige Code funktioniert nicht, da die Spaltennamen nicht mit den Eigenschaften des Objekts (Person) übereinstimmen. Kann ich in Dapper die Spaltennamen mit Objekteigenschaften manuell person_id => PersonId (zB person_id => PersonId )?

Jeder Hinweis oder Hilfe würde sehr geschätzt werden.

   

    Das funktioniert gut:

     var sql = @"select top 1 person_id PersonId, first_name FirstName, last_name LastName from Person"; using (var conn = ConnectionFactory.GetConnection()) { var person = conn.Query(sql).ToList(); return person; } 

    Dapper hat keine Möglichkeit, ein Spaltenattribut anzugeben, ich bin nicht dagegen, Unterstützung hinzuzufügen, vorausgesetzt, wir ziehen die Abhängigkeit nicht ein.

    Dapper unterstützt jetzt benutzerdefinierte Column-zu-Property-Mapper. Dies geschieht über die ITypeMap- Schnittstelle. Eine class CustomPropertyTypeMap wird von Dapper bereitgestellt, die den Großteil dieser Arbeit erledigen kann. Beispielsweise:

     Dapper.SqlMapper.SetTypeMap( typeof(TModel), new CustomPropertyTypeMap( typeof(TModel), (type, columnName) => type.GetProperties().FirstOrDefault(prop => prop.GetCustomAttributes(false) .OfType() .Any(attr => attr.Name == columnName)))); 

    Und das Modell:

     public class TModel { [Column(Name="my_property")] public int MyProperty { get; set; } } 

    Es ist wichtig zu beachten, dass die Implementierung von CustomPropertyTypeMap erfordert, dass das Attribut vorhanden ist und einem der Spaltennamen entspricht oder dass die Eigenschaft nicht zugeordnet wird. Die class DefaultTypeMap stellt die Standardfunktionalität bereit und kann verwendet werden, um dieses Verhalten zu ändern:

     public class FallbackTypeMapper : SqlMapper.ITypeMap { private readonly IEnumerable _mappers; public FallbackTypeMapper(IEnumerable mappers) { _mappers = mappers; } public SqlMapper.IMemberMap GetMember(string columnName) { foreach (var mapper in _mappers) { try { var result = mapper.GetMember(columnName); if (result != null) { return result; } } catch (NotImplementedException nix) { // the CustomPropertyTypeMap only supports a no-args // constructor and throws a not implemented exception. // to work around that, catch and ignore. } } return null; } // implement other interface methods similarly // required sometime after version 1.13 of dapper public ConstructorInfo FindExplicitConstructor() { return _mappers .Select(mapper => mapper.FindExplicitConstructor()) .FirstOrDefault(result => result != null); } } 

    Und damit ist es einfach, einen benutzerdefinierten Typ-Mapper zu erstellen, der automatisch die Attribute verwendet, wenn sie vorhanden sind, ansonsten aber auf das Standardverhalten zurückgreift:

     public class ColumnAttributeTypeMapper : FallbackTypeMapper { public ColumnAttributeTypeMapper() : base(new SqlMapper.ITypeMap[] { new CustomPropertyTypeMap( typeof(T), (type, columnName) => type.GetProperties().FirstOrDefault(prop => prop.GetCustomAttributes(false) .OfType() .Any(attr => attr.Name == columnName) ) ), new DefaultTypeMap(typeof(T)) }) { } } 

    Das bedeutet, dass wir nun einfach Typen unterstützen können, die die Verwendung von Attributen erfordern:

     Dapper.SqlMapper.SetTypeMap( typeof(MyModel), new ColumnAttributeTypeMapper()); 

    Hier ist ein Gist zum vollständigen Quellcode .

    Für einige Zeit sollte folgendes funktionieren:

     Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 

    Hier ist eine einfache Lösung, die keine Attribute benötigt, die es Ihnen ermöglichen, den Infrastrukturcode aus Ihren POCOs herauszuhalten.

    Dies ist eine class, die sich mit den Zuordnungen befasst. Ein Wörterbuch funktioniert, wenn Sie alle Spalten zugeordnet haben, aber diese class ermöglicht Ihnen, nur die Unterschiede anzugeben. Darüber hinaus enthält es umgekehrte Karten, so dass Sie das Feld aus der Spalte und der Spalte aus dem Feld abrufen können, was beispielsweise beim Generieren von SQL-statementen hilfreich sein kann.

     public class ColumnMap { private readonly Dictionary forward = new Dictionary(); private readonly Dictionary reverse = new Dictionary(); public void Add(string t1, string t2) { forward.Add(t1, t2); reverse.Add(t2, t1); } public string this[string index] { get { // Check for a custom column map. if (forward.ContainsKey(index)) return forward[index]; if (reverse.ContainsKey(index)) return reverse[index]; // If no custom mapping exists, return the value passed in. return index; } } } 

    Richten Sie das ColumnMap-Objekt ein und weisen Sie Dapper an, das Mapping zu verwenden.

     var columnMap = new ColumnMap(); columnMap.Add("Field1", "Column1"); columnMap.Add("Field2", "Column2"); columnMap.Add("Field3", "Column3"); SqlMapper.SetTypeMap(typeof (MyClass), new CustomPropertyTypeMap(typeof (MyClass), (type, columnName) => type.GetProperty(columnMap[columnName]))); 

    Ich mache folgendes mit dynamic und LINQ:

      var sql = @"select top 1 person_id, first_name, last_name from Person"; using (var conn = ConnectionFactory.GetConnection()) { List person = conn.Query(sql) .Select(item => new Person() { PersonId = item.person_id, FirstName = item.first_name, LastName = item.last_name } .ToList(); return person; } 

    Eine einfache Möglichkeit, dies zu erreichen, besteht darin, Aliase für die Spalten in Ihrer Abfrage zu verwenden. Wenn Ihre databasespalte PERSON_ID und die PERSON_ID Ihres Objekts ID , können Sie einfach select PERSON_ID as Id ... in Ihrer Abfrage select PERSON_ID as Id ... und Dapper wird es wie erwartet abholen.

    Entnommen aus den Dapper Tests, die derzeit auf Dapper 1.42.

     // custom mapping var map = new CustomPropertyTypeMap(typeof(TypeWithMapping), (type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName)); Dapper.SqlMapper.SetTypeMap(typeof(TypeWithMapping), map); 

    Helper-class, um Namen aus dem Description-Attribut zu bekommen (Ich habe selbst Column wie @kalebs verwendet)

     static string GetDescriptionFromAttribute(MemberInfo member) { if (member == null) return null; var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false); return attrib == null ? null : attrib.Description; } 

    class

     public class TypeWithMapping { [Description("B")] public string A { get; set; } [Description("A")] public string B { get; set; } } 

    Das Chaos mit dem Mapping bewegt sich grenzwertig in echtes ORM-Land. Anstatt damit zu kämpfen und Dapper in seiner wahren, einfachen (schnellen) Form zu halten, modifiziere deine SQL einfach so:

     var sql = @"select top 1 person_id as PersonId,FirstName,LastName from Person"; 

    Bevor Sie die Verbindung zu Ihrer database öffnen, führen Sie diesen Code für jede Ihrer poco-classn aus:

     // Section SqlMapper.SetTypeMap(typeof(Section), new CustomPropertyTypeMap( typeof(Section), (type, columnName) => type.GetProperties().FirstOrDefault(prop => prop.GetCustomAttributes(false).OfType().Any(attr => attr.Name == columnName)))); 

    Fügen Sie die Datenanmerkungen wie folgt zu Ihren poco-classn hinzu:

     public class Section { [Column("db_column_name1")] // Side note: if you create aliases, then they would match this. public int Id { get; set; } [Column("db_column_name2")] public string Title { get; set; } } 

    Danach sind Sie fertig. Führen Sie einfach einen Abfrageaufruf aus, etwa so:

     using (var sqlConnection = new SqlConnection("your_connection_string")) { var sqlStatement = "SELECT " + "db_column_name1, " + "db_column_name2 " + "FROM your_table"; return sqlConnection.Query
    (sqlStatement).AsList(); }

    Wenn Sie .NET 4.5.1 oder höher verwenden, überprüfen Sie Dapper.FluentColumnMapping, um den LINQ-Stil zuzuordnen . Sie können das db-Mapping vollständig von Ihrem Modell trennen (keine Anmerkungen erforderlich)

    Dies ist eine harmlose Abkehr von anderen Antworten. Es ist nur ein Gedanke, den ich hatte, um die Abfragezeichenfolgen zu verwalten.

    Person.cs

     public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public static string Select() { return $"select top 1 person_id {nameof(PersonId)}, first_name {nameof(FirstName)}, last_name {nameof(LastName)}from Person"; } } 

    API-Methode

     using (var conn = ConnectionFactory.GetConnection()) { var person = conn.Query(Person.Select()).ToList(); return person; } 

    Für alle, die Dapper 1.12 verwenden, müssen Sie Folgendes tun:

  • Fügen Sie eine neue Spaltenattributklasse hinzu:
  •   [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property] public class ColumnAttribute : Attribute { public string Name { get; set; } public ColumnAttribute(string name) { this.Name = name; } } 
  • Suche nach dieser Zeile:
  •  map = new DefaultTypeMap(type); 

    und kommentiere es aus.

  • Schreibe dies stattdessen:
  •   map = new CustomPropertyTypeMap(type, (t, columnName) => { PropertyInfo pi = t.GetProperties().FirstOrDefault(prop => prop.GetCustomAttributes(false) .OfType() .Any(attr => attr.Name == columnName)); return pi != null ? pi : t.GetProperties().FirstOrDefault(prop => prop.Name == columnName); }); 

    Kaleb Pedersons Lösung funktionierte für mich. Ich habe den ColumnAttributeTypeMapper so aktualisiert, dass ein benutzerdefiniertes Attribut (mit zwei unterschiedlichen Zuordnungen für dasselbe Domänenobjekt) und aktualisierte Eigenschaften zulässig waren, um private Setter in Fällen zuzulassen, in denen ein Feld abgeleitet und die Typen unterschiedlich sein mussten.

     public class ColumnAttributeTypeMapper : FallbackTypeMapper where A : ColumnAttribute { public ColumnAttributeTypeMapper() : base(new SqlMapper.ITypeMap[] { new CustomPropertyTypeMap( typeof(T), (type, columnName) => type.GetProperties( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(prop => prop.GetCustomAttributes(true) .OfType() .Any(attr => attr.Name == columnName) ) ), new DefaultTypeMap(typeof(T)) }) { // } }