Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
WPF

Как внедрить модель во viewModel?

26.12.2024, 11:19. Показов 25466. Ответов 295
Метки mvvm (Все метки)

Студворк — интернет-сервис помощи студентам
День добрый. Есть у меня метод в проекте, который вызывается из конструктора. Метод по типу "Получить все из бд и вывести на экран".

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

Теперь код.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class BirdsViewModel : ObservableObject
{
    // [B]Конструктор[/B]
    public BirdsViewModel(IBirdRepository birdRepository, Func<BirdEntity, BirdViewModel> birdView) // [B]Прошу обратить 
//внимание на делегат. Его, думаю, и нужно заменять.[/B]
 
    {
        Birds = [];
        _birdView = birdView;
        _birdRepository = birdRepository;
        LoadBirds();
    }
 
// [B]Проблемный метод[/B]
private async void LoadBirds()
{
    var birds = await _birdRepository.GetAllAsync(); // [B]Получение данных из БД[/B]
    switch (Name)
    {
        default:
            foreach (var bird in birds)
                Birds.Add(_birdView(bird)); // [B]С помощью делегата я во ViewModel передаю Entity, 
//с которой та и работает. Есть ли другие методы подобной реализации, чтобы у каждой ViewModel был свой Entity?[/B]
            break;
    }
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
26.12.2024, 11:19
Ответы с готовыми решениями:

Как из ViewModel UserControl'a изменить свойство в ViewModel главного окна?
Недавно начал попытки создавать приложение WPF с применением MVVM, и у меня возник вопрос, который не даёт мне продвигаться в разработке...

Как зарегистрировать ViewModel через Autofac, создав между ViewModel и нужным View DataContext связь?
Доброго времени суток. Есть желание при регистрации ViewModel'и сразу же &quot;подключиться&quot; к DataContext'у у нужной View, как например: ...

WPF получить доступ из одной viewmodel к другой viewmodel
Собственно есть у меня две viewmodel, mainviewmodel и settingsviewmodel, в mainviewmodel есть вот такая штука private Visibility...

295
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
31.12.2024, 14:04
Студворк — интернет-сервис помощи студентам
Vissmuy, несколько нюансов.

Startup="OnStartup"> - заменено переопределение OnStartup на вызов обработчика события. Причина: Ресурсы приложения и запуск основного окна из Code-Behind App [WPF, Элд Хасп]

Подсоединение второго обработчика Startup в конструкторе
C#
13
14
15
16
17
18
19
20
21
        public App()
        {
            Startup += async (s, e) =>
            {
                MainBirdsViewModel vm = (MainBirdsViewModel)FindResource("mainVM");
                vm.Birds.EnableCollectionSynchronization();
                await vm.LoadAsync();
            };
        }
Сделано так, чтобы не ждать завершения загрузки и не потерять исключений, если они вдруг возникнут. Все обработчики вызываются синхронно последовательно, но если обработчики асинхронные, то синхронный вызов завершается на первом await и потом синхронный вызов переходит к следующему обработчику. После завершения await происходит возврат к синхронному исполнению дальнейшего кода, но к этому времени все другие синхронные обработчики уже будут выполнены.

Синхронизация привязки к коллекции:
C#
18
                vm.Birds.EnableCollectionSynchronization();
Нужно чтобы ObservableCollection можно было обновлять из любого потока. Без этого методы обновления БД в Репозитории будут выкидывать исключения. Это специфика платформы WPF. В UWP, например, в таком нет необходимости.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
01.01.2025, 11:09  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
ViewModelBase
Я так понял в словаре хранятся все, так скажем, свойства и команды.

А почему для Get и Set словарь хранит значение типа object, а в командах прямо указано RelayCommand?

ViewModelBase.GetSet
C#
1
private readonly Dictionary<string, object?> _properties = new();
ViewModelBase.RelayCommand
C#
1
private readonly Dictionary<string, RelayCommand> commands = new();
Я так понял, что свойства с разным типом данных могут быть, а команды всегда с одним.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
01.01.2025, 13:16
Vissmuy, к клаcсам из Step1 относитесь просто как библиотеке. Не важно как они устроены. Вам нужно знать только как их использовать.

Цитата Сообщение от Vissmuy Посмотреть сообщение
А почему для Get и Set словарь хранит значение типа object
Типов свойств может быть множество.
Любое значение можно хранить в object. При этом будут потери (времени) на упаковку/распаковку или приведение.
Чтобы не было этих потерь, придётся делать на каждый тип свой собственный словарь. Надо будет сначала искать нужный словарь типа и только потом выбирать нужное значение из него. В целом это вряд ли будет более оптимальным, но точно будет гораздо более сложным по коду.

Цитата Сообщение от Vissmuy Посмотреть сообщение
а в командах прямо указано RelayCommand?
Так команды все одного типа. Поэтому и хранятся в отдельном словаре.
При создании команды с параметром можно получить экземпляры RelayCommand<T>, но они производные от базового RelayCommand и ничем кроме конструктора от него не отличаются. Поэтому тип параметра имеет значение только при создании команды. А для её использования это безразлично. И хранить тип параметра поэтому тоже не нужно. Достаточно знать что это RelayCommand.

Добавлено через 6 минут
В ViewModelBase есть статическое свойство IsInDesignModeStatic. Если оно true значит приложение находится в Режиме Разработки.
Это можно использовать для изменения логики в зависимости от режима.
В данном случае это не нужно, поскольку SQLite автоматически создаётся через Code First. Но в других случаях может быть очень полезным, так как не любой тип источника можно заставить корректно и одинаково работать в различных режимах.

Добавлено через 1 минуту
Есть свойство Dispatcher.
Его при необходимости можно использовать для синхронизации с View, но лучше создавать такой код где это не понадбится.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
02.01.2025, 06:17  [ТС]
Элд Хасп, хочу уточнить - нормально ли то, что MainViewModel взяла на себя функции из других ViewModels?
Например, команда по добавлению сущности была в AddBirdViewModel, теперь же класс по сути лишний, так как он пустой. Разве не должно быть разделения логики, дабы не городить все в одном классе?

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Если будет несколько репозиториев, то их совокупность будет Моделью.
Вот тут не понял, что значит их совокупность будет Моделью? К примеру, у меня есть несколько сущностей, а не одна, как в этом проекте.
Bird и, к примеру, Set, то есть набор сущностей Bird. Связь один ко многим.

У меня есть generic интерфейс IRepository, который, как я понял, имеет общую логику репозиториев.
В классе, который его наследует (В учебном проекте это DbBirdsRepository) есть методы для операций с бд.
Получается, что мне нужно будет два таких репозитория, один для Bird, как сейчас и второй для Set, так ведь?

Цитата Сообщение от Элд Хасп Посмотреть сообщение
BirdConfiguration - конфигурация БД для Code First. Есть первичная инициализация демо-данными.
Видел. А разве бд сама Id не устанавливает? Или это только в Sqlite Id самому нужно устанавливать?

C#
1
2
3
4
builder.HasData(
    new Bird() { Id = 1, Arrival = DateOnly.Parse("19.01.2001", CultureInfo.GetCultureInfo("ru-ru")), Description = "", Name = "Большак 1" },
    new Bird() { Id = 2, Arrival = DateOnly.Parse("19.01.2001", CultureInfo.GetCultureInfo("ru-ru")), Description = "", Name = "Большак 3" },
    new Bird() { Id = 3, Arrival = DateOnly.Parse("19.01.2001", CultureInfo.GetCultureInfo("ru-ru")), Description = "", Name = "Большак 5" }

Можете пожалуйста рассказать, что за @new? Погуглив узнал, что модификатор new скрывает реализацию базового класса и можно как бы переопределить класс без модификаторов virtual и override. Что-то типа того. Сильно не всматривался. Но про @new как то и не нашел.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public async Task<Bird> UpdateAsync(Bird bird) => await Task.Run(() =>
{
    Bird @new;
    using (DbContext context = createContext())
    {
        DbSet<Bird> birds = context.Set<Bird>();
        @new = birds.Find(bird.Id) ?? throw new Exception("Записи с таким Id нет.");
 
        @new.Name = bird.Name;
        @new.Description = bird.Description;
        @new.Arrival = bird.Arrival;
        @new.Departure = bird.Departure;
        @new.IsActive = false;
 
        birds.Update(@new);
        context.SaveChanges();
    }
    {
        GetObservableCollection().ReplaceOrAdd(b => b.Id == bird.Id, @new);
        birds.Local.FindEntry(bird.Id)!.State = EntityState.Unchanged;
    }
 
    return @new;
});

Когда подробней смотрел в класс BaseInpc, в методе Set стало не понятно, почему так. Вот строка - ArgumentException.ThrowIfNullOrWhiteSpace(propertyName);. Но у нас же propertyName имеет значение по умолчанию null.
Я так понял, что это если Set вызывается не из свойства. Во ViewModelBase.GetSet тоже самое.
Но разве может быть необходимость вызывать этот метод не из свойства?

C#
1
2
3
4
5
6
7
8
9
10
11
12
protected void Set<T>(ref T propertyFiled, T newValue, [CallerMemberName] string? propertyName = null)
{
    ArgumentException.ThrowIfNullOrWhiteSpace(propertyName);
    if (!object.Equals(propertyFiled, newValue))
    {
        T oldValue = propertyFiled;
        propertyFiled = newValue;
        RaisePropertyChanged(propertyName);
 
        OnPropertyChanged(propertyName, oldValue, newValue);
    }
}
Добавлено через 3 минуты
Элд Хасп, и да, если или когда начнете снова вносить изменения в проект, не забудьте пожалуйста запулить проект, были некоторые корректировки.

Добавлено через 15 минут
Также не нашел класс, в котором происходит регистрация объектов в DI-контейнер. В нем больше нет необходимости?

Добавлено через 8 минут
Цитата Сообщение от Элд Хасп Посмотреть сообщение
await vm.LoadAsync();
А какую функцию выполняет эта строка в CB класса App? Проследил метод до репозитория, там увидел строку - public async Task LoadAsync() => await Task.Run(birds.Load);

birds тут - это DbSet. То есть происходит загрузка в DbSet данных, так выходит?
Вот описание метода Load.
// Enumerates the query. When using Entity Framework, this causes the results of
// the query to be loaded into the associated context. This is equivalent to calling
// ToList and then throwing away the list (without the overhead of actually creating
// the list).
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
02.01.2025, 06:25  [ТС]
Также в AddBirdView есть ComboBox с привязкой к коллекции. Но последние две строки работают как-то странно. Они просто скрывают элементы.
XML
1
2
3
4
5
<ComboBox
          ItemsSource="{Binding BirdNameGroups, Mode=OneWay, Source={StaticResource mainVM}}"
          SelectedValue="{Binding Name}"
          SelectedValuePath="Name"
          DisplayMemberPath="Name">
Вот с ними.


Эффект, если удалить две последние строки.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
02.01.2025, 11:12
Цитата Сообщение от Vissmuy Посмотреть сообщение
нормально ли то, что MainViewModel взяла на себя функции из других ViewModels?
Да.
Нормально.
команда по добавлению сущности может потребоваться в любой дочерней VM, поэтому для уменьшения дублирования кода её можно перенести в основную VM. Если бы таких команд и других членов было бы очень много, то для упрощения привязок можно было предоставлять их через прокси свойства и в дочерней VM. Например, как это сделано со свойством BirdNameGroups.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Вот тут не понял, что значит их совокупность будет Моделью?
........................
Получается, что мне нужно будет два таких репозитория, один для Bird, как сейчас и второй для Set, так ведь?
Да. Будет два отдельных Репозитория.
Но это потребует внедрения механизма отслеживания изменений связанных сущностей.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Видел. А разве бд сама Id не устанавливает? Или это только в Sqlite Id самому нужно устанавливать?
В Методе Birds.Add - Id будет задаваться механизмами БД.
А при инициализации БД в методе её конфигурирования OnModelCreating метод HasData записывает сущности "как есть". Поэтому в данном случае нужно "руками" задать Id.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Можете пожалуйста рассказать, что за @new?
Просто название переменной. Правила именования:
Можно объявить идентификаторы, соответствующие ключевым словам C#, с помощью префикса @ идентификатора. @ не является частью имени идентификатора. Например, @if объявляет идентификатор с именем if.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Когда подробней смотрел в класс BaseInpc, в методе Set стало не понятно, почему так. Вот строка - ArgumentException.ThrowIfNullOrWhiteSpac e(propertyName);. Но у нас же propertyName имеет значение по умолчанию null.
Я так понял, что это если Set вызывается не из свойства. Во ViewModelBase.GetSet тоже самое.
Но разве может быть необходимость вызывать этот метод не из свойства?
Мы не можем запретить вызов этих методов из других мест.
Например, в методе ViewModelBase.SetSet, вызывается метод BaseInpc.Set.
Но эти методы бессмысленно вызывать, если не передавать имени свойства.
Проверить точно есть ли такое свойство можно, но это сложно. А вот проверить есть ли там хоть какое-то имя - можно.
ArgumentException.ThrowIfNullOrWhiteSpace(propertyName) и делает такую проверку.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Также не нашел класс, в котором происходит регистрация объектов в DI-контейнер. В нем больше нет необходимости?
Я его убрал.
На данном этапе вам нужно понять как создаются связи между слоями, объектами.
Когда поймёте, как это делается "руками", то при необходимости сможете внедрить DI-контейнеры.
DI контейнеры требуют начальной регистрации типов.
Я уже вам показал какие ошибки это может вызвать, если это делать в Code Behind.
Для удобства Конструирования XAML нужна регистрация слоёв в XAML. И это сложно сочетать с DI контейнерами.
Можно, но сложно. В данном случае у вас и так хватает сложностей в которых надо разобраться, поэтому добавлять сюда "разборки" с DI контейнерами посчитал преждевременным.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Цитата Сообщение от Элд Хасп Посмотреть сообщение
await vm.LoadAsync();
А какую функцию выполняет эта строка в CB класса App?
Асинхронный запуск загрузки БД.
Связывание локального кеша с View происходит сразу при запуске приложения.
Но локальный кеш пуст. Надо его загрузить.
Загрузка может занять длительное время. Поэтому её надо отделить от связывания и запустить асинхронно.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Также в AddBirdView есть ComboBox с привязкой к коллекции. Но последние две строки работают как-то странно. Они просто скрывают элементы.
Добавил в него список всех Bird с показом только их имени и передачей имени выбранного в текущей (новый) Bird.
Это просто для примера, так как не понял для чего он здесь.
Надо заменить на требуемую для вас логику.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
02.01.2025, 12:58  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
На данном этапе вам нужно понять как создаются связи между слоями, объектами.
Я так полазил в отладчике. Сейчас у нас, получается, есть тот же Singleton, но реализованный через словарь.
Когда я переключаю представление, то сначала срабатывает метод Get, а потом уже в Set в старое значение по ссылке присваивается новое.

Добавлено через 15 минут
А с какой целью в этой команде производится клонирование? И немного не пойму, что значит индикация выполнения метода.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public RelayCommand DeleteBirdCommand => GetCommand<Bird>
(
    async bird =>
    {
        // Здесь нужна индикация выполнения метода? 
 
        Bird bird1 = bird.Clone();
        bird1.Departure = Departure;
        bird1.IsActive = false;
 
        Bird bird2 = await birdsRepository.UpdateAsync(bird);
        //Birds.Replace(b => b == bird1, bird2);
    },
    bird => bird.IsActive
);
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
02.01.2025, 13:39
Цитата Сообщение от Vissmuy Посмотреть сообщение
если или когда начнете снова вносить изменения в проект, не забудьте пожалуйста запулить проект, были некоторые корректировки.
Скинул ещё Pull request с исправлением ошибки и мелкими изменениями.

Добавлено через 22 минуты
Цитата Сообщение от Vissmuy Посмотреть сообщение
А с какой целью в этой команде производится клонирование?
Я уже писал выше, что тип Bird несёт двойную функцию:
Цитата Сообщение от Элд Хасп Посмотреть сообщение
И объявлен класс (запись) Bird. Bird несёт двоякую функцию. При получении от Репозитория - это сущность связанная и отслеживаемая Репозиторием. Менять свойства этих сущностей нельзя - это может повлечь баги в работе Репозитория. При создании Bird вне репозитория -это обычный DTO, который используется как контейнер для передачи значений.
В данной реализации, Репозиторий выполнен по "дружественной" схеме. Поэтому нет реализации строгой безопасности.
Если реализацию делать безопасной, то требуется использование иммутабельных DTO.
Второй нюанс - это "когда должны отобразиться изменения в View?"
View - это представление Модели. Поэтому будет некорректным, если мы изменим свойства сущности представляемой в View до того как эти изменения будут зафиксированы в Модели. Поэтому создаём дубль, вносим в него изменения, фиксируем изменения в Модели и только после фиксации новая сущность, с уже новыми данными, поступит в View для представления.

Цитата Сообщение от Vissmuy Посмотреть сообщение
И немного не пойму, что значит индикация выполнения метода.
Например, если метод выполняется очень долго, то отобразить прогресс загрузки, отключить кнопку и т.п.
В данной реализации в этом нет необходимости, поскольку SQLite локальна и метод выполняется за миллисекунды. По сути здесь даже нет необходимости в асинхронном методе. Синхронного было бы вполне достаточно.
Но возможно у вас есть какая-то другая рабочая реализация, в которой это действительно долго выполняется.

Добавлено через 3 минуты
Цитата Сообщение от Vissmuy Посмотреть сообщение
Я так полазил в отладчике. Сейчас у нас, получается, есть тот же Singleton, но реализованный через словарь.
Не понял о каком фрагменте кода вы это пишите.

Добавлено через 2 минуты
О работе свойства Current?
Это не Синглтон, но DI заменён на явное создание дочерних VM.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
02.01.2025, 14:13  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
О работе свойства Current?
И о нем тоже, но я про словарь из ViewModelBase. Сначала же идет проверка, есть ли в нем нужное свойство.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private readonly Dictionary<string, object?> _properties = new();
 
protected T? Get<T>([CallerMemberName] string propertyName = "")
{
    T? value;
    if (_properties.TryGetValue(propertyName, out object? _prop))
    {
        value = (T?)_prop;
    }
    else
    {
        value = default;
    }
    return value;
}
 
protected void Set<T>(T newValue, [CallerMemberName] string? propertyName = "")
{
    ArgumentException.ThrowIfNullOrWhiteSpace(propertyName);
    T? oldValue = Get<T>(propertyName);
    _properties[propertyName] = newValue;
    Set(ref oldValue, newValue, propertyName);
}
Добавлено через 2 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Например, если метод выполняется очень долго, то отобразить прогресс загрузки, отключить кнопку и т.п.
А каким образом такое делается? В реальной бд в этом проекте у меня под 300 записей.
А есть еще один проект. Этот учебный как раз таки слизан с того, там по сути все те же проблемы. Потом как этот учебный изучу, будет интересно попробовать переделать его. Так вот в нем 4к объектов в бд. На хостинге там секунд 6 грузится.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
02.01.2025, 14:21
Цитата Сообщение от Vissmuy Посмотреть сообщение
Сначала же идет проверка, есть ли в нем нужное свойство.
Да.
Если нет, то возвращается значение по умолчанию для типа.
Изменить значение в словаре можно только методом Set.

Но это не имеет отношения к Singleton. Напротив, пред усматривается хранение разных экземпляров.

Цитата Сообщение от Vissmuy Посмотреть сообщение
А каким образом такое делается? В реальной бд в этом проекте у меня под 300 записей.
300 записей это "ни о чём".
Здесь может иметь значение не сколько запись, а удалённая БД это или локальная, есть ли многопользовательский режим. Если удалённая и/ил многопользовательская, то как работает с ней сервис и т.п.
А для локальной однопользовательской БД даже для 3млн. записей выполнение будет практически мгновенным.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
02.01.2025, 14:26  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
уже писал выше, что тип Bird несёт двойную функцию:
Ладно, может когда-нибудь пойму, но пока я не вижу разницы - передавать в бд клон или оригинал. Просто в проекте работает и так, и так.
Миниатюры
Как внедрить модель во viewModel?  
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
02.01.2025, 14:27  [ТС]
Забыл вложение удалить, там испорченный коммент из pull request.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
02.01.2025, 15:15
Цитата Сообщение от Vissmuy Посмотреть сообщение
Просто в проекте работает и так, и так.
В данном случае, сохранение происходит фактически мгновенно и не бывает отказов в сохранении. Поэтому разницу заметить без специальных тестов невозможно. Но это разница есть.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Забыл вложение удалить, там испорченный коммент из pull request.
Странно.
У меня вроде всё ок.

Добавлено через 5 минут
Проверил сейчас свой Fork - почему-то он не совпадает с моим локальным Репозиторием.
Пытаюсь разобраться.

Добавлено через 2 минуты
А....
Это я косяканул.
Fork обновил, а локальный реп нет.

Добавлено через 24 минуты
Цитата Сообщение от Vissmuy Посмотреть сообщение
там испорченный коммент из pull request.
Похоже у вас в настройках Студии не задан UTF-8. Поэтому Юникод искажается.
Можно скопировать из предыдущих комитов.

Добавлено через 26 секунд
Отправил исправление конфликта слияния и коммента.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
03.01.2025, 15:15
Vissmuy, отправил изменения:
Реализация с иммутабельным Bird. Для демонстрации добавлена кнопку удаления (Remove) Bird.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
05.01.2025, 09:23  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
отправил изменения:
Вижу, что теперь поля у entity Bird задаются через конструктор. Почему теперь так? В чем отличие от предыдущего варианта?

Что означают эти строки в BirdsWPF.csproj?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ItemGroup>
    <None Remove="wwwroot\Images\awake_adium_bird_20653.ico" />
    <None Remove="wwwroot\Images\ex.png" />
    <None Remove="wwwroot\Images\file_basket_red.png" />
    <None Remove="wwwroot\Images\ok.png" />
    <None Remove="wwwroot\Images\shutdown.png" />
  </ItemGroup>
 
<ItemGroup>
    <Resource Include="wwwroot\Images\awake_adium_bird_20653.ico" />
    <Resource Include="wwwroot\Images\ex.png" />
    <Resource Include="wwwroot\Images\file_basket_red.png" />
    <Resource Include="wwwroot\Images\ok.png" />
    <Resource Include="wwwroot\Images\shutdown.png" />
  </ItemGroup>
Также хотел узнать для чего такие вот файлы нужны?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 public class BirdExtension : MarkupExtension
 {
     /// <summary>Целочисленный идентификатор.</summary>
     public int Id { get ; set ; }
 
     /// <summary>Имя.</summary>
     public string? Name { get; set; }
 
     /// <summary>Описание.</summary>
     public string? Description { get; set; }
 
     /// <summary>Прибытие. Для простоты ввода по умолчанию установлена текущая дата. <br/>
     /// (Чтобы руками не заполнять поле)</summary>
     public DateOnly Arrival { get; set; }
 
     /// <summary>Отправление.</summary>
     public DateOnly Departure { get; set; }
 
     /// <summary>Активная Птица.</summary>
     public bool IsActive { get; set; }
 
     public override object ProvideValue(IServiceProvider serviceProvider)
     {
         return new Bird(Id, Name, Description, Arrival, Departure, IsActive);
     }
 }
По поводу удаления. А как метод Find находит сущность в другом единоразовом контексте? Или контексты делятся между собой данными, хранящимися в DbSet?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public async Task DeleteAsync(int id) => await Task.Run(() =>
{
    // Удаление сущности через другой, одноразовый Контекст БД.
    using (DbContext context = createContext())
    {
        DbSet<Bird> birds = context.Set<Bird>();
        Bird brd = birds.Find(id) ?? throw new Exception("Записи с таким Id нет.");
        birds.Remove(brd);
        context.SaveChanges();
    }
    {
        // Обновление локального кеша, если там есть сущность с таким Id.
        Bird? brd = birds.Find(id);
        if (brd is not null)
        {
            birds.Entry(brd).Reload();
        }
    }
});
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
05.01.2025, 11:26
Цитата Сообщение от Vissmuy Посмотреть сообщение
Вижу, что теперь поля у entity Bird задаются через конструктор. Почему теперь так? В чем отличие от предыдущего варианта?
Это пример использования иммутабельного DTO. В иммутабельном можно задать только через конструктор.

Иммутабельный DTO - это более "чистая" реализация MV*.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Что означают эти строки в BirdsWPF.csproj?
Не забивайте голову.
Это Студия так настраивает проект.
Можно делать это и "руками", но это совершенно отдельная область знаний не имеющая отношения к Шарпу.

Обращаюсь к ней только, если возникают какие-то шибко нестандартные проблемы при сборке, переносе проектов.

Цитата Сообщение от Vissmuy Посмотреть сообщение
Также хотел узнать для чего такие вот файлы нужны?
Это называется "Расширение Разметки XAML".
Создаются для упрощения XAML.
В данном случае, надо создать экземпляр иммутабельного типа Bird. У него нет конструктора по умолчанию. Создать его можно или при помощи ObjectDataProvider (Провайдеры данных. ObjectDataProvider | METANIT.COM), либо через свое "самописное" расширение разметки.

Цитата Сообщение от Vissmuy Посмотреть сообщение
По поводу удаления. А как метод Find находит сущность в другом единоразовом контексте? Или контексты делятся между собой данными, хранящимися в DbSet?
Find ищет только в своём контексте.
В данном случае, в новом контексте происходит запрос на удаление.
Если запрос удачно выполнился (не было исключения), то с помощью Find ищется такая же сущность в постоянном контексте. Если она там есть, то происходит её перезагрузка. А так как на этот момент в БД она уже удалена, то перезагрузка приводит и к удалению сущности из постоянного контекста.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
07.01.2025, 17:56
Vissmuy, отправил ещё изменения:
Обновление сущности с уведомлением привязок через PropertyDescriptor об изменении Bird.IsActive. Вариант показан, как альтернативная реализация. В данной задаче, на практике, более лучшим вариантом будет замена сущности.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
08.01.2025, 12:12  [ТС]
Есть такая вот проблема. Когда я нажимаю на кнопку "Удалить", то сначала выводится значение по умолчанию для DateOnly, но после переключения вида значение становится нормальным. Как сделать, чтобы значение сразу было нужным?

Команда
C#
1
2
3
4
5
6
7
8
9
10
11
public RelayCommand DeleteBirdCommand => GetCommand<Bird>
(
    async bird =>
    {
        // Создание клона с внесёнными изменениями, которые отобразятся только после сохранения в Репозитории.
        Bird bird1 = new(bird.Id, bird.Name, bird.Description, bird.Arrival, Departure, false);
 
        await birdsRepository.UpdateAsync(bird1);
    },
    bird => bird.IsActive
);
Перед нажатием кнопки


Сразу после нажатия кнопки


После переключения вида.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
08.01.2025, 14:19
Цитата Сообщение от Vissmuy Посмотреть сообщение
Как сделать, чтобы значение сразу было нужным?
Я писал в комменте к изменению, что это пример альтернативного варианта заставляющего привязки обновиться.
Используется механизм PropertyDescriptor.
В этом варианте для примера было уведомление только о свойстве IsActive.

Сейчас скинул вариант с уведомлением обо всех свойствах.

На данный момент я показал вам три варианта:
1) С внедрением INPC в мутабельную сущность для EF;
2) С заменой иммутабельной сущности в локальном кеше Контекста;
3) С вызовом обновления привязок к сущности без внедрения INPC.


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

Есть ещё "безопасные" реализации. В них потребуется несколько больше кода, так как сущности используемые Контекстом ни при каких обстоятельствах не выходят из Репозитория. Даже если используется один и тот же тип, то происходит его копирование или отсоединение от контекста перед передачей.
В такой реализации потребуется уже "ручное" создание локального кеша и, скорее всего, события в Репозитории уведомляющие об его изменении.
Не смотря на более сложную реализацию, такие "безопасные" Репозитории более предпочтительны и считаются более качественным исполнением.
В коллективной работе практически всегда используются именно "безопасные" реализации (не только Репозиториев).
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
08.01.2025, 22:25
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Есть ещё "безопасные" реализации.
Vissmuy, скинул пример такой реализации.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
08.01.2025, 22:25

Mvvm ViewModel в ViewModel
Есть одно окошко там много вкладок, в каждой вкладке есть подвкладки и там много разных таблиц и кнопок. ViewModel будет просто огромной,...

Как внедрить БД в exe на VB 6.0
Hi! Есть база на Access 7.0 и проект на VB 6.0. Можно ли как-нибудь всунуть .mdb файл в exe-шник, чтобы никто не мог его открыть и...

Как внедрить функцию
Здравствуйте, есть программа которая считает в edit сколько открывающихся скобок и закрывающихся. мой код выглядит так: procedure...

как с внедрить свой код
Всем привет! Не подскажите как можно создать свою папку в вордпресссом и внедрить свой код. Сейчас есть сайт на вордпресс, там нужно...

Как внедрить ProgressBar в макрос
Добрый день! почитал соседние темки на эту тему, нашел у товарища analyst архив с замечательными формами ProgressBar но так как...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
120
Ответ Создать тему
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru