Не совсем понимаю некоторые вещи в веб технологиях... Сейчас тренируюсь и делаю простое приложение для отправки картинок на сервер. Задачу себе расписал такую: Клиент на WPF (без MVVM), просто формочка. Поля: Имя, Фамилия, Отдел и кнопка открыть картинку (загрузка фото человека), ну и время, когда были отправлены данные так же записываю в БД MSSQL пока локально. Для работы с БД использую EF + Code First. С текстовыми данными у меня вроде бы получилось. А вот с картинкой совсем не понимаю как быть. BLOB отметаю. Помогите реализовать. Мне необходима именно реализация, так как она у меня и хромает. Буду благодарен, если кто-то дополнит мой код для передачи картинок и сохранения их путей и тд.. (Контекст и регистрацию показывать не буду, что бы не нагромождать код).
Сервер Модель:
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string Department { get; set; }
public DateTimeOffset DateTime { get; set; }
public string Path { get; set; }
}
Интерфейс хранилища и реализация:
public interface IPersonRepository
{
Person Add(Person person);
Person Get(Guid id);
IEnumerable<Person> GetAll();
void Remove(Guid id);
bool Update(Person person);
}
public class PersonRepository : IPersonRepository
{
private readonly PersonContext _personContext;
public PersonRepository(PersonContext personContext)
{
_personContext = personContext;
}
public Person Add(Person person)
{
try
{
person.Id = Guid.NewGuid();
_personContext.Persons.Add(person);
_personContext.SaveChanges();
}
catch (Exception err)
{
throw new Exception(err.Message);
}
return person;
}
public Person Get(Guid id)
{
return _personContext.Persons.SingleOrDefault(c => c.Id == id);
}
public IEnumerable<Person> GetAll()
{
try
{
return _personContext.Persons.AsQueryable();
}
catch (Exception err)
{
throw new Exception(err.Message);
}
}
public void Remove(Guid id)
{
try
{
Person employeeToRemove = new Person { Id = id };
_personContext.Persons.Attach(employeeToRemove);
_personContext.Persons.Remove(employeeToRemove);
_personContext.SaveChanges();
}
catch (Exception err)
{
throw new Exception(string.Format("Сотрудника не существует или запись удалена!" + err.Message));
}
}
public bool Update(Person person)
{
throw new NotImplementedException();
}
}
Контроллер:
[Route("api/[controller]")]
public class PersonController : Controller
{
public IPersonRepository Person { get; set; }
public PersonController(IPersonRepository person)
{
Person = person;
}
public IEnumerable<Person> GetAll()
{
return Person.GetAll();
}
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(Guid id)
{
var item = Person.Get(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
[HttpPost]
public IActionResult Create([FromBody] Person person)
{
if (person == null)
{
return BadRequest();
}
Person.Add(person);
return CreatedAtRoute("GetTodo", new { id = person.Id }, person);
}
[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody] Person person)
{
//Do something
}
[HttpPatch("{id}")]
public IActionResult Update([FromBody] Person item, string id)
{
// Do something
}
[HttpDelete("{id}")]
public IActionResult Delete(Guid id)
{
var todo = Person.Get(id);
if (todo == null)
{
return NotFound();
}
Person.Remove(id);
return new NoContentResult();
}
}
Клиент Получение всех данных и добавление пользователя.
public partial class MainWindow : Window
{
String urlAddress = ConfigurationManager.AppSettings["serverUriString"];
public MainWindow()
{
InitializeComponent();
BindTodoList();
}
private void BindTodoList()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(urlAddress);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = client.GetAsync("api/person/").GetAwaiter().GetResult())
{
if (response.IsSuccessStatusCode)
{
var employees = response.Content.ReadAsAsync<IEnumerable<Person>>().GetAwaiter().GetResult();
grdEmployee.ItemsSource = employees;
}
else
{
MessageBox.Show("Error Code" + response.StatusCode + " : Message - " + response.ReasonPhrase);
}
}
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(urlAddress);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var todo = new Person();
todo.Name = txtName.Text;
todo.LastName = txtLastName.Text;
todo.Department = txtDepartment.Text;
todo.Path = txtPath.Text;
todo.DateTime = DateTime.Now;
using (var response = client.PostAsJsonAsync("api/person/", todo).GetAwaiter().GetResult())
{
if (response.IsSuccessStatusCode)
{
MessageBox.Show("Employee Added");
txtName.Text = "";
txtLastName.Text = "";
txtDepartment.Text = "";
txtPath.Text = "";
BindTodoList();
}
else
{
MessageBox.Show("Error Code" + response.StatusCode + " : Message - " + response.ReasonPhrase);
}
}
}
private void btnShowAll_Click(object sender, RoutedEventArgs e)
{
BindTodoList();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
BindTodoList();
}
private void btnOpenFile_Click(object sender, RoutedEventArgs e)
{
// OpenFileDialog
}
public bool SendData(byte[] image)
{
// Не знаю что...
return true;
}
}
Во-первых, храните в базе только имя файла (а сам файл в некоторой папке /uploads/ - причём имя файла генерируйте сами, чтобы при загрузке Image1.jpg не перетирало предыдущий загруженный файл). В принципе, можно и в BLOB засунуть, если ваш вопрос увидит Майоров он наверное так и посоветует, будем считать, что можно и так и так - выбор на ваш вкус.
Во-вторых, можете почитать как грузятся файлы через Request.Files - тынц: https://metanit.com/sharp/articles/mvc/16.php - там уже готовый код для серверной части (Upload, который разбирает Request.Files и сохраняет принятые файлы на диск)
Глядя на ваш код думаю этих двух подсказок будет достаточно.
А, нет. Вот ещё одна. Сделайте ещё одну модель - UploadPersonRequest, нагляднее будет:
public class UploadPersonRequest
{
public Guid Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string Department { get; set; }
public DateTimeOffset DateTime { get; set; }
public HttpPostedFileBase File { get; set; }
}
Также просто поищите на so по тегу c# и слову HttpPostedFileBase вопросы, будет вам образцом для вдохновения.
Возникло несколько вопросов: Скажите пожалуйста, для данной модели необходим свой репозиторий и контекст для Code First? Как потом связать две таблицы, ведь основная у меня существует и мне необходимо тянуть данные основные. Зачем еще одна модель?
Для данной модели не нужен ни свой репозиторий и его не надо тащить в EF Code First. Почитайте в этом вопросе то, что я пишу про RegisterViewModel: видите там два поля пароля и проверку на их одинаковость? В базе же мы храним только один раз и не в явном виде, а хешированное и часто ещё - солёное.
С этой моделью точно такая же ситуация: этот класс - лишь группировка входных параметров, чтобы вы могли сделать единый .Validate (допустим, проверить, что число переданных файлов не ноль или скажем ровно 1).
И эти данные request мы после валидации отправим на сохранение частично в одно место (на диск сам файл) и частично в базу (там у вас будет модель Person, при этом вы можете как сделать это просто dto-объектом, так и навесить дополнительную логику - зависит от архитектуры). И вот для работы с БД вам нужен отдельный репозиторий и для работы с диском вам нужен отдельный репозиторий.
Разумеется, вы можете работать и отделив HttpPostedFileBase от модели, просто тогда у вас будет часть информации в одном месте, часть в другом месте и я считаю такой способ менее удобным, если у вас есть цельная модель и на неё можно писать цельные юнит-тесты.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости