Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.50/4: Рейтинг темы: голосов - 4, средняя оценка - 4.50
0 / 0 / 0
Регистрация: 19.07.2023
Сообщений: 4

MVVM DI crud и иже с ними

12.03.2024, 13:18. Показов 796. Ответов 6
Метки crud, di, mvvm (Все метки)

Студворк — интернет-сервис помощи студентам
Появились некоторые вопросы непосредственно по MVVM архитектуре приложения, на которые никак не нашла ответов, да и примеров нормальных.
Вопросы безотносительно конкретных технологий, а скорее в выбираемых подходах:

Вопрос №1, как быть с DI:
Пусть у нас есть классическая реализация MVVM
Базовая VM
C#
1
2
3
4
5
6
7
8
9
internal abstract class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Допустим у нас есть несколько вкладок со списками разных данных, вида:

C#
1
2
3
4
5
6
internal class Page1ViewModel<T> : ViewModel
{
        public Page1ViewModel(IService<T> service) 
        {
        }
}
Но нам необходимо открыть на редактирование один из элементов списка, и при это, там логика зависит от других сервисов нежели тот что используется для отображения.
Как тут быть в случае с DI? В конструкторе прокидываем сервисы, создаем VM и через WindowsManager создаем окно уже? а информацию об объекте как туда же прокинуть в таком случае? внедрение через метод/параметр получается?
Есть какие нибудь красивые примеры для этого? Не хочется руками прокидывать сервисы для создания вм на редактирование..

Вопрос №2:
Про модель приложения, что лучше, куча сервисов с чем то взаимодействующих которые по отдельности прокидываются в каждую VM (тут как раз к первому вопросу относится), или сделать одну модель с разными состояниями: условно список с моделями отображаемой текущей вкладки, модели открытых окон и тд, в которой как раз один раз через DI проинициализировать взаимодействие с API (несколько десятков интерфейсов, в разных окнах надо по идее с разными частями взаимодействовать) настройками пользователя и тд. После чего в VM уже пользоваться Model как в случае с локатором? Тогда по идее можно будет создать VM окна для редактирования, обращаясь к свойствам модели которые являются реализациями интерфейсов передав туда Id объекта и ссылку на модель. Хоть локатор и антипаттерн но тут оно вроде должно как раз таки сделать удобнее.

Вопрос №3:
И вопрос про банальный круд. Открыли мы окно создания, заполнили все поля. Нажимаем сохранить - где лучше перекладывать заполненные поля в окне (с хз каким поведением) в модельку? ) Или модель окна это модель окна, а отображаемые пля - это часть касающаяся представления модели в данном виде, и сразу мапить VM модели, а дальше автомапером в модель и отправлять в API ?

В общем все вопросы из разряда "как сделать красиво"..
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
12.03.2024, 13:18
Ответы с готовыми решениями:

Строки и иже с ними
Как бы странно не звучало, но:я не могу нормально очистить строку в C++ пытаюсь присвоить признак конца строки первому символу...

Speed2.ru и иже с ними
Вчера началось, обычный откат в восстановлении системы не помог. (вижу не я 1 с ним тут воюю) Логи ниже.

SocketServerClientConnect и иже с ними
Есть SocketServer. При подключении клиента создается стековый объект (не знаю как назвать), который содержит все текущие рабочие...

6
 Аватар для Andrey-MSK
3313 / 2200 / 387
Регистрация: 14.08.2018
Сообщений: 7,404
Записей в блоге: 4
12.03.2024, 14:17
Цитата Сообщение от sgsi Посмотреть сообщение
В конструкторе прокидываем сервисы, создаем VM и через WindowsManager создаем окно уже?
Ага
Цитата Сообщение от sgsi Посмотреть сообщение
а информацию об объекте как туда же прокинуть в таком случае?
Через интерфейс VM, который зарегистрирован в DI как тип VM
C#
1
.AddTransient<IEObjectsDialogVM, EObjectsDialogVM>()
Добавлено через 2 минуты
sgsi, Вот небольшой кусочек - Разделение функционала между слоями MVVM на примере создания простого WPF приложения с БД

Добавлено через 7 минут
Цитата Сообщение от sgsi Посмотреть сообщение
Вопрос №3:
Я делаю проще. Подготавливаю данные в VM и потом вызываю метод репозитория для взаимодействия с БД. В роли модели у меня выступает сервер СУБД, там всё считается, готовится и т.д.

Добавлено через 14 минут
sgsi, Вот подробнее пример - Создание диалоговых окон, согласно паттерну MVVM
1
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
12.03.2024, 18:24
Цитата Сообщение от sgsi Посмотреть сообщение
Но нам необходимо открыть на редактирование один из элементов списка, и при это, там логика зависит от других сервисов нежели тот что используется для отображения.
Как тут быть в случае с DI? В конструкторе прокидываем сервисы, создаем VM и через WindowsManager создаем окно уже? а информацию об объекте как туда же прокинуть в таком случае? внедрение через метод/параметр получается?
Есть какие нибудь красивые примеры для этого? Не хочется руками прокидывать сервисы для создания вм на редактирование..
В текущем экспериментальном решении у меня как-то так получается:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public async Task EditItemCommandHandler()
{
    //Создать VM окна редактирования так.
    var editVm = _viewModelFactory.Create<EditVm>(vm => vm.ItemId = this.SelectedItem.Id);
 
    //Или вообще так (более однозначно, чем предугадывать, что надо именно ItemId заполнить).
    //var editVm = _viewModelFactory.Create<EditVm>(new EditVm.Args(this.SelectedItem.Id));
 
    //Найти, создать, связать с VM и показать окно. 
    //И дождаться результата (null или значение конкретного типа).
    var result = await _viewManager.ShowDialog(editVm);
 
    //Далее можно:
    //    либо тут обновить отображаемую коллекцию на базе 'result'
    //    либо тут ничего не делать, получить от Модели событие, что айтем обновился и обновить в обработчике
}
При этом у обеих VM (и у той, из которой вызывается окно, и у 'EditVm') - свой собственный набор зависимостей, автоматически предоставляемых в конструктор через DI-контейнер.

Цитата Сообщение от sgsi Посмотреть сообщение
Про модель приложения, что лучше, куча сервисов с чем то взаимодействующих которые по отдельности прокидываются в каждую VM (тут как раз к первому вопросу относится), или сделать одну модель с разными состояниями: условно список с моделями отображаемой текущей вкладки, модели открытых окон и тд
Модели окон? Вроде, модели вообще никаким образом ни про окна, ни про VM не знают. Как правило, они регистрируются в DI-контенере как Singleton и одна модель может использоваться одновременно сразу в нескольких VM. Разбивать на маленькие или нет - вопрос неоднозначный. Я разбиваю, т.к. в этом случае контракт отдельной VM становится более однозначным, т.е. сразу понятно какие конкретно части (всей большой модели приложения) требуется конкретно для этой VM. Если предполагается ещё и тесты на VM делать, то, наверное, это так же будет плюсом, т.к. если VM получает на вход "всю модель приложения" (по сути, Локатор), то совершенно не понятно какие именно части этой модели надо мокать для конкретного теста.
* но сам я тесты для VM не делаю, чисто ради эксперимента пару раз, так что точно тут не скажу

Цитата Сообщение от sgsi Посмотреть сообщение
И вопрос про банальный круд. Открыли мы окно создания, заполнили все поля. Нажимаем сохранить - где лучше перекладывать заполненные поля в окне (с хз каким поведением) в модельку? ) Или модель окна это модель окна, а отображаемые пля - это часть касающаяся представления модели в данном виде, и сразу мапить VM модели, а дальше автомапером в модель и отправлять в API ?
Вроде как в модельку ничего перекладывать не надо, у модельки должен быть метод, изменяющий состояние согласно какой-то бизнес логике, ну т.е. (хотя, наверное, возможны и другие подходы):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
public async Task OkCommandHandler()
{
    var itemDto = new ItemDto()
    {
        Id = this.Id,
        Name = this.Name,
        AnnuledAt = this.AnnuledAt
    };
 
    var result = await _itemsModel.Update(itemDto);
 
    //Какая-то логика обработки ошибок/исключений и т.д.
}
При этом, если метод 'Update' не смог в силу любых причин, то состояние модели (и данных приложения) останестя без изменений.
А если 'Update' отработал корректно, то модель уведомит всех заинтересованных соответствующим событием (и они смогут обновить свои состояния по актуальным данным модели).

Фактическая реализация бизнес-логики (т.е. метода 'Update') при этом может быть где угодно - я прямо на стороне клиента (с локальной базой/файлом) и где-то на сервере, с перенаправлением запроса туда через HTTP/gRPC API.

Добавлено через 21 минуту
Ещё такой нюанс - там выше используется сервис '_viewModelFactory', запрашиваемый через конструктор. Вот это в каком-то смысле тоже антипатерн наподобии СервисЛокатора, т.к. делает контракт VM слишком расплывчатым, ну т.е.:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public sealed class SomeViewModel
{
    private readonly IViewModelFactory _viewModelFactory;
 
    public SomeViewModel(IViewModelFactory viewModelFactory)
    {
        _viewModelFactory = viewModelFactory;
    }
 
    private void DoOther()
    {
        //И вот тут вот можно запросить абсолютно любую VM.
        //И при взгляде на конструктор это совершенно не очевидно.
        //Т.е. чтобы понять, какие конкретно зависимости есть у этой 'SomeViewModel',
        //придётся найти все обращения к фабрике и посмотреть что именно через неё запрашивается.
        //А вдруг у нас вообще доступа к исходникам этой VM нет? И останется только гадать.
 
        var otherVm = _viewModelFactory.Create<OtherVm>();
        var nextVm = _viewModelFactory.Create<NextVm>();
    }
 
}
И более чистый и красивый вариант будет выглядеть скорее так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public sealed class SomeViewModel
{
    private readonly IViewModelFactory<OtherVm> otherVmFactory;
    private readonly IViewModelFactory<NextVm> nextVmFactory;
 
    public SomeViewModel(
        IViewModelFactory<OtherVm> otherVmFactory,
        IViewModelFactory<NextVm> nextVmFactory)
    {
        _otherVmFactory = otherVmFactory;
        _nextVmFactory = nextVmFactory;
    }
 
    private void DoOther()
    {
        var otherVm = _otherVmFactory.Create();
        var nextVm = _nextVmFactory.Create();
    }
}
1
Эксперт .NET
 Аватар для Wolfdp
3789 / 1766 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
12.03.2024, 21:51
набросаю мыслей, всё бегло, так что не воспринимайте как полноценный ответ.

Цитата Сообщение от sgsi Посмотреть сообщение
public Page1ViewModel(IService<T> service)
Если это таки WPF, то я бы кидал сервисы таки в Command. Модель от этого разгружается, а команда стает более лаконичной.

Цитата Сообщение от sgsi Посмотреть сообщение
Есть какие нибудь красивые примеры для этого? Не хочется руками прокидывать сервисы для создания вм на редактирование..
IoC, ServiceProvider и прочее. Можно пойти вообще следующим путем:
- на старте приложения подымаем host, все настройки, конфигурации и прочие идут с ним
- на Exit стопаем

Можно подключить только либу реализующую DI и развлекаться с ней.

Цитата Сообщение от sgsi Посмотреть сообщение
Про модель приложения, что лучше, куча сервисов с чем то взаимодействующих которые по отдельности прокидываются в каждую VM
Сервисов должно быть не "куча", а инкапсулирующих определённую логику, к которой вы будете обращаться. Количество != качество. И "да" -- создаем сервисы, и прокидываем в VM.

Цитата Сообщение от sgsi Посмотреть сообщение
или сделать одну модель с разными состояниями
Звучит как "божественный класс". Я бы так не делал. Обычно объединяют что-то в базовое с схожей логикой, но не более. И то не всегда это оправданно.

Цитата Сообщение от sgsi Посмотреть сообщение
Или модель окна это модель окна, а отображаемые пля - это часть касающаяся представления модели в данном виде, и сразу мапить VM модели, а дальше автомапером в модель и отправлять в API ?
В идеале -- да. Но реально утомляет: подель для DAL, модель для BL... Плюс однотипные мапинги. "Добавить поле" заставляет в каждом классе его добавлять в каждом слое. Зато если где-то меняется хранение, но при этом UI не должно затрагивать -- легче править. Ну или переиспользовать нижний слой в другом приложении (это когда он разбито на отдельные проекты)
1
0 / 0 / 0
Регистрация: 19.07.2023
Сообщений: 4
12.03.2024, 22:24  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Вот небольшой кусочек
Ага. внедрение через свойство. Тоже так делала. Но, не понравилось, что родительской VM получается нужно знать о том что же проставить и заполнить. Как вариант еще для всех VM по умолчанию добавляла в интерфейс метод Build.
т.е. получалось как то так:
C#
1
2
3
IMiMReportWindowVM vm = _viewModelFactory.CreateViewModel<IMiMReportWindowVM>();
vm.Build(SelectedEObject)
_windowService.ShowWindow(vm);
и внутри уже на основе прокинутых туда сервисов, данный метод инициализировал уже всё что нужно. И получалось что уже vm диалогового окна как раз таки и знала о своих зависимостях и реализациях.
Цитата Сообщение от kotelok Посмотреть сообщение
Модели окон? Вроде, модели
Это была условность, скорее что то на что можно спроецировать текущее состояние приложения. Если очень упрощенно. то как то так:
Кликните здесь для просмотра всего текста
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
27
28
29
30
31
32
33
34
public class MainModel
{
    private LocalDatabase _localDatabase;
 
    public MainModel()
    {
        AppSettings = SettingsStorage.LoadSettings(Environment.AppSettingsFilePath);
        if (AppSettings.Advanced.LoggingEnabled)
        {
            EnableLogging();
        }
    }
    
    public AppSettings AppSettings { get; }
    
    public int CurrentBookId { get; private set; }
    
    public Task<Book> LoadBookAsync(int bookId)
    {
        Logger.Debug($"Loading book with ID = {bookId}.");
        CurrentBookId = bookId;
        return LoadItemAsync(_localDatabase.GetBookById, bookId);
    }
    
    public Task<List<Book>> SearchAsync(string searchQuery, IProgress<SearchProgress> progressHandler, CancellationToken cancellationToken)
    {
        return SearchItemsAsync(_localDatabase.Books, searchQuery, progressHandler, cancellationToken);
    }
    
    public Task<DatabaseStatus> OpenDatabase(string databaseFilePath)
    {
    ...
    }
}

ну т.е. как раз своего рода собирает в себе всю логику приложения, а VM и V уже для отображения, валидаций (на уровне GUI) и тд...
Да, это уже мало пригодно для тестирования, т.к. бизнес-логика собирается в MainModel. Но написание тестов это вообще отдельная задача. особенно для бизнес процессов. Тут CQS/CQRS интересно смотрится, с их идеей декорирования хэндлеров, но если очень много - то становится сложно находить зависимости..
Цитата Сообщение от kotelok Посмотреть сообщение
т.к. если VM получает на вход "всю модель приложения" (по сути, Локатор), то совершенно не понятно какие именно части этой модели надо мокать для конкретного теста.
Тут не с чем спорить, отсюда те же сомнения и все вопросы. Чистый код/Чистая архитектура, совершенно не закрывают данные вопросы, а хорошие примеры с соблюдением парадигм сложно находить. И раз за разом в какой то момент код начинает скатываться.

Спасибо за ответы и примеры. Есть над чем подумать
0
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
13.03.2024, 07:16
Цитата Сообщение от sgsi Посмотреть сообщение
Тут CQS/CQRS интересно смотрится, с их идеей декорирования хэндлеров, но если очень много - то становится сложно находить зависимости..
Один из последних проектов сделан через это (WPF + WebAPI). Хэндлеров много, они там вообще на каждый чих. Но т.к. и клиент, и сервер на .NET, то и поддержка согласованности клиента+сервера упрощается, и сам код обращения к API тоже становится проще, и, если требуется, подмена/расширение (например, для кэширования) реализации отдельных API-методов на клиенте делается прозрачно.

А что за проблема с поиском зависимостей? Хэндлеры разложены по категориям (по папкам), могут быть, при необходимости, и вовсе по разным сборкам разнесены, у каждого свой индивидуальный набор зависимостей.
0
 Аватар для Andrey-MSK
3313 / 2200 / 387
Регистрация: 14.08.2018
Сообщений: 7,404
Записей в блоге: 4
13.03.2024, 08:32
Цитата Сообщение от sgsi Посмотреть сообщение
C#
1
2
3
4
5
6
7
8
public MainModel()
 {
 AppSettings = SettingsStorage.LoadSettings(Environment.AppSettingsFilePath);
 if (AppSettings.Advanced.LoggingEnabled)
 {
 EnableLogging();
 }
 }
А зачем модели знать про конфигурацию приложения? Не проще запустить конфигурацию как сервис и прокидывать её куда надо?
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
27
28
29
30
31
32
33
34
35
36
public partial class App : Application
{
 
    public IServiceProvider Services { get; }
    public IConfiguration Configuration { get; }
 
    public static new App Current => (App)Application.Current;
 
    public App()
    {
        Configuration = GetConfiguration();
        Services = ConfigureServices();
 
        InitializeComponent();
    }
 
    private static IConfiguration GetConfiguration()
    {
        IConfigurationBuilder builder = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddJsonFile("appsettings.json");
 
        return builder.Build();
    }
 
    private static IServiceProvider ConfigureServices()
    {
        ServiceCollection services = new ServiceCollection();
 
        services
            .AddSingleton(Current.Configuration)
            .AddSingleton<IMainDA, MainDA>();
            
        return services.BuildServiceProvider();
    }
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
13.03.2024, 08:32
Помогаю со студенческими работами здесь

Шаблоны, буферизация, иже с ними.
Здравствуйте, уважаемые. Нахожусь сейчас на самом начальном уровне изучения такого замечательного языка программирования как ПХП и в своем...

Маги, гадания и иже с ними
Праздный интерес - как Вы лично относитесь к форумам эзотерической направленности, какова Ваша реакция на посты вроде &quot;Я знаю...

XBee/ZigBee и иже с ними
Всем доброго времени суток! Использую в проекте для связи модули Xbee (Xbee PRO), столкнулся с граблями. Щупаю вообще первый раз их,...

TeamViewer, AnyDesk и иже с ними
Подскажите, как, на ваш взгляд, работают данные программы? Есть 3 части программы: главная (главный сервер, на котором находятся списки...

платная регистрация в Ya M@ и иже с ними
Подскажите плиззз стоит ли проходить платную регистрацию в каталогах, в частности сейчас стоит вопрос о том что б зарегистрироваться на...


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

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru