Отображение имен столбцов с пробелами в dapper [duplicate]

Протестированный код для хранения и извлечения изображений с использованием MySQL и VB.NET

Public Class FMImage

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    If OpenFileDialog1.ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
        TextBox1.Text = OpenFileDialog1.FileName

        Dim filename As String = TextBox1.Text
        Dim FileSize As UInt32

        Dim Conn As MySql.Data.MySqlClient.MySqlConnection
        Conn = New MySql.Data.MySqlClient.MySqlConnection

        Try
            If Conn.State = ConnectionState.Open Then Conn.Close()
            Conn.ConnectionString = MySQLConnectionString
            Conn.Open()

        Catch ex As Exception
            MessageBox.Show(ex.ToString, "Connect")
        End Try


        Dim mstream As System.IO.MemoryStream = ConvertImageFiletoMemoryStream(filename)
        PbPicture.Image.Save(mstream, Drawing.Imaging.ImageFormat.Jpeg)
        Dim arrImage() As Byte = ConvertImageFiletoBytes(filename)

        FileSize = mstream.Length
        Dim sqlcmd As New MySql.Data.MySqlClient.MySqlCommand
        Dim sql As String
        mstream.Close()

        'CREATE TABLE `actors` ( `actor_pic` longblob,`filesize` bigint(20) default NULL,`filename` varchar(150) default NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;

        sql = "insert into actors (actor_pic, filesize, filename) VALUES(@File, @FileName, @FileSize)"

        Try

            With sqlcmd
                .CommandText = sql
                .Connection = Conn
                .Parameters.AddWithValue("@FileName", filename)
                .Parameters.AddWithValue("@FileSize", FileSize)
                .Parameters.AddWithValue("@File", arrImage)
                .ExecuteNonQuery()
            End With
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try


        Dim adapter As New MySql.Data.MySqlClient.MySqlDataAdapter
        adapter.SelectCommand = New MySql.Data.MySqlClient.MySqlCommand("SELECT actor_pic, filesize, filename FROM actors", Conn)

        Dim Data As New DataTable
        'adapter = New MySql.Data.MySqlClient.MySqlDataAdapter("select picture from [yourtable]", Conn)

        Dim commandbuild As New MySql.Data.MySqlClient.MySqlCommandBuilder(adapter)
        adapter.Fill(Data)
        MsgBox(Data.Rows.Count)


        Dim lb() As Byte = Data.Rows(Data.Rows.Count - 1).Item("actor_pic")
        Dim lstr As New System.IO.MemoryStream(lb)
        PbPicture.Image = Image.FromStream(lstr)
        PbPicture.SizeMode = PictureBoxSizeMode.StretchImage
        lstr.Close()

    End If
End Sub

Public Function ConvertImageFiletoBytes(ByVal ImageFilePath As String) As Byte()
    Dim _tempByte() As Byte = Nothing
    If String.IsNullOrEmpty(ImageFilePath) = True Then
        Throw New ArgumentNullException("Image File Name Cannot be Null or Empty", "ImageFilePath")
        Return Nothing
    End If
    Try
        Dim _fileInfo As New IO.FileInfo(ImageFilePath)
        Dim _NumBytes As Long = _fileInfo.Length
        Dim _FStream As New IO.FileStream(ImageFilePath, IO.FileMode.Open, IO.FileAccess.Read)
        Dim _BinaryReader As New IO.BinaryReader(_FStream)
        _tempByte = _BinaryReader.ReadBytes(Convert.ToInt32(_NumBytes))
        _fileInfo = Nothing
        _NumBytes = 0
        _FStream.Close()
        _FStream.Dispose()
        _BinaryReader.Close()
        Return _tempByte
    Catch ex As Exception
        Return Nothing
    End Try
End Function

Public Function ConvertBytesToMemoryStream(ByVal ImageData As Byte()) As IO.MemoryStream
    Try
        If IsNothing(ImageData) = True Then
            Return Nothing
            'Throw New ArgumentNullException("Image Binary Data Cannot be Null or Empty", "ImageData")
        End If
        Return New System.IO.MemoryStream(ImageData)
    Catch ex As Exception
        Return Nothing
    End Try
End Function

Public Function ConvertImageFiletoMemoryStream(ByVal ImageFilePath As String) As IO.MemoryStream
    If String.IsNullOrEmpty(ImageFilePath) = True Then
        Return Nothing
        ' Throw New ArgumentNullException("Image File Name Cannot be Null or Empty", "ImageFilePath")
    End If
    Return ConvertBytesToMemoryStream(ConvertImageFiletoBytes(ImageFilePath))
End Function

End Class
128
задан Metro Smurf 8 September 2014 в 23:44
поделиться

13 ответов

Это отлично работает:

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<Person>(sql).ToList();
    return person;
}

У Dapper нет объекта, который позволяет вам указать атрибут столбца , я не против добавления поддержки для него, если мы не тянуть зависимость.

56
ответ дан Sam Saffron 24 August 2018 в 20:49
поделиться

для всех вас, кто использует Dapper 1.12. Вот что вам нужно сделать, чтобы это сделать:

  • Добавить новый класс атрибутов столбца:
      [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property]
    
      public class ColumnAttribute : Attribute
      {
    
        public string Name { get; set; }
    
        public ColumnAttribute(string name)
        {
          this.Name = name;
        }
      }
    
  • Найдите эту строку:
    map = new DefaultTypeMap(type);
    
    и прокомментируйте это.
  • Вместо этого напишите:
            map = new CustomPropertyTypeMap(type, (t, columnName) =>
            {
              PropertyInfo pi = t.GetProperties().FirstOrDefault(prop =>
                                prop.GetCustomAttributes(false)
                                    .OfType<ColumnAttribute>()
                                    .Any(attr => attr.Name == columnName));
    
              return pi != null ? pi : t.GetProperties().FirstOrDefault(prop => prop.Name == columnName);
            });
    
  • 1
    ответ дан Ben Collins 24 August 2018 в 20:49
    поделиться

    Легкий способ достичь этого - просто использовать псевдонимы в столбцах в вашем запросе. Если ваш столбец базы данных PERSON_ID, а свойство вашего объекта - ID, вы можете просто выполнить select PERSON_ID as Id ... в своем запросе, а Dapper заберет его, как ожидалось.

    10
    ответ дан Brad Westness 24 August 2018 в 20:49
    поделиться

    Это ответная реакция от других ответов. Это просто мысль, которую я использовал для управления строками запроса.

    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-метод

    using (var conn = ConnectionFactory.GetConnection())
    {
        var person = conn.Query<Person>(Person.Select()).ToList();
        return person;
    }
    
    3
    ответ дан christo8989 24 August 2018 в 20:49
    поделиться

    Решение Калеба Педерсона работало для меня. Я обновил ColumnAttributeTypeMapper, чтобы разрешить настраиваемый атрибут (имел требование для двух разных сопоставлений на одном и том же объекте домена) и обновил свойства, чтобы позволить частным сеттерам в случаях, когда необходимо было получить поле и типы отличались.

    public class ColumnAttributeTypeMapper<T,A> : 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<A>()
                                   .Any(attr => attr.Name == columnName)
                               )
                       ),
                    new DefaultTypeMap(typeof(T))
                })
        {
            //
        }
    }
    
    1
    ответ дан GameSalutes 24 August 2018 в 20:49
    поделиться

    Прежде чем открыть соединение с вашей базой данных, выполните этот фрагмент кода для каждого из ваших классов poco:

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

    Затем добавьте аннотации данных к вашим классам poco следующим образом:

    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; }
    }
    

    После этого вы все настроены. Просто сделайте запрос, например:

    using (var sqlConnection = new SqlConnection("your_connection_string"))
    {
        var sqlStatement = "SELECT " +
                    "db_column_name1, " +
                    "db_column_name2 " +
                    "FROM your_table";
    
        return sqlConnection.Query<Section>(sqlStatement).AsList();
    }
    
    6
    ответ дан JedatKinports 24 August 2018 в 20:49
    поделиться

    Я делаю следующее, используя динамический и LINQ:

        var sql = @"select top 1 person_id, first_name, last_name from Person";
        using (var conn = ConnectionFactory.GetConnection())
        {
            List<Person> person = conn.Query<dynamic>(sql)
                                      .Select(item => new Person()
                                      {
                                          PersonId = item.person_id,
                                          FirstName = item.first_name,
                                          LastName = item.last_name
                                      }
                                      .ToList();
    
            return person;
        }
    
    13
    ответ дан Jeson Martajaya 24 August 2018 в 20:49
    поделиться

    Теперь Dapper поддерживает настраиваемые столбцы для свойств. Он делает это через интерфейс ITypeMap . Класс CustomPropertyTypeMap предоставляется Dapper, который может выполнять большую часть этой работы. Например:

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

    И модель:

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

    Важно отметить, что для реализации CustomPropertyTypeMap требуется, чтобы атрибут существовал и соответствовал одному из имен столбцов или свойство не будет отображаться. Класс DefaultTypeMap предоставляет стандартную функциональность и может быть использован для изменения этого поведения:

    public class FallbackTypeMapper : SqlMapper.ITypeMap
    {
        private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;
    
        public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> 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);
        }
    }
    

    И с этим на месте становится легко создавать настраиваемый тип-транслятор, который будет автоматически использовать атрибуты, если они присутствуют, но в противном случае отпадут в стандартное поведение:

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

    Это означает, что теперь мы можем легко поддерживать типы, которым требуется карта с использованием атрибутов:

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

    Вот Gist для полного исходного кода .

    159
    ответ дан Kaleb Pederson 24 August 2018 в 20:49
    поделиться

    Если вы используете проверку .NET 4.5.1 или выше Dapper.FluentColumnMapping для отображения стиля LINQ. Он позволяет полностью отделить отображение db от вашей модели (нет необходимости в аннотации)

    3
    ответ дан mamuesstack 24 August 2018 в 20:49
    поделиться

    В течение некоторого времени должно работать следующее:

    Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
    
    55
    ответ дан Marc Gravell 24 August 2018 в 20:49
    поделиться

    Мессинг с отображением граничит с реальной землей ORM. Вместо того, чтобы сражаться с ним и держать Dapper в его простой (быстрой) форме, просто немного измените свой SQL так:

    var sql = @"select top 1 person_id as PersonId,FirstName,LastName from Person";
    
    7
    ответ дан mxmissile 24 August 2018 в 20:49
    поделиться

    Взято из теста Dapper Tests , который в настоящее время находится на 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);
    

    Класс помощника, чтобы получить имя от атрибута Description (я лично использовал Column like @kalebs пример)

    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;
    }
    

    Класс

    public class TypeWithMapping
    {
       [Description("B")]
       public string A { get; set; }
    
       [Description("A")]
       public string B { get; set; }
    }
    
    8
    ответ дан Oliver 24 August 2018 в 20:49
    поделиться

    Вот простое решение, которое не требует атрибутов, позволяющих вам сохранить код инфраструктуры из ваших POCOs.

    Это класс для работы с сопоставлениями. Словарь будет работать, если вы сопоставили все столбцы, но этот класс позволяет указать только различия. Кроме того, он включает в себя обратные карты, поэтому вы можете получить поле из столбца и столбца из поля, что может быть полезно при выполнении таких операций, как создание операторов sql.

    public class ColumnMap
    {
        private readonly Dictionary<string, string> forward = new Dictionary<string, string>();
        private readonly Dictionary<string, string> reverse = new Dictionary<string, string>();
    
        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;
            }
        }
    }
    

    Настройте объект ColumnMap и скажите Dapper использовать сопоставление.

    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])));
    
    24
    ответ дан Randall Sutton 24 August 2018 в 20:49
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: