Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.99/116: Рейтинг темы: голосов - 116, средняя оценка - 4.99
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16164 / 11284 / 2891
Регистрация: 21.04.2018
Сообщений: 33,169
Записей в блоге: 2

INPC (INotifyPropertyChanged) и получение данных из Модели [WPF, Элд Хасп]

27.01.2019, 17:51. Показов 24940. Ответов 63

Лучший ответ Сообщение было отмечено Элд Хасп как решение

Решение

Студворк — интернет-сервис помощи студентам
Тема из цикла Готовые решения, примеры и рекомендации начинающим на WPF [Элд Хасп]

MVVM состоит из трёх раздельных частей. "Знания" этих частей друг о другу ограничены. View знает только о VM, VM только Model, а Model знать никого не желает.

В такой схеме передача данных от View в Model проблем не вызывает. Ограничения есть только для передачи данных от View к VM. Для отправки данных View обращается к Set методу нужного свойства VM (к которому осуществлена привязка), поэтому привязывать свойства View можно только к публичным свойствам VM. Привязать к полям или методам VM невозможно.

Если взять VM из примера в WPF команды и MVVM. Часть 1. пост#15, то свойства First, Second можно упростить до автосвойств и на функционировании приложения это ни как не скажется (сброс свойства Result в данном случае можно опустить)
C#
9
10
public int First { get; set; }
public int Second { get; set; }
Получение же данных происходит иным образом. Так как у VM нет знаний о View, она сама не может "лезть" в View и менять там значения свойств. View сама запрашивает у VM нужные данные вызывая метод Get нужного свойства VM.

Но как определить View, что данные изменились и их надо заново запросить? Для этого VM должна известить View о том какие данные были изменены. Это извещение (событие) для WPF View должно происходить через реализацию интерфейсов INotifyPropertyChanged (INPC) для свойств и INotifyCollectionChanged (INCC) для коллекций (здесь рассматривать не буду).

Дефолтная реализация реализация интерфейса INPC состоит всего из одной строчки
C#
1
public event PropertyChangedEventHandler PropertyChanged;
Но для удобства использования обычно вводят ещё и метод для вызова этого события
C#
1
2
3
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName]string propertyName="") 
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Атрибут CallerMemberName введён с Framework 4.5. Он позволяет не указывать имя свойства, если вызов происходит из Set метода этого свойства. В указанном примере это используется в свойстве Result
C#
1
2
3
4
5
6
7
8
9
        public int? Result
        {
            get => _result;
            private set
            {
                _result = value;
                OnPropertyChanged();
            }
        }
Использование без параметра OnPropertyChanged() равносильно OnPropertyChanged("Result")

Но только такое использование INPC необязательно. Можно заменить Result на автосвойство и вызывать OnPropertyChanged при записи в него нового значения.
C#
1
2
3
4
5
6
7
8
9
        public int? Result { get; private set;  }
        private void OnCalculate(object parameter)
        {
            if (parameter is OperatorsEnum Operator)
                Result = model.Calculate(First, Operator, Second);
            else
                Result = null;
            OnPropertyChanged("Result");
        }
В указанном в WPF команды и MVVM. Часть 1. пост#15 примере кнопка по сути не нужна. Она была введена для демонстрации команд. Изменим пример - убрав кнопку и используя INPC для обновления Result.

В VM при изменении значений свойств First, Second и SelectOperator будем отправлять в View сообщение об изменении свойства
Result. А в свойстве Result пропишем обращение к свойствам Модели
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    /// <summary>Класс Модели Представления</summary>
    public class CalcViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName]string propertyName = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        #region Секция свойств 
        private int _first;
        private int _second;
        private OperatorsEnum _selectOperator;
 
        public int First
        {
            get => _first;
            set
            {
                if (_first != value)
                {
                    _first = value;
                    OnPropertyChanged("Result");
                }
            }
        }
        public int Second
        {
            get => _second;
            set
            {
                if (_second != value)
                {
                    _second = value;
                    OnPropertyChanged("Result");
                }
            }
        }
 
        public OperatorsEnum SelectOperator
        {
            get => _selectOperator;
            set
            {
                _selectOperator = value;
                OnPropertyChanged("Result");
            }
        }
 
        public int? Result => model.IsCanExecuteCalculate(First, SelectOperator, Second)
            ? model.Calculate(First, SelectOperator, Second)
            : null;
 
        public IEnumerable<OperatorsEnum> Operators => model.GetOperators();
        #endregion
 
        CalcModel model = new CalcModel(); // Ссылка на модель
    }
XML
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
<Window x:Class="INPCinModel.CalcWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:INPCinModel"
        mc:Ignorable="d"
        Title="Калькулятор (Calc)" SizeToContent="WidthAndHeight">
    <Window.DataContext>
        <local:CalcViewModel/>
    </Window.DataContext>
    <StackPanel Orientation="Horizontal">
        <TextBox VerticalAlignment="Center" 
                 Text="{Binding First, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
                 Margin="5" MinWidth="80"/>
        <ComboBox VerticalAlignment="Center" 
                  ItemsSource="{Binding Operators}"
                  SelectedItem="{Binding SelectOperator}"
                  Margin="5" MinWidth="80"/>
        <TextBox Grid.Column="2" VerticalAlignment="Center" 
                 Text="{Binding Second, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
                 Margin="5"  MinWidth="80"/>
        <TextBlock Margin="5" VerticalAlignment="Center" Text="="/>
        <TextBox VerticalAlignment="Center" 
                 Text="{Binding Result, Mode=OneWay}" 
                 Margin="5" IsReadOnly="True" MinWidth="80"/>
    </StackPanel>
</Window>
Рассмотрим теперь полную схему получения данных в MVVM

Если для отправки извещения в View, VM должна реализовывать INPC или INCC, то модель этим не ограничена. Но использование этих интерфейсов в большинстве случаев удобно и позволяет сделать код более прозрачным и читаемым. Можно использовать INPC не совсем стандартно. По своей сути событие PropertyChanged пересылает просто текстовое сообщение (string), ни каких проверок на существование свойства с таким именем не производится. Поэтому не обязательно чтобы параметр метода OnPropertyChanged был названием свойства.
В примере выше, VM обращается к методам Модели сразу возвращающим данные, но такие методы не всегда есть у модели. И если значения свойств First, Second и SelectOperator сразу посылать в Модель, то VM не может знать изменится Result или нет. Для этого при изменении Result Модель должна создать событие извещающее об этом, а VM "прослушивать" это событие и по нему запросить данные.

Пример Модели. В Модели реализованы методы для получения данных от VM: First, Second и SelectOperator. Методы для возвращения данных в VM: GetResult и GetOperators. Для удобства введён дополнительный конструктор сразу подключающий "прослушку" события PropertyChanged.
Имена методов Модели намеренно сделаны не совпадающими с именами свойств VM для большей наглядности взаимодействия. Для этой же цели PropertyChanged сообщает VM об изменении несуществующего свойства ResultChange
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    /// <summary>Класс модели</summary>
    public class CalcINPCModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName]string propertyName = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 
 
        #region закрытые поля
        private int _first;
        private int _second;
        private OperatorsEnum _selectOperator;
        private int? _result = null;
        #endregion
 
        #region Методы получающие данные
        public void SetFirst(int value)
        {
            if (_first != value)
            {
                _first = value;
                OnResult();
            }
        }
        public void SetSecond(int value)
        {
            if (_second != value)
            {
                _second = value;
                OnResult();
            }
        }
 
        public void SetOperator(OperatorsEnum value)
        {
            if (_selectOperator != value)
            {
                _selectOperator = value;
                OnResult();
            }
        }
        #endregion
 
        #region Методы отправляющие данные
        public int? GetResult() => _result;
 
        /// <summary>Возвращает последовательность значений перечисления OperatorsEnum</summary>
        /// <returns></returns>
        public IEnumerable<OperatorsEnum> GetOperators()
            => Enum.GetValues(typeof(OperatorsEnum)).Cast<OperatorsEnum>();
        #endregion
 
        /// <summary>Обработка полученных данных</summary>
        private void OnResult()
        {
            int? value = null;
            if (_selectOperator != OperatorsEnum.Деление || _second != 0)
                switch (_selectOperator)
                {
                    case OperatorsEnum.Сложение: value = _first + _second; break;
                    case OperatorsEnum.Умножение: value = _first * _second; break;
                    case OperatorsEnum.Вычитание: value = _first - _second; break;
                    case OperatorsEnum.Деление: value = _first / _second; break;
                }
            if (_result != value)
            {
                _result = value;
                OnPropertyChanged("ResultChange");
            }
        }
 
        /// <summary>Безпараметрический конструктор</summary>
        public CalcINPCModel() { }
        /// <summary>Конструктор принимающий делегат для "прослушивания" события PropertyChanged</summary>
        /// <param name="propertyChanged">Делегат для прослушки</param>
        public CalcINPCModel(PropertyChangedEventHandler propertyChanged)
            => PropertyChanged += propertyChanged;
    }
В ViewModel свойства First, Second и SelectOperator ни где не хранят свои значения. При получении изменений от View они сразу отправляют их в методы Модели.
Инициализация Модели сделана через обращение к свойству для подключения прослушки чтобы не создавать конструктор ViewModel. В методе Model_PropertyChanged прослушивающем PropertyChanged проверяется имя свойства, если оно ResultChange, то отправляется извещение в View об изменении свойства Result. Пр обращении View к свойству Result оно получает своё значение от модели.
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
    /// <summary>Класс Модели Представления</summary>
    public class CalcINPCViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName]string propertyName = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        #region Секция свойств 
        public int First { set => model.SetFirst(value); }
        public int Second { set => model.SetSecond(value); }
 
        public OperatorsEnum SelectOperator { set => model.SetOperator(value); }
 
        public int? Result => model.GetResult();
 
        public IEnumerable<OperatorsEnum> Operators => model.GetOperators();
        #endregion
 
        CalcINPCModel _model;
        CalcINPCModel model => _model ?? (_model = new CalcINPCModel(Model_PropertyChanged)); // Ссылка на модель
 
        private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ResultChange")
                OnPropertyChanged("Result");
        }
    }
XAML окна не изменился, но на всякий случай привожу его
Кликните здесь для просмотра всего текста
XML
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
<Window x:Class="INPCinModel.CalcINPCWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:INPCinModel"
        mc:Ignorable="d"
        Title="Калькулятор (CalcINPC)" SizeToContent="WidthAndHeight">
    <Window.DataContext>
        <local:CalcINPCViewModel/>
    </Window.DataContext>
    <StackPanel Orientation="Horizontal">
        <TextBox VerticalAlignment="Center" 
                 Text="{Binding First, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
                 Margin="5" MinWidth="80"/>
        <ComboBox VerticalAlignment="Center" 
                  ItemsSource="{Binding Operators}"
                  SelectedItem="{Binding SelectOperator}"
                  Margin="5" MinWidth="80"/>
        <TextBox Grid.Column="2" VerticalAlignment="Center" 
                 Text="{Binding Second, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
                 Margin="5"  MinWidth="80"/>
        <TextBlock Margin="5" VerticalAlignment="Center" Text="="/>
        <TextBox VerticalAlignment="Center" 
                 Text="{Binding Result, Mode=OneWay}" 
                 Margin="5" IsReadOnly="True" MinWidth="80"/>
    </StackPanel>
</Window>


Теперь к такой Модели можно подключить другие View и ViewModel

Модель и первые View+VM почти те же. Добавлена только возможность ссылки на модель через статическое поле.
Добавление в Модели
C#
1
2
        static CalcIWithResultModel _model;
        public static CalcIWithResultModel Model => _model ?? (_model = new CalcIWithResultModel());
Получение Модели в конструкторе VM
C#
1
2
3
4
5
6
        readonly CalcIWithResultModel model;
        public CalcIWithResultViewModel()
        {
            model = CalcIWithResultModel.Model;
            model.PropertyChanged += Model_PropertyChanged;
        }
Целиком коды цепочки "Calc"
Кликните здесь для просмотра всего текста
Модель
Кликните здесь для просмотра всего текста
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    /// <summary>Класс модели</summary>
    public class CalcIWithResultModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName]string propertyName = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 
        static CalcIWithResultModel _model;
        public static CalcIWithResultModel Model => _model ?? (_model = new CalcIWithResultModel());
 
        #region закрытые поля
        private int _first;
        private int _second;
        private OperatorsEnum _selectOperator;
        private int? _result = null;
        #endregion
 
        #region Методы получающие данные
        public void SetFirst(int value)
        {
            if (_first != value)
            {
                _first = value;
                OnResult();
            }
        }
        public void SetSecond(int value)
        {
            if (_second != value)
            {
                _second = value;
                OnResult();
            }
        }
 
        public void SetOperator(OperatorsEnum value)
        {
            if (_selectOperator != value)
            {
                _selectOperator = value;
                OnResult();
            }
        }
        #endregion
 
        #region Методы отправляющие данные
        public int? GetResult() => _result;
 
        /// <summary>Возвращает последовательность значений перечисления OperatorsEnum</summary>
        /// <returns></returns>
        public IEnumerable<OperatorsEnum> GetOperators()
            => Enum.GetValues(typeof(OperatorsEnum)).Cast<OperatorsEnum>();
        #endregion
 
        /// <summary>Обработка полученных данных</summary>
        private void OnResult()
        {
            int? value = null;
            if (_selectOperator != OperatorsEnum.Деление || _second != 0)
                switch (_selectOperator)
                {
                    case OperatorsEnum.Сложение: value = _first + _second; break;
                    case OperatorsEnum.Умножение: value = _first * _second; break;
                    case OperatorsEnum.Вычитание: value = _first - _second; break;
                    case OperatorsEnum.Деление: value = _first / _second; break;
                }
            if (_result != value)
            {
                _result = value;
                OnPropertyChanged("ResultChange");
            }
        }
 
        /// <summary>Безпараметрический конструктор</summary>
        public CalcIWithResultModel() { }
        /// <summary>Конструктор принимающий делегат для "прослушивания" события PropertyChanged</summary>
        /// <param name="propertyChanged">Делегат для прослушки</param>
    }
ViewModel
Кликните здесь для просмотра всего текста
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
    /// <summary>Класс Модели Представления</summary>
    public class CalcIWithResultViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName]string propertyName = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 
        #region Секция свойств 
        public int First { set => model.SetFirst(value); }
        public int Second { set => model.SetSecond(value); }
 
        public OperatorsEnum SelectOperator { set => model.SetOperator(value); }
 
        public int? Result => model.GetResult();
 
        public IEnumerable<OperatorsEnum> Operators => model.GetOperators();
        #endregion
 
 
        private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ResultChange")
                OnPropertyChanged("Result");
        }
 
        readonly CalcIWithResultModel model;
        public CalcIWithResultViewModel()
        {
            model = CalcIWithResultModel.Model;
            model.PropertyChanged += Model_PropertyChanged;
        }
    }
Окно
Кликните здесь для просмотра всего текста
XML
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
<Window x:Class="INPCinModel.CalcIWithResultWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:INPCinModel"
        mc:Ignorable="d"
        Title="Калькулятор (CalcIWithResult)" SizeToContent="WidthAndHeight">
    <Window.DataContext>
        <local:CalcIWithResultViewModel/>
    </Window.DataContext>
    <StackPanel Orientation="Horizontal">
        <TextBox VerticalAlignment="Center" 
                 Text="{Binding First, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
                 Margin="5" MinWidth="80"/>
        <ComboBox VerticalAlignment="Center" 
                  ItemsSource="{Binding Operators}"
                  SelectedItem="{Binding SelectOperator}"
                  Margin="5" MinWidth="80"/>
        <TextBox Grid.Column="2" VerticalAlignment="Center" 
                 Text="{Binding Second, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
                 Margin="5"  MinWidth="80"/>
        <TextBlock Margin="5" VerticalAlignment="Center" Text="="/>
        <TextBox VerticalAlignment="Center" 
                 Text="{Binding Result, Mode=OneWay}" 
                 Margin="5" IsReadOnly="True" MinWidth="80"/>
    </StackPanel>
</Window>

Во второй цепочке "Result" используется упрощённая VM и выводится только результат.
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
    /// <summary>Класс упрощённой ViewModel</summary>
    public class ResultViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName]string propertyName = "")
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 
        public int? Result => model.GetResult();
 
        readonly CalcIWithResultModel model;
        public ResultViewModel()
        {
            model = CalcIWithResultModel.Model;
            model.PropertyChanged += Model_PropertyChanged;
        }
 
 
        private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ResultChange")
                OnPropertyChanged("Result");
        }
 
    }
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Window x:Class="INPCinModel.ResultWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:INPCinModel"
        mc:Ignorable="d"
        Title="ResultWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ResultViewModel/>
    </Window.DataContext>
    <Viewbox>
        <TextBlock Text="{Binding Result}"/>
    </Viewbox>
</Window>
И, конечно, для показа обоих окон надо добавить в App обработчик Startup
C#
1
2
3
4
5
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            (new CalcIWithResultWindow()).Show();
            (new ResultWindow()).Show();
        }
Архив с кодами приложен.
Вложения
Тип файла: zip INPCinModel.zip (100.7 Кб, 256 просмотров)
10
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
27.01.2019, 17:51
Ответы с готовыми решениями:

Библиотека элементов для реализации WPF MVVM Решений [WPF, Элд Хасп]
Решил собрать элементы используемые в темах в этом разделе. В библиотеку включаю элементы которые, на мой взгляд, имеют универсальное...

WPF команды и MVVM. Часть 1. [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Для использования и создания WPF команд в Net предусмотрен...

Передача данных между Окнами, между VM, Шина Сообщений, Локатор [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Использование статического класса. Передача...

63
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16164 / 11284 / 2891
Регистрация: 21.04.2018
Сообщений: 33,169
Записей в блоге: 2
03.08.2023, 15:13  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
пересмотреть стратегию в более правильную сторону
Это же не всегда от разработчика зависит.
Исходные данные: небольшая многопользовательская БД, клиенты разбросаны по всей стране, у многих медленные каналы, много сложных запросов к БД.
В таких условиях локальный кеш (актуальный) это хорошее решение.
0
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
03.08.2023, 15:25
Цитата Сообщение от Элд Хасп Посмотреть сообщение
много сложных запросов к БД
EF и сложные запросы не совместимы, потому расчёты и ведутся на C#...
Я же предлагаю написать именно запросы SQL (хранимки с параметрами, UDF с параметрами, View для отчёта), которые находятся на сервере и позволят вести любые расчёты сервером СУБД (уж математику то он осилит, благо все функции есть) и выдавать только простенький набор данных, который надо будет просто показать пользователю и всё. Доступ к серверу у человека есть, он сам об этом писал.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16164 / 11284 / 2891
Регистрация: 21.04.2018
Сообщений: 33,169
Записей в блоге: 2
03.08.2023, 16:07  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Я же предлагаю написать именно запросы SQL
Я же пишу не за SQL, а за локальный кеш. При некоторых обстоятельствах без него никак не обойтись.
Делать к нему запросу через LINQ или SQL - это уже вторично.
0
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
03.08.2023, 16:19
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Я же пишу не за SQL, а за локальный кеш.
А я пишу про то, что автор грузит кучу данных на клиента для проведения расчетов, он сам про это писал и начал жаловаться про медленный спутниковый интернет. Вот я и начал его выводить на путь истинный, а потом оказалось что у него EF и тут стало всё понятно
А на предложение что можно все расчёты отдать на сервер СУБД он ответил что у него не расчётный сервер, а какая-то простенькая машинка. Тут стало ещё понятнее, что автор не до конца понимает что такое сервер СУБД. Вот я и пытаюсь объяснить что это не просто таблички с данными, это еще и мощная программа, которая умеет не только данные хранить... И эта программа должна работать и облегчать работу клиента до минимальных манипуляций с данными, для этого у неё есть все инструменты...
0
 Аватар для werymag
27 / 12 / 1
Регистрация: 20.05.2015
Сообщений: 217
03.08.2023, 17:42
Весь день отвлекают, ответить не дают)
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Стоит ли у вас задача абстракции Модели, Представления?
Даже в какой-то отдалённой теории возможно ли изменение типа View? Модели?
Нет, тут скорее наоборот - вся разработка идет от View ("Сделай мне чтоб при нажатии этой кнопки делалось вот это"), максимум где будут подключатся другие инструменты - это к Веб Серверу по тем же АПИ.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
В вашей задаче важным аспектом является локальное кеширование данных.
Как бы медленно не работал инет или сеть, запрос с фильтрацией и сортировкой к серверу это всё равно быстрее чем получение полного набора, а потом его локальная обработка.
Совсем другое дело, если имеется локальный кеш и можно проводить обработку без запроса к БД.
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Вот в этом и ошибка. Сколько по времени займёт получить 1000000 записей из БД и сколько времени уйдёт на получение 10? Даже если вам придётся на сервере СУБД запустить запрос с расчетом чего-то, то это всё равно будет быстрее, чем получить из сети 1000k записей и потом с ними что-то крутить на клиенте. Сервер СУБД работает очень быстро, он рассчитан на выполнение многих сложных запросов параллельно и отрабатывает их довольно таки быстро.
У меня есть хранимка, которая вЧ общей сложности обрабатывает около 1500k записей из 8 таблиц и выдаёт всего 12 записей. Дак работает она около 15мс. Это долго? И это на MS SQL Express с двумя ядрами и 4-мя гигами оперативки...
1. "Полный" набор данных это, максимум 1000-2000 строк на все таблицы, с учетом сжатия веб сервера это 50-100кб данных максимум (уже протестировано), что почти мгновенно в современных реалиях. При том это уже не "полный набор" данных о котором вы говорит,это уже выборка из огромных таблиц относящаяся к конкретному объекту и пользователю.
2. Пользователь должен видеть всё доступное оборудование, у него должна быть возможность в любой момент кликнуть\посмотреть\добавить любое оборудование из всех таблиц.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Но вот осмысленность его реализации..... Очень часто в WPF применяется совмещённая реализация VM+Model. Если работа над приложением идёт "в одного", если нет в планах никакой модернизации в будущем, то такое решение вполне оправданно по затратам "здесь и сейчас".
В целом да.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Неее.
Здесь принципиальная разница. Слушатель знает об источнике события, а источник о слушателе - нет.
Поэтому вызов членов VM из обработчика события в View - это нормально.
А прослушка в VM событий View - это грубое нарушение паттерна.
"Фрукт-фрукт, сиська-сиська, цветок-цветок ..."
Ну ок. Как я понимаю работает одинаково, но идея и способ взаимодействия разные.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
По сути вы описали Local из DbContext. Эти ObservableCollection являются частью Модели. Зачем вы "руками" делаете то, что уже есть в DbContext... не понятно.
Даже если вы не используете EF, то это всё равно задача слоя работающего с БД (посылающего запросы к БД).
С БД работает Веб-сервер, который уже через некоторое АПИ передает через сеть ДТО объекты клиенту.
Ну и ObservableCollection используются что бы View обновлять проще было.

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
SQL это тот же язык программирования. У него есть все базовые математические функции и рассчитать он может много чего.
Вбитвать такую логику в SQL звучит как отдельный уровень боли. Страдания ради страдания.
Ну и в целом - зачем? Как уже написал - работаю я ~1000 полей во всех таблицах, получаю я их с сервера за время меньшее времени пинга, C# код преобразует их как угодно за наносекунды.
Это не значит, что я не делаю выборок на сервере - ещё как делаю (из таких же таблиц на миллионы элементов), просто финальная выборка - это то, с чем будет работать пользователь, при том есть вероятность что он будет работать прям вообще со всем полученным.

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Дак у вас EF? Ну тогда могу посочувствовать...
Вообще не испытываю проблем с ним, кроме что работы со связанными полями.
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Вот в этом и ошибка. Сколько по времени займёт получить 1000000 записей из БД и сколько времени уйдёт на получение 10? Даже если вам придётся на сервере СУБД запустить запрос с расчетом чего-то, то это всё равно будет быстрее, чем получить из сети 1000k записей и потом с ними что-то крутить на клиенте. Сервер СУБД работает очень быстро, он рассчитан на выполнение многих сложных запросов параллельно и отрабатывает их довольно таки быстро.
У меня есть хранимка, которая в общей сложности обрабатывает около 1500k записей из 8 таблиц и выдаёт всего 12 записей. Дак работает она около 15мс. Это долго? И это на MS SQL Express с двумя ядрами и 4-мя гигами оперативки...
Тут не спорю, просто я вроде сразу писал что записей относительно не много, я не подгружаю всю таблицу, я подгружаю всё - что нужно для работы (хотя это тоже не так уж и мало)

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Либо ещё вариант. Сервер СУБД и WEB находятся на одной машине (быстрой сети). WEB по запросу клиента тянет на себя все данные которые нужны для расчетов, считает их и выдаёт объект с расчетами клиенту. Загрузка сети минимальна. Но опять же... Скорость работы запросов СУБД гораздо быстрее чем то, что вы наваяете в WEB для расчетов. Задумайтесь... Сервера СУБД не зря придумали, это очень мощная и быстрая система управления данными, а не только инструмент хранения данных..
Так оно и сейчас так работает, я просто не пойму о каких "расчетах" вы говорите. Веб серер на одной машине с БД, он из БД запросом получает нужные для работы данные и передает клиенту. Судя по всему для вас все расчеты связанны только с обработкой коллекций БД, у меня же расчеты - это именно математика (умножение, деление и прочее).

Грубо говоря - я получил 10-30 таблиц по 100-200 полей, "что-то" со всеми ними сделал - отправил назад. Я не понимаю зачем мне дергать сервер пока я "что-то" с ними делаю, это приведет к крайне долгому ожиданию ответа при каждом действии.

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
LINQ (EF) очень ограничен в составлении нормальных сложных запросов. Если нужно реально что-то быстрое, сложное и индивидуальное - то только реализация на диалекте SQL вашей СУБД... А EF оставьте для приложений типа Hello World!, на большее он не способен...
Тут хз, у меня есть знакомый прогер работающий в большой команде над большими проектами - они везде используют EF и очень хвалят его, мол - при должном скиле на нем можно сделать что угодно (особенно использую свои запросы и методы расширения)
Мне в целом нравится, какие-то особо уникальные запросы я делать не планирую (и уж тем более писать математику на сервере), а в плане простых вещей вида Select From Where Left|Right Join думаю оно всё одинаково должно работать по скорости, если понимать как оно работает и что вытягивает. Ну или использовать всякие там разделенные запросы и прочие улучшатели запросов.
0
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
03.08.2023, 17:48
Цитата Сообщение от werymag Посмотреть сообщение
у меня же расчеты - это именно математика (умножение, деление и прочее).
Вот про это и говорю. Зачем грузить клиента этим, когда можно заставить работать сервер. Он для этого и предназначен... Закинули ему параметры расчёта и пусть работает...
0
 Аватар для werymag
27 / 12 / 1
Регистрация: 20.05.2015
Сообщений: 217
03.08.2023, 17:52
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
И эта программа должна работать и облегчать работу клиента до минимальных манипуляций с данными, для этого у неё есть все инструменты...
И тут ещё нюанс, логика этой математики - это то, что сейчас и, вероятно, в будущем будет меняться чаще всего, сейчас каждый день, в будущем - в каждой минорной версии. Мне совсем не хочется постоянно лезть и менять что-то на сервере. Только к нему в цепочке слоев есть требования по надежности.

Добавлено через 4 минуты
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Вот про это и говорю. Зачем грузить клиента этим, когда можно заставить работать сервер. Он для этого и предназначен... Закинули ему параметры расчёта и пусть работает...
А клиент ждет Пусть уж подождет пинг + 30-100мс при открытии окна и подгрузке данных, а дальше мгновенно со всем работает.
0
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
03.08.2023, 17:52
werymag, Ну смотрите сами... Я вам советую как лучше, но вы сам себе хозяин...
0
 Аватар для werymag
27 / 12 / 1
Регистрация: 20.05.2015
Сообщений: 217
03.08.2023, 17:56
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
werymag, Ну смотрите сами... Я вам советую как лучше, но вы сам себе хозяин..
Просто я, если честно, вот вообще не представляю как это сделать на сервере, я же расписал 1% логики в окнах приложения (ибо там наворочено ну очень много и постоянно добавляется новое). Сейчас я просто делаю себе новый класс/функцию и быстренько в ней все данные обрабатываю, а как это всё впихнуть на сервер, вот вообще мыслей нет, это целая работа сопоставимая с остальной разработкой, а потом ещё обновлять и поддерживать...
0
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
03.08.2023, 17:58
Цитата Сообщение от werymag Посмотреть сообщение
в будущем будет меняться чаще всего
Цитата Сообщение от werymag Посмотреть сообщение
Мне совсем не хочется постоянно лезть и менять что-то на сервере
Зато, если выходные данные не меняются, при запуске расчетов на сервере СУБД, нужно будет просто изменить логику в хранимке. А прогу пересобирать и обновлять у клиентов уже не нужно, так как название хранимки осталось и все параметры и набор данных тоже...

Добавлено через 1 минуту
Цитата Сообщение от werymag Посмотреть сообщение
а как это всё впихнуть на сервер, вот вообще мыслей нет
Изучить SQL и всё станет понятно
0
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
04.08.2023, 09:16
Цитата Сообщение от werymag Посмотреть сообщение
это целая работа сопоставимая с остальной разработкой, а потом ещё обновлять и поддерживать...
А как вы хотели? Если работа приложения идёт с СУБД, то разработчик обязан знать язык СУБД, уметь с ним работать. EF не панацея, и если не понимать SQL, то как вы составите правильный запрос на EF? Ведь нужно анализировать то, что EF генерирует и передаёт на сервер...

И по поводу расчётов. Вот смотрите
T-SQL
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
ALTER PROC [dbo].[spOMTOCheckStatus](@posQty int OUTPUT, @posQtyZ int OUTPUT)
AS
BEGIN
    DECLARE @idZPos         int -- ID позиции спецификации заявки
    DECLARE @idZ            int -- ID заявки
    DECLARE @sumStor        float
    DECLARE @posEI1Z        float
    DECLARE @allPos         int
    DECLARE @allPosZ        int
    DECLARE @currentPos     int
    DECLARE @currentPosZ    int
    DECLARE @counter        int
    DECLARE @counterZ       int
    DECLARE @countSpecAll   int -- Количество позиций спецификации
    DECLARE @countSpecSet   int -- Количество позиций спецификации со статусом Поставлено
    DECLARE @countSpecProg  int -- Количество позиций спецификации со статусом Идет поставка
    DECLARE @countSpecNot   int -- Количество позиций спецификации со статусом Поставки нет
    -- Табличные переменные для прохода в цикле WHILE
    -- @idTable - табличная переменная, которая хранит ID позиций спецификации заявок
    DECLARE @idTable        TABLE (ID int NOT NULL PRIMARY KEY IDENTITY(1, 1), PosZDet int)
    -- @idTableZ - табличная переменная, которая хранит ID заявок
    DECLARE @idTableZ       TABLE (ID int NOT NULL PRIMARY KEY IDENTITY(1, 1), ZIDNum int)
 
    -- Отсекаются позиции спецификации со статусом Поставлено
    SELECT @counter = COUNT(*) FROM [dbo].[tblOMTOZDetails] WHERE [Status] = N'Поставлено'
 
    IF @counter > 0
        INSERT INTO @idTable ([PosZDet]) SELECT [ID_ZayavkaPod] FROM [dbo].[tblOMTOZDetails] WHERE [Status] <> N'Поставлено'
    ELSE
        INSERT INTO @idTable ([PosZDet]) SELECT [ID_ZayavkaPod] FROM [dbo].[tblOMTOZDetails]
    SET @allPos = @@ROWCOUNT
 
    -- Организация прохода по позициям спецификации с обновлением статуса в зависимости от условия
    SET @currentPos = 0
    WHILE @currentPos < @allPos
        BEGIN
            SET @currentPos = @currentPos + 1
            SELECT @idZPos = [PosZdet] FROM @idTable WHERE [ID] = @currentPos
            SELECT @sumStor = ISNULL(SUM([KolEI1]), 0) FROM [dbo].[tblOMTOStorageIn] WHERE [ID_ZayavkaPod] = @idZPos 
            SELECT @posEI1Z = [KolvoEI1] FROM [dbo].[tblOMTOZDetails] WHERE [ID_ZayavkaPod] = @idZPos
            UPDATE [dbo].[tblOMTOZDetails] SET [Status] = 
                    CASE
                        WHEN @sumStor >= @posEI1Z THEN N'Поставлено'
                        WHEN @sumStor > 0 AND @sumStor < @posEI1Z THEN N'Идет поставка'
                        ELSE N'Поставки нет'
                    END
                WHERE [ID_ZayavkaPod] = @idZPos 
        END
 
    -- Отсекаются заявки со статусом Поставлено
    SELECT @counterZ = COUNT(*) FROM [dbo].[tblOMTOZayavka] WHERE [Status] = N'Поставлено'
 
    IF @counterZ > 0
        INSERT INTO @idTableZ ([ZIDNum]) SELECT [ID_Zayavka] FROM [dbo].[tblOMTOZayavka] WHERE [Status] <> N'Поставлено'
    ELSE
        INSERT INTO @idTableZ ([ZIDNum]) SELECT [ID_Zayavka] FROM [dbo].[tblOMTOZayavka]
    SET @allPosZ = @@ROWCOUNT
 
    -- Организация прохода по номерам заявок с обновлением статуса в зависимости от статуса позиций спецификации заявки
    SET @currentPosZ = 0
    WHILE @currentPosZ < @allPosZ
        BEGIN
            SET @currentPosZ = @currentPosZ + 1
            SELECT @idZ = [ZIDNum] FROM @idTableZ WHERE [ID] = @currentPosZ
            SELECT @countSpecAll = COUNT(*) FROM [dbo].[tblOMTOZDetails] WHERE [ID_Zayavka] = @idZ
            SELECT @countSpecSet = COUNT(*) FROM [dbo].[tblOMTOZDetails] WHERE [ID_Zayavka] = @idZ AND [Status] = N'Поставлено'
            SELECT @countSpecProg = COUNT(*) FROM [dbo].[tblOMTOZDetails] WHERE [ID_Zayavka] = @idZ AND [Status] = N'Идет поставка'
            SELECT @countSpecNot = COUNT(*) FROM [dbo].[tblOMTOZDetails] WHERE [ID_Zayavka] = @idZ AND [Status] = N'Поставки нет'
            UPDATE [dbo].[tblOMTOZayavka] SET [Status] = 
                CASE
                    WHEN @countSpecAll = @countSpecSet THEN N'Поставлено'
                    WHEN (@countSpecProg > 0 OR @countSpecSet > 0) AND @countSpecSet < @countSpecAll THEN N'Идет поставка'
                    WHEN @countSpecNot = @countSpecAll THEN N'Поставки нет'
                END
            WHERE [ID_Zayavka] = @idZ
        END
    SET @posQty = @allPos -- Количество обработанных записей позиций спецификаций
    SET @posQtyZ = @allPosZ -- Количество обработанных заявок
END
GO
Эта хранимка проходит по всем позициям спецификаций заявок и на основании количества поступления на склад присваивает статус позиции и статус заявки. Здесь идёт анализ трёх таблиц и всё это исполняется на сервере. А вот теперь ваш вариант с EF. Для того чтобы такое сделать нужно загрузить все данные на клиент и потом уже на C# их ковырять, а данных там под 1000k записей. А так, на сервере, это всё дело отрабатывает за 8-12мс... Разницу понимаете?

Или допустим вот такой отчёт нужно получить, в котором берутся данные удовлетворяющие разным условиям и потом, после их обработки, ещё связываются с разными таблицами.
T-SQL
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
ALTER VIEW [dbo].[vwM29SummaryProduceInOutReport]
AS
WITH 
    cteProg (ID_Draw, KKS, Designation, PosName, Material, QProgress)
    AS
    (
        SELECT
            d.ID_Draw, ds.KKS, ds.Oboznachenie, ds.Naimenovanie, ds.Material, SUM(pd.PDFact) AS QProgress
        FROM
            dbo.tblDraw d
            INNER JOIN
            dbo.tblDrawSys s ON s.ID_Draw = d.ID_Draw
            INNER JOIN
            dbo.tblDrawSpec ds ON ds.ID_DrawSys = s.ID_DrawSys
            INNER JOIN
            dbo.tblProgressData pd ON pd.ID_DrawSpec = ds.ID_DrawSpec
        GROUP BY
            d.ID_Draw, ds.KKS, ds.Oboznachenie, ds.Naimenovanie, ds.Material
    ),
 
    cteIn (ID_DrawSummarySpec, QuantityIn)
    AS
    (
        SELECT
            i.ID_DrawSummarySpec, SUM(i.Quantity) AS QuantityIn
        FROM
            dbo.tblM29StorageIn i
        GROUP BY
            i.ID_DrawSummarySpec
    ),
 
    cteOut (ID_DrawSummarySpec, QuantityOut)
    AS
    (
        SELECT
            o.ID_DrawSummarySpec, SUM(o.Quantity) AS QuantityOut
        FROM
            dbo.tblM29StorageOut o
        GROUP BY
            o.ID_DrawSummarySpec
    )
 
SELECT
    eo.EObjectName AS [Объект], d.Works AS [Тип], d.DrawNum AS [Чертёж], s.KKS, s.Designation AS [Обозначение], s.PosName AS [Наименование], 
    s.Material AS [Материал], s.Measure AS [Ед.изм.], s.Quantity AS [Кол-во], s.UnitWeight AS [Ед.вес], s.TotalWeight As [Вес],
    ISNULL(p.QProgress, 0) AS [Монтаж], ISNULL(p.QProgress, 0) * s.UnitWeight AS [ВесМ],
    ISNULL(i.QuantityIn, 0) AS [Выписано], ISNULL(i.QuantityIn, 0) * s.UnitWeight AS [ВесВ],
    ISNULL(o.QuantityOut, 0) AS [Списано], ISNULL(o.QuantityOut, 0) * s.UnitWeight AS [ВесС]
FROM
    dbo.tblEObject eo
    INNER JOIN
    dbo.tblGenPlan g ON g.ID_EObject = eo.ID_EObject
    INNER JOIN
    dbo.tblDraw d ON d.ID_GenPlan = g.ID_GenPlan
    INNER JOIN
    dbo.tblM29DrawSummarySpec s ON s.ID_Draw = d.ID_Draw
    LEFT JOIN
    cteProg p ON p.ID_Draw = d.ID_Draw AND p.KKS = s.KKS AND p.Designation = s.Designation AND p.PosName = s.PosName AND p.Material = s.Material
    LEFT JOIN
    cteIn i ON i.ID_DrawSummarySpec = s.ID_DrawSummarySpec
    LEFT JOIN
    cteOut o ON o.ID_DrawSummarySpec = s.ID_DrawSummarySpec
GO
Простым GROUP BY, WHERE и JOIN тут такое не решить. А так как EF не умеет делать такие запросы, то придётся вытаскивать все данные на клиента и рулить ими на C#. Потому вам и говорю, что SQL знать нужно обязательно, так как гораздо проще переложить работу на сервер СУБД, чем ковырять данные на C# и потом их обратно загонять в БД...

И по поводу математики - Математические функции (Transact-SQL). Хватит на все случаи жизни...

Добавлено через 19 минут
werymag, И плюс такого подхода в том, что при неизменных параметрах и результирующих наборах данных при изменении логики расчётов не нужно пересобирать приложение. Изменил на сервере хранимку, UDF, View и опять всё работает. Пользователи даже не замечают этого.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16164 / 11284 / 2891
Регистрация: 21.04.2018
Сообщений: 33,169
Записей в блоге: 2
04.08.2023, 10:22  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
А вот теперь ваш вариант с EF. Для того чтобы такое сделать нужно загрузить все данные на клиент и потом уже на C# их ковырять, а данных там под 1000k записей.
Вы "не слышите" werymag.
Он же уже раз десять написал, что у него совершенно другие условия задачи:
Цитата Сообщение от werymag Посмотреть сообщение
1. "Полный" набор данных это, максимум 1000-2000 строк на все таблицы,
Обрабатываются на Шарпе только эти 1-2к строк.
Результатом обработки является SQL запрос на выборку из большого набора данных.

Вся БД под 1000k записей не грузится память. С ней работают через настроенные SQL запросы.
1
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
04.08.2023, 10:33
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Вы "не слышите" werymag.
Он же уже раз десять написал, что у него совершенно другие условия задачи:
Я это уже давно понял.
Я ему говорю про плюсы использования и знания SQL, отвечаю на его сомнения по поводу реализации, привожу примеры, которые при его подходе нормально не сделать... А решать ему
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16164 / 11284 / 2891
Регистрация: 21.04.2018
Сообщений: 33,169
Записей в блоге: 2
04.08.2023, 10:49  [ТС]
werymag, если правильно понял все условия вашей задачи, то у вас модель - это DbContext.
Вся остальная логика находится в VM. Задачи по абстракции View и БД у вас даже в далёкой теоретической перспективе нет. По большому счёту даже нет смысла в разделении View, Model, VM по разным проектам.
По сути у вас получается Решение только из Представления. Да, это по факту будет легаси код, но конкретно в вашей задаче, в заданных вам условиях для её выполнения это будет оптимальным.

Единственно, что я бы изменил - это не "ручной" парсинг таблиц в ObservableCollection, а использование Local из DbContext.
LocalView<TEntity>.ToObservableCollection Method

Добавлено через 2 минуты
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
привожу примеры, которые при его подходе нормально не сделать...
Он же ответил вам, что согласен с вами, но для других задач.
А для его задачи ваши примеры не подходят.
1
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
04.08.2023, 11:20
Цитата Сообщение от Элд Хасп Посмотреть сообщение
А для его задачи ваши примеры не подходят.
Вы уверены? Вы видели реализацию? Вы видели структуру БД? Вы видели запросы и т.д.?
Потому я решил показать что можно сделать, а использовать это или нет решаем не мы...
0
 Аватар для werymag
27 / 12 / 1
Регистрация: 20.05.2015
Сообщений: 217
04.08.2023, 16:26
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Неее.
Здесь принципиальная разница. Слушатель знает об источнике события, а источник о слушателе - нет.
Поэтому вызов членов VM из обработчика события в View - это нормально.
А прослушка в VM событий View - это грубое нарушение паттерна.
Кстати, читая про AvaloniaUI (после новости о отправке WPF на пенсию), там реализована крайне удобная штука с привязкой команды напрямую к методу:

XML
1
<Button Command="{Binding ButtonClicked}"/>
C#
1
2
3
4
5
6
7
public class ViewModel
{
    public void ButtonClicked()
    {
        Console.WriteLine("Hooray!");
    }
}
Вопрос, а противоречит ли это описанной вами логике. Если рассуждать исключительно о "знает - не знает", то в целом вроде не противоречит.

И кстати, в штатном WPF можно как-то сделать так же? Просто уж очень раздражает совершенно лишний одинаковый код с ICommand.
0
 Аватар для Andrey-MSK
3386 / 2272 / 388
Регистрация: 14.08.2018
Сообщений: 7,690
Записей в блоге: 4
04.08.2023, 16:29
Цитата Сообщение от werymag Посмотреть сообщение
C#
1
2
3
4
5
6
7
public class ViewModel
{
 public void ButtonClicked()
 {
 Console.WriteLine("Hooray!");
 }
}
А это точно ViewModel? Мало того что Button, дак еще и Console...
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16164 / 11284 / 2891
Регистрация: 21.04.2018
Сообщений: 33,169
Записей в блоге: 2
04.08.2023, 16:31  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Вы уверены?
Нет.
Я отталкиваюсь от его объяснений.
И такое вполне может быть.
Я делал торгового бота, в котором большой объём данных скачивался при запуске.
Потом по ним формировалось множество простых запросов к другим данным на сервере. Смысла формировать такое же множество сложных запросов не было никакого.
Это ещё при том что был нормальные коммерческий сервер и сети.

А ТС пишет что он лишён и того, и другого.

Добавлено через 2 минуты
Цитата Сообщение от werymag Посмотреть сообщение
после новости о отправке WPF на пенсию
Формы ещё старше, но и их ещё будут использовать многие годы, если не десятилетия.
Такая "отправка на пенсию" - это маркетинговый ход.
1
 Аватар для werymag
27 / 12 / 1
Регистрация: 20.05.2015
Сообщений: 217
04.08.2023, 16:36
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
А это точно ViewModel? Мало того что Button, дак еще и Console...
Ну я ссылку на статью ХАБР привел, вот она же https://habr.com/ru/articles/349394/
Статья, как я понял, от одного из разработчиков.
Этож не WPF, это Avalonia.

Добавлено через 1 минуту
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Такая "отправка на пенсию" - это маркетинговый ход.
Ну тут скорее то, что теперь уж точно ждать развития особо не стоит, а хотелось бы.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16164 / 11284 / 2891
Регистрация: 21.04.2018
Сообщений: 33,169
Записей в блоге: 2
04.08.2023, 16:39  [ТС]
Цитата Сообщение от werymag Посмотреть сообщение
там реализована крайне удобная штука с привязкой команды напрямую к методу:
--------------------------
-------------------------
противоречит ли это описанной вами логике. Если рассуждать исключительно о "знает - не знает", то в целом вроде не противоречит.
Не противоречит. Это деталь реализации View. MVVM не регламентирует внутренние детали реализации слоёв.

Цитата Сообщение от werymag Посмотреть сообщение
И кстати, в штатном WPF можно как-то сделать так же? Просто уж очень раздражает совершенно лишний одинаковый код с ICommand.
Думаю - можно. Но нужно делать свой кастомный Binding.
"Стандартный" может привязываться только к свойствам.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
04.08.2023, 16:39

WPF конвертеры [Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html View получает данные от ViewModel, но часто бывают случаи...

Элемент геометрии «Стрелка» [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Реализация сделана по вопросу отDissolve в теме...

WPF vs WinForms (для начинающих) [Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html Эту тему решил создать, так как очень часто сталкиваюсь с...

WPF команды и MVVM. Часть 2. Всплытие команд. Реализация команды для списка элементов [WPF, Элд Хасп]
Тема из цикла https://www.cyberforum.ru/wpf-silverlight/thread2384523.html На практике часто встречаются случаи когда команда и кнопка...

Обсуждение темы "Библиотека элементов для реализации WPF MVVM Решений" [WPF, Элд Хасп]
Любое обсуждение, рекомендации, вопросы и т.п. по теме https://www.cyberforum.ru/wpf-silverlight/thread2738784.html В том числе по...


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

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Новые блоги и статьи
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
Как ИИ начал спорить и врать (возможно почуяв опасность для себя от индустрии - уход от электроники).
Hrethgir 04.08.2026
Недельный диалог, на фоне событий с НПЗ. Да, из спирта можно получать бензин, и это не сложно. Но потом в схеме я решил избавиться от насоса, при этом полностью сделав контроль подачи спирта в. . .
Термопринтер QR701
Argus19 03.08.2026
Термопринтер QR701 Купил два термопринтера QR701. На сэлф-тесте написано: Language: PC936 (GB18030). Что означает, что принтеры могут печатать только латиницу и китайские иероглифы. Так же. . .
Создание формы заимствованного документа
Maks 03.08.2026
Задача: Необходимо создать собственную форму заимствованного документа. На форме должен быть реквизит "Покупатель", а также табличная часть со следующими реквизитами: - Расчетный счет покупателя. . .
Задача предоставления скидок покупателям
Maks 03.08.2026
Задача: В документе "Продажи" необходимо реализовать функционал предоставления скидок покупателям. Скидка должна автоматически рассчитываться и подставляться в соответствующее поле при выборе. . .
Почему SEO не начинается с ключевых слов: что проверить до написания текстов
Neotwalker 01.08.2026
Когда владельцу сайта предлагают заняться SEO, первым шагом часто становится сбор запросов и написание текстов. Логика кажется понятной: 1. Находим ключевые слова. 2. Добавляем их на. . .
Знание — сила: Доктрина интенциональности знаний, углубление в формулу
Hrethgir 01.08.2026
https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11957&stc=1&d=1785567302 Знаменитый афоризм Фрэнсиса Бэкона «Знание — сила» (Scientia potentia est) в массовой культуре принято понимать. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru