Протестированный код для хранения и извлечения изображений с использованием 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
Это отлично работает:
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 нет объекта, который позволяет вам указать атрибут столбца , я не против добавления поддержки для него, если мы не тянуть зависимость.
для всех вас, кто использует 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);
});
Легкий способ достичь этого - просто использовать псевдонимы в столбцах в вашем запросе. Если ваш столбец базы данных PERSON_ID
, а свойство вашего объекта - ID
, вы можете просто выполнить select PERSON_ID as Id ...
в своем запросе, а Dapper заберет его, как ожидалось.
Это ответная реакция от других ответов. Это просто мысль, которую я использовал для управления строками запроса.
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;
}
Решение Калеба Педерсона работало для меня. Я обновил 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))
})
{
//
}
}
Прежде чем открыть соединение с вашей базой данных, выполните этот фрагмент кода для каждого из ваших классов 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();
}
Я делаю следующее, используя динамический и 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;
}
Теперь 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>());
Если вы используете проверку .NET 4.5.1 или выше Dapper.FluentColumnMapping для отображения стиля LINQ. Он позволяет полностью отделить отображение db от вашей модели (нет необходимости в аннотации)
В течение некоторого времени должно работать следующее:
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
Мессинг с отображением граничит с реальной землей ORM. Вместо того, чтобы сражаться с ним и держать Dapper в его простой (быстрой) форме, просто немного измените свой SQL так:
var sql = @"select top 1 person_id as PersonId,FirstName,LastName from Person";
Взято из теста 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; }
}
Вот простое решение, которое не требует атрибутов, позволяющих вам сохранить код инфраструктуры из ваших 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])));