none
Загрузка и сохранение изображений при работе с SQL Server и Entity Framework RRS feed

  • Общие обсуждения

  • Имеется база данных в SQL Server со списком пациентов. Приложение разарабатывается на WPF с использованием EF.

    В базе имеется поле Foto(varbinary(max)). Имеется модель данных Patient, где есть поле Foto типа byte[].

    Вот такой код формы:

    using System;
    using System.Data.Entity;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Media.Imaging;
    using Microsoft.Win32;
    
    namespace Patients
    {
       /// <summary>
       /// Interaction logic for MainWindow.xaml
       /// </summary>
       public partial class MainWindow : Window
       {
          private PatientsEntities _context;
          private CollectionViewSource patientViewSource;
    
          public MainWindow()
          {
             InitializeComponent();
          }
    
          private void Window_Loaded(object sender, RoutedEventArgs e)
          {
             _context = new PatientsEntities();
    
             patientViewSource = ((CollectionViewSource)(this.FindResource("patientViewSource")));
             _context.Patients.Load();
             patientViewSource.Source = _context.Patients.Local;
    
          }
    
          private void FirstBtn_Click(object sender, RoutedEventArgs e)
          {
             patientViewSource.View.MoveCurrentToFirst();
          }
    
          private void PrevBtn_Click(object sender, RoutedEventArgs e)
          {
             if (patientViewSource.View.CurrentPosition > 0)
             {
                patientViewSource.View.MoveCurrentToPrevious();
             }
          }
    
          private void NextBtn_Click(object sender, RoutedEventArgs e)
          {
             if (patientViewSource.View.CurrentPosition < ((CollectionView)(patientViewSource.View)).Count - 1)
             {
                patientViewSource.View.MoveCurrentToNext();
             }
          }
    
          private void LastBtn_Click(object sender, RoutedEventArgs e)
          {
             patientViewSource.View.MoveCurrentToLast();
          }
    
          private void saveBtn_Click(object sender, RoutedEventArgs e)
          {
             try
             {
                int numChanges;
                patient.Foto = ConvertImageToByteArray(selectedFileName);
                patient = null;
             }
             catch (Exception ex)
             {
                MessageBox.Show(ex.Message);
             }
          }
    
          private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
          {
             _context.Dispose();
          }
    
          private string selectedFileName;
    
          private void addFotoBtn_Click(object sender, RoutedEventArgs e)
          {
             OpenFileDialog dlg = new OpenFileDialog();
             dlg.InitialDirectory = "";
             dlg.Filter = "Image files (*.jpg,*.png,*.bmp)|*.jpg;*.png;*.bmp|All Files (*.*)|*.*";
             if (dlg.ShowDialog() == true)
             {
                BitmapImage bmi = new BitmapImage();
                selectedFileName = dlg.FileName;
                bmi.BeginInit();
                bmi.CacheOption = BitmapCacheOption.OnLoad;
                bmi.UriSource = new Uri(selectedFileName);
                bmi.EndInit();
                fotoImage.Source = bmi;
             }
    
             patientViewSource.View.Refresh();
          }
    
          private static byte[] ConvertImageToByteArray(string fileName)
          {
             Bitmap bitMap = new Bitmap(fileName);
             ImageFormat bmpFormat = bitMap.RawFormat;
             var imageToConvert = Image.FromFile(fileName);
             using (MemoryStream ms = new MemoryStream())
             {
                imageToConvert.Save(ms, bmpFormat);
                return ms.ToArray();
             }
          } 
    
          private Patient patient = null;
    
          private void addBtn_Click(object sender, RoutedEventArgs e)
          {
             patient = new Patient {Name = "NewPatient"};
             _context.Patients.Add(patient);
             patientViewSource.View.MoveCurrentToLast();
          }
    
          private void revertBtn_Click(object sender, RoutedEventArgs e)
          {
             if (patient != null)
             {
                _context.Patients.Remove(patient);
             }
             patient = null;
          }
    
       }
    }

    Все хорошо работает при добавлении новой записи. Здесь у меня есть новый объект Patient, который я создаю.:

    После сохранения записи и перемещении на другую запись в окне постоянно "красуется" последняя фотография. Т.е. я понимаю, что нужно поменять Source  у поля Foto на форме. Наверное, это можно сделать после сохранения изменений и перепривязки поля на форме к текущему объекту.

    Вопрос: Как получить ссылку на текущий объект?

    2 июля 2014 г. 13:36

Все ответы

  • Все как-то перепутано. Разбейте на классы, четко разделяющие ответственности.

    Mak Arti

    2 июля 2014 г. 14:32
  • А что перепутано? Они есть, эти классы, я просто не стал эти классы приводить. Т.е. есть класс Patients

        using System;
        using System.Data.Entity;
        using System.Data.Entity.Infrastructure;
        
        public partial class PatientsEntities : DbContext
        {
            public PatientsEntities()
                : base("name=PatientsEntities")
            {
            }
        
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                throw new UnintentionalCodeFirstException();
            }
        
            public virtual DbSet<Patient> Patients { get; set; }
        }

    соответственно есть класс Patient

      using System;
        using System.Collections.ObjectModel;
        
        public partial class Patient
        {
            public int PatientID { get; set; }
            public string Name { get; set; }
            public string Vorname { get; set; }
            public byte[] Foto { get; set; }
         }

    там много еще чего, все не имеет смысла приводить.

    Есть кнопка "Новая запись" - метод addBtn_Click. Здесь создается новый объект Patient, добавляется в контекст, указатель перемещается на этот объект.

    Есть кнопка "Добавить фото" - метод addFotoBtn_Click, в котором фото читается из файла и ссылка на него присваивается полю Foto на форме. 

    Есть кнопка "Сохранить запись" - метод saveBtn_Click. Поскольку объект Patient был уже создан, то нет проблемы присвоить его полю Foto сконвертированное в поток байтов изображение. Понятно, что после сохранения объекта в БД, нужно обнулить ссылку на него.

    Вот тут-то и проблема - не могу сообразить, как приваивать объекту ссылку на существующую запись в БД. Почему-то думал, что EF делает это автоматически, но не могу понять как.

    2 июля 2014 г. 14:56
  • если изображение остается везде, возможно оно просто кэшируется, способов отключить много, но попробуйте самый быстрый

    [OutputCacheAttribute(VaryByParam = "*", Duration = 0, NoStore = true)] // отключение кэша на весь контроллер
    public class MyController : Controller
    {
      // ... 
    }
    отпишитесь о результате


    if (Thread.Was == HelpFul) Mark.As(HelpFul); else if (Thread.Was == Answered) Mark.As(Answered); else Provide(More.Details);



  • Нет, дело не в кешировании, дело в привязке. Изначально поле fotoImage привязано к соответствующему свойству объекта Patient. После загрузки изображения на форму я принудительно привязываю это поле к другому источнику.

    fotoImage.Source = bmi;
    Я думал, что EF будет менять эту привязку автоматически при смене записи, но этого не происходит.

    Я посидел вчера допоздна и в принципе решил проблему принудительной перепривязкой источника к полю fotoImage каждый раз, как осуществляю переход по записям. Т.е. создал переменную объекта Patient и держу ее все время в памяти. А при смене объекта меняю ссылку.

         private Patient GetPatient(int pos)
          {
             int patientID = _context.Patients.Local[pos].PatientID;
             return _context.Patients.Find(patientID);
          }
    
          private void FirstBtn_Click(object sender, RoutedEventArgs e)
          {
             patientViewSource.View.MoveCurrentToFirst();
             patient = GetPatient(patientViewSource.View.CurrentPosition);
             fotoImage.Source = ConvertByteArrayToImage(patient.Foto);
          }

    Теперь все работает, правда, не совсем так как хотелось бы. Решение не очень изящное, т.к. на мой взгляд появилось много лишнего кода и я понимаю, что самое лучшее решение просто возвращать старую привязку, но как это сделать не соображу, а может просто "глаз замылился". В общем буду искать дальше