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

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

27.01.2019, 17:51. Показов 25240. Ответов 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 Кб, 260 просмотров)
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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 2
03.08.2023, 15:13  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
пересмотреть стратегию в более правильную сторону
Это же не всегда от разработчика зависит.
Исходные данные: небольшая многопользовательская БД, клиенты разбросаны по всей стране, у многих медленные каналы, много сложных запросов к БД.
В таких условиях локальный кеш (актуальный) это хорошее решение.
0
 Аватар для Andrey-MSK
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 4
03.08.2023, 15:25
Цитата Сообщение от Элд Хасп Посмотреть сообщение
много сложных запросов к БД
EF и сложные запросы не совместимы, потому расчёты и ведутся на C#...
Я же предлагаю написать именно запросы SQL (хранимки с параметрами, UDF с параметрами, View для отчёта), которые находятся на сервере и позволят вести любые расчёты сервером СУБД (уж математику то он осилит, благо все функции есть) и выдавать только простенький набор данных, который надо будет просто показать пользователю и всё. Доступ к серверу у человека есть, он сам об этом писал.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 2
03.08.2023, 16:07  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Я же предлагаю написать именно запросы SQL
Я же пишу не за SQL, а за локальный кеш. При некоторых обстоятельствах без него никак не обойтись.
Делать к нему запросу через LINQ или SQL - это уже вторично.
0
 Аватар для Andrey-MSK
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 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
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 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
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 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
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 4
03.08.2023, 17:58
Цитата Сообщение от werymag Посмотреть сообщение
в будущем будет меняться чаще всего
Цитата Сообщение от werymag Посмотреть сообщение
Мне совсем не хочется постоянно лезть и менять что-то на сервере
Зато, если выходные данные не меняются, при запуске расчетов на сервере СУБД, нужно будет просто изменить логику в хранимке. А прогу пересобирать и обновлять у клиентов уже не нужно, так как название хранимки осталось и все параметры и набор данных тоже...

Добавлено через 1 минуту
Цитата Сообщение от werymag Посмотреть сообщение
а как это всё впихнуть на сервер, вот вообще мыслей нет
Изучить SQL и всё станет понятно
0
 Аватар для Andrey-MSK
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 2
04.08.2023, 10:22  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
А вот теперь ваш вариант с EF. Для того чтобы такое сделать нужно загрузить все данные на клиент и потом уже на C# их ковырять, а данных там под 1000k записей.
Вы "не слышите" werymag.
Он же уже раз десять написал, что у него совершенно другие условия задачи:
Цитата Сообщение от werymag Посмотреть сообщение
1. "Полный" набор данных это, максимум 1000-2000 строк на все таблицы,
Обрабатываются на Шарпе только эти 1-2к строк.
Результатом обработки является SQL запрос на выборку из большого набора данных.

Вся БД под 1000k записей не грузится память. С ней работают через настроенные SQL запросы.
1
 Аватар для Andrey-MSK
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 4
04.08.2023, 10:33
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Вы "не слышите" werymag.
Он же уже раз десять написал, что у него совершенно другие условия задачи:
Я это уже давно понял.
Я ему говорю про плюсы использования и знания SQL, отвечаю на его сомнения по поводу реализации, привожу примеры, которые при его подходе нормально не сделать... А решать ему
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 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
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 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
3397 / 2283 / 388
Регистрация: 14.08.2018
Сообщений: 7,755
Записей в блоге: 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,175
Записей в блоге: 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
Ответ Создать тему
Новые блоги и статьи
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru