У меня есть приложение mvvm wpf, которое работало должным образом до одного момента, когда оно зависало и приводило к массовой утечке памяти. Файлы решения: Ссылка на решение Google Disk
Приложение использует локальный файл mdf, использует Mahhaps и некоторые дополнительные ссылки (например, для отображения гифок).
Это вызов метода в модели представления, который вызывает проблему, назначение контактов с помощью await создает проблему - это используется в одной другой модели представления, где не возникает никаких проблем, даже здесь он работал нормально до тех пор, пока один момент.
public async void OnLoad()
{
IsRefreshEnabled = false;
IsRefreshProgressActive = true;
Contacts =await Task.Run(() => _repository.GetContactsAsync()) ;
IsRefreshEnabled = true;
IsRefreshProgressActive = false;
}
Так визуализируется View
<DataGrid SelectedItem = "{Binding Contact}" AutoGenerateColumns = "True" ItemsSource = "{Binding Path=Contacts, Mode=TwoWay}" Style = "{StaticResource AzureDataGrid}" x:Name = "dataGridCodeBehind" Margin = "10,54,521,0" VerticalAlignment = "Top" HorizontalAlignment = "Stretch" HorizontalContentAlignment = "Stretch" ColumnWidth = "*">
<DataGrid.ColumnHeaderStyle>
<Style TargetType = "DataGridColumnHeader">
<Setter Property = "FontSize" Value = "10"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
Я попытался удалить библиотеки для гифок (гифки не отображаются в этом представлении в другом представлении, что не вызывает никаких проблем).
Тот же вызов репозитория - для получения данных контактов, которые у меня есть в одном другом представлении, и это не создает никаких проблем.
Этот вид контактов работал хорошо до внезапно.
Пробовал отлаживать, но отладчик даже не доходит до кода.
После загрузки некоторых данных в базу данных я зависаю.
Замораживание выполняется методом OnLoad из ContactsViewModel - этот вызов репозитория используется еще в одной модели ViewModel, где у него нет никаких проблем - он быстро возвращает данные.
КонтактыViewModel код:
using Digital_Data_House_Bulk_Mailer.Commands;
using Digital_Data_House_Bulk_Mailer.Model;
using Digital_Data_House_Bulk_Mailer.Repository;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Digital_Data_House_Bulk_Mailer.ViewModel
{
class ContactsViewModel : INotifyPropertyChanged
{
private Contact _contact;
private IContactRepository _repository = new ContactRepository();
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static void NotifyStaticPropertyChanged(string propertyName)
{
if (StaticPropertyChanged != null)
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
public RelayCommand UpdateCommand { get; set; }
public RelayCommand LoadCommand { get; set; }
public RelayCommand DeleteCommand { get; set; }
public RelayCommand DeleteAllContactsCommand { get; set; }
public RelayCommand RecreateFiltersCommand { get; set; }
private ObservableCollection<Contact> _contacts;
public ObservableCollection<Contact> Contacts
{
get { return _contacts; }
set
{
_contacts = value;
PropertyChanged(this, new PropertyChangedEventArgs("Contacts"));
//used in case of static Contacts property
//NotifyStaticPropertyChanged("Contacts");
}
}
public Contact Contact
{
get { return _contact; }
set
{
_contact = value;
DeleteCommand.RaiseCanExecuteChanged();
UpdateCommand.RaiseCanExecuteChanged();
PropertyChanged(this, new PropertyChangedEventArgs("Contact"));
}
}
private bool _isRefreshEnabled=true;
public bool IsRefreshEnabled
{
get { return _isRefreshEnabled; }
set
{
_isRefreshEnabled = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsRefreshEnabled"));
}
}
private bool _isRefreshProgressActive = false;
public bool IsRefreshProgressActive
{
get { return _isRefreshProgressActive; }
set
{
_isRefreshProgressActive = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsRefreshProgressActive"));
}
}
public ContactsViewModel()
{
DeleteCommand = new RelayCommand(OnDelete, CanDelete);
UpdateCommand = new RelayCommand(OnUpdate, CanUpdate);
LoadCommand = new RelayCommand(OnLoad, CanLoad);
DeleteAllContactsCommand = new RelayCommand(OnDeleteAllContacts, CanDeleteAllContacts);
RecreateFiltersCommand = new RelayCommand(OnRecreateFilters, CanRecreateFilters);
OnLoad();
}
public bool CanRecreateFilters()
{
return true;
}
public async void OnRecreateFilters()
{
IsRefreshProgressActive = true;
await Task.Run(() => _repository.ResetFilters());
IsRefreshProgressActive = false;
}
public async void OnDeleteAllContacts()
{
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure?", "DELETE ALL EXISTING CONTACTS", System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.Yes)
{
IsRefreshProgressActive = true;
await Task.Run(() => _repository.DeleteAllContacts());
IsRefreshProgressActive = false;
}
}
public bool CanDeleteAllContacts()
{
return true;
}
private void OnDelete()
{
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show("Are you sure?", "Delete Contact Confirmation", System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.Yes)
{
_repository.DeleteContactAsync(Contact);
Contacts.Remove(Contact);
}
}
private bool CanDelete()
{
if (Contact != null )
{
return true;
}
return false;
}
private void OnUpdate()
{
_repository.AddContactAsync(Contact);
}
private bool CanUpdate()
{
if (Contact != null )
{
return true;
}
return false;
}
public async void OnLoad()
{
IsRefreshEnabled = false;
IsRefreshProgressActive = true;
Contacts =await Task.Run(() => _repository.GetContactsAsync()) ;
IsRefreshEnabled = true;
IsRefreshProgressActive = false;
}
private ObservableCollection<Contact> GetContactsAsync()
{
return _repository.GetContactsAsync();
}
public bool CanLoad()
{
return true;
}
}
}
ContactRepository класс:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Digital_Data_House_Bulk_Mailer.Model;
using System.Data.Entity;
using System.Collections.ObjectModel;
using System.Data.Entity.Migrations;
using System.Collections;
using System.Data.Entity.Core.Objects;
using System.Data.SqlClient;
namespace Digital_Data_House_Bulk_Mailer.Repository
{
class ContactRepository : IContactRepository
{
digital_datahouse_bulk_mailerEntities db = null;
public ContactRepository()
{
db = new digital_datahouse_bulk_mailerEntities();
}
public string SELECT_ALL { get { return "Select All"; } private set { } }
public ObservableCollection<StateFilter> GetStates()
{
ObservableCollection<StateFilter> stateFilters = new ObservableCollection<StateFilter>();
foreach (StateFilter state in db.StateFilters.ToList() )
{
db.Entry<StateFilter>(state).Reload();
stateFilters.Add(state);
}
return stateFilters;
}
public ObservableCollection<CountyFilter> GetCounties()
{
ObservableCollection<CountyFilter> countyFilters = new ObservableCollection<CountyFilter>();
foreach (CountyFilter county in db.CountyFilters.ToList())
{
db.Entry<CountyFilter>(county).Reload();
countyFilters.Add(county);
}
return countyFilters;
}
public ObservableCollection<GenderFilter> GetGenders()
{
ObservableCollection<GenderFilter> genderFilters = new ObservableCollection<GenderFilter>();
foreach (GenderFilter gender in db.GenderFilters.ToList())
{
db.Entry<GenderFilter>(gender).Reload();
genderFilters.Add(gender);
}
return genderFilters;
}
public ObservableCollection<IndustryFilter> GetIndustries()
{
ObservableCollection<IndustryFilter> industryFilters = new ObservableCollection<IndustryFilter>();
foreach (IndustryFilter industry in db.IndustryFilters.ToList())
{
db.Entry<IndustryFilter>(industry).Reload();
industryFilters.Add(industry);
}
return industryFilters;
}
public ObservableCollection<IsContactedFilter> GetIsContacted()
{
ObservableCollection<IsContactedFilter> isContactedFilters = new ObservableCollection<IsContactedFilter>();
foreach (IsContactedFilter isContacted in db.IsContactedFilters.ToList())
{
db.Entry<IsContactedFilter>(isContacted).Reload();
isContactedFilters.Add(isContacted);
}
return isContactedFilters;
}
public ObservableCollection<SicCodeDescriptionFilter> GetSicCodeDescriptions()
{
ObservableCollection<SicCodeDescriptionFilter> sicCodeDescriptionFilters = new ObservableCollection<SicCodeDescriptionFilter>();
foreach (SicCodeDescriptionFilter sicCodeDescriptionFilter in db.SicCodeDescriptionFilters.ToList())
{
db.Entry<SicCodeDescriptionFilter>(sicCodeDescriptionFilter).Reload();
sicCodeDescriptionFilters.Add(sicCodeDescriptionFilter);
}
return sicCodeDescriptionFilters;
}
public void AddContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.AddOrUpdate(contact);
db.SaveChangesAsync();
}
}
public void DeleteContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.Remove(contact);
db.SaveChangesAsync();
}
}
public void UpdateContactAsync(Contact contact)
{
if (contact != null)
{
db.Contacts.AddOrUpdate(contact);
db.SaveChangesAsync();
}
}
public ObservableCollection<Contact> GetContactsAsync()
{
db = new digital_datahouse_bulk_mailerEntities();
ObservableCollection<Contact> contacts = new ObservableCollection<Contact>();
foreach (var contact in db.Contacts.ToList())
{
contacts.Add(contact);
}
return contacts;
}
public ObservableCollection<Contact> FilterContacts(ObservableCollection<StateFilter> states, ObservableCollection<CountyFilter> counties,
ObservableCollection<GenderFilter> genders, ObservableCollection<IndustryFilter> industries, ObservableCollection<IsContactedFilter> contacted,
ObservableCollection<SicCodeDescriptionFilter> codes, bool hasWebsite)
{
db = new digital_datahouse_bulk_mailerEntities();
ObservableCollection<Contact> filteredContacts = new ObservableCollection<Contact>();
string[] stateArray = (from s in states where s.IsChecked==true select s.State).ToArray();
string[] countyArray= (from c in counties where c.IsChecked==true select c.County).ToArray();
string[] genderArray= (from g in genders where g.IsChecked==true select g.Gender).ToArray();
string[] industryArray = (from i in industries where i.IsChecked==true select i.Industry).ToArray();
string[] contactedArray = (from c in contacted where c.IsChecked==true select c.IsContacted.ToString()).ToArray();
string[] sicCodeArray = (from c in codes where c.IsChecked==true select c.SicCodeDescription).ToArray();
var contacts=(from c in db.Contacts
where
stateArray.Contains(c.State) &&
countyArray.Contains(c.County) &&
genderArray.Contains(c.Gender) &&
industryArray.Contains(c.Industry) &&
contactedArray.Contains(c.IsContacted) &&
sicCodeArray.Contains(c.SIC_Code_Description)
select c).ToList();
foreach (Contact contact in contacts)
{
if (hasWebsite==true)
{
if (!String.IsNullOrEmpty(contact.WebSite))
{
filteredContacts.Add(contact);
}
}
else
{
filteredContacts.Add(contact);
}
}
return filteredContacts;
}
public void AddContactsRange(ObservableCollection<Contact> contacts)
{
db.Contacts.AddRange(contacts);
db.SaveChanges();
}
public void ResetFilters()
{
ResetStates();
ResetCounties();
ResetIsContactedFilters();
ResetGenders();
ResetIndustries();
ResetSicFilters();
}
private void ResetStates()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [StateFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<StateFilter> stateFilters = new List<StateFilter>();
var states = (
from c in db.Contacts
select c.State
).Distinct().ToList();
foreach (var stateName in states)
{
StateFilter state = new StateFilter();
state.State = stateName;
state.IsChecked = true;
stateFilters.Add(state);
}
db.StateFilters.AddRange(stateFilters);
db.SaveChanges();
}
public void DeleteAllContacts()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [Contact]");
db = new digital_datahouse_bulk_mailerEntities();
}
private void ResetCounties()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [CountyFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<CountyFilter> countyFilters = new List<CountyFilter>();
var counties = (
from c in db.Contacts
select c.County
).Distinct().ToList();
foreach (var countyName in counties)
{
CountyFilter county = new CountyFilter();
county.County = countyName;
county.IsChecked = true;
countyFilters.Add(county);
}
db.CountyFilters.AddRange(countyFilters);
db.SaveChanges();
}
private void ResetGenders()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [GenderFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<GenderFilter> genderFilters = new List<GenderFilter>();
var genders = (
from c in db.Contacts
select c.Gender
).Distinct().ToList();
foreach (var genderName in genders)
{
GenderFilter gender = new GenderFilter();
gender.Gender = genderName;
gender.IsChecked = true;
genderFilters.Add(gender);
}
db.GenderFilters.AddRange(genderFilters);
db.SaveChanges();
}
private void ResetIndustries()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [IndustryFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<IndustryFilter> industryFilters = new List<IndustryFilter>();
var industries = (
from c in db.Contacts
select c.Industry
).Distinct().ToList();
foreach (var industryName in industries)
{
IndustryFilter industry = new IndustryFilter();
industry.Industry = industryName;
industry.IsChecked = true;
industryFilters.Add(industry);
}
db.IndustryFilters.AddRange(industryFilters);
db.SaveChanges();
}
private void ResetIsContactedFilters()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [IsContactedFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<IsContactedFilter> isContactedFilters = new List<IsContactedFilter>();
var isContacted = (
from c in db.Contacts
select c.IsContacted
).Distinct().ToList();
foreach (var contactedName in isContacted)
{
IsContactedFilter contacted = new IsContactedFilter();
contacted.IsContacted = contactedName;
contacted.IsChecked = true;
isContactedFilters.Add(contacted);
}
db.IsContactedFilters.AddRange(isContactedFilters);
db.SaveChanges();
}
private void ResetSicFilters()
{
db.Database.ExecuteSqlCommand("TRUNCATE TABLE [SicCodeDescriptionFilter]");
db = new digital_datahouse_bulk_mailerEntities();
List<SicCodeDescriptionFilter> sicFilters = new List<SicCodeDescriptionFilter>();
var sics = (
from c in db.Contacts
select c.SIC_Code_Description
).Distinct().ToList();
foreach (var sic in sics)
{
SicCodeDescriptionFilter sicCode = new SicCodeDescriptionFilter();
sicCode.SicCodeDescription = sic;
sicCode.IsChecked = true;
sicFilters.Add(sicCode);
}
db.SicCodeDescriptionFilters.AddRange(sicFilters);
db.SaveChanges();
}
public void UpdateIsContactedInformation(Contact contact)
{
db = new digital_datahouse_bulk_mailerEntities();
contact.IsContacted = "True";
contact.MessageDateSent = DateTime.Today;
db.Contacts.AddOrUpdate(contact);
db.SaveChanges();
}
}
}
Это представление, которое использует ViewModel, оно называется ContactsView - это представление включено в MainWindow, как и другие представления.
КонтактыПросмотр:
<UserControl x:Class = "Digital_Data_House_Bulk_Mailer.View.ContactsView"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:local = "clr-namespace:Digital_Data_House_Bulk_Mailer.View"
xmlns:Controls = "clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:model = "clr-namespace:Digital_Data_House_Bulk_Mailer.ViewModel"
mc:Ignorable = "d"
d:DesignHeight = "300" d:DesignWidth = "300">
<UserControl.DataContext>
<model:ContactsViewModel/>
</UserControl.DataContext>
<Grid Margin = "0,0,-690,-36" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width = "61*"/>
<ColumnDefinition Width = "10*"/>
</Grid.ColumnDefinitions>
<Button Style = "{StaticResource AccentedSquareButtonStyle}" Command = "{Binding DeleteCommand}" Controls:TextBoxHelper.ClearTextButton = "True" x:Name = "delete" Content = "DELETE" HorizontalAlignment = "Left" Margin = "115,21,0,0" VerticalAlignment = "Top" Width = "100" RenderTransformOrigin = "0.267,0.519"/>
<Button Style = "{StaticResource AccentedSquareButtonStyle}" Command = "{Binding UpdateCommand}" Controls:TextBoxHelper.ClearTextButton = "True" x:Name = "update" Content = "add / update" HorizontalAlignment = "Left" Margin = "10,21,0,0" VerticalAlignment = "Top" Width = "100" RenderTransformOrigin = "0.267,0.519"/>
<Button Style = "{StaticResource AccentedSquareButtonStyle}" Controls:TextBoxHelper.ClearTextButton = "True" x:Name = "bulk_import" Content = "import new data" HorizontalAlignment = "Left" Margin = "220,21,0,0" VerticalAlignment = "Top" Width = "110" RenderTransformOrigin = "0.267,0.519" Click = "bulk_import_Click"/>
<DataGrid SelectedItem = "{Binding Contact}" AutoGenerateColumns = "True" ItemsSource = "{Binding Path=Contacts, Mode=TwoWay}" Style = "{StaticResource AzureDataGrid}" x:Name = "dataGridCodeBehind" Margin = "10,54,521,0" VerticalAlignment = "Top" HorizontalAlignment = "Stretch" HorizontalContentAlignment = "Stretch" ColumnWidth = "*">
<DataGrid.ColumnHeaderStyle>
<Style TargetType = "DataGridColumnHeader">
<Setter Property = "FontSize" Value = "10"/>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
<Button IsEnabled = "{Binding IsRefreshEnabled}" Style = "{StaticResource AccentedSquareButtonStyle}" Command = "{Binding LoadCommand}" x:Name = "refreshData" Content = "Refresh Contacts" HorizontalAlignment = "Left" Height = "22" Margin = "335,21,0,0" VerticalAlignment = "Top" Width = "114"/>
<Controls:ProgressRing IsActive = "{Binding IsRefreshProgressActive}" Foreground = "{DynamicResource AccentColorBrush}" Margin = "720,0,0,0" RenderTransformOrigin = "-2.889,0.463" HorizontalAlignment = "Left" VerticalAlignment = "Top" Height = "50" Width = "50"/>
<Button x:Name = "deleteContacts" Command = "{Binding DeleteAllContactsCommand}" Style = "{StaticResource AccentedSquareButtonStyle}" Content = "Delete All Contacts" HorizontalAlignment = "Left" Height = "22" Margin = "454,21,0,0" VerticalAlignment = "Top" Width = "114"/>
<Button x:Name = "recreateFilters" Command = "{Binding RecreateFiltersCommand}" Content = "Recreate TO MAIL Filters" HorizontalAlignment = "Left" Style = "{StaticResource AccentedSquareButtonStyle}" Height = "22" Margin = "573,21,0,0" VerticalAlignment = "Top" Width = "142"/>
</Grid>
Очень плохо то, что когда я пытаюсь отладить, он даже не переходит к вызову кода - метода. Пытался также использовать некоторые инструменты профилирования, но я не пришел к какому-либо выводу, что вызывает эту проблему с зависанием ...
Когда я запускаю запрос к базе данных из редактора базы данных, я получаю мгновенные результаты.
Я также использую отчет SSRS в приложении, отображаемом с помощью ReportViewer - он также отлично работает и возвращает те же данные - он отображается в отдельном представлении.
В одном из представлений я показываю GIF-анимацию с использованием библиотеки WpfAnimatedGif - я попытался удалить эту ссылку, чтобы посмотреть, не вызывает ли это проблема, но это не значит, что замораживание продолжается ...
Я также попытался переписать свой класс репозитория, чтобы использовать команду using для создания нового экземпляра db для каждого метода, но проблема не в этом.
Файлы решения: Ссылка на решение Google Disk
Несколько дней назад я смог загрузить 100 000 данных без каких-либо проблем, теперь я попытался загрузить 10 контактов, и они не зависают, когда я загружаю 1000, они зависают. Я попытался удалить async и await, и это не имеет значения - он снова зависает. A также попытался удалить все стили из этого представления, все равно без разницы. Это может быть проблема с базой данных, возможно, файл каким-то образом поврежден. Странно то, что у меня есть представление, в котором я получаю одни и те же данные, и они загружаются нормально ...
A также уменьшил размер локального файла mdf, запустив DBCC SHRINKDATABASE (0); GO, опять приложение зависает
Подключите профилировщик к своей базе данных и посмотрите, что происходит, когда вы запускаете замораживание. В вашем коде ничего не выделяется, но создание DbContexts с последующим возвратом сущностей определенно будет неэффективным. Я бы рекомендовал не возвращать Entities в ваше представление, вместо этого создайте модель просмотра, выберите свои объекты и заполните модели просмотра, чтобы вернуть только те данные, которые вам нужны. Используя фильтрацию, по возможности, составляйте условия в свой Linq, а не перебирайте список сущностей.
@ Steve Py - поскольку я использую локальный файл mdf, я не уверен, как подключить профилировщик студии управления сервером sql для мониторинга локального файла mdf db ...
Я повторно подключил приложение к базе данных Sql Server и запустил профилировщик на базе данных, не возвращает никаких операторов sql после зависания приложения. Приложение зависает независимо от того, использует ли он локальный файл db или sql-сервер ... Так что проблема здесь не в этом
Я думаю, это связано с неправильными асинхронными методами. Как я уже сказал в своем первом комментарии, паттерн Task.Run странный. Кроме того, async void - это антипаттерн. Хотя бы вернуть Task. Зависание при отсутствии доступа к базе данных указывает на взаимоблокировку потоков.
Я попробовал обе вещи, чтобы вернуть задачу вместо void, а также попробовал второй вариант полного удаления async и await, сделав вызовы synh, без изменений.
Я включил папку решения, заархивированную на диске Google, со всеми необходимыми элементами для воссоздания проблемы, перейдите в раздел «Настройка данных», нажмите «Импорт», сначала выберите файл Alaska_short для импорта, нажав «Выбрать файл», установите оба флажка, для разделителя выберите запятую, нажмите «Загрузить» данные для сопоставления, сопоставьте хотя бы название компании и адрес электронной почты с GenericCompanyEmailAddress, нажмите «Импорт», когда закончите, закройте окно для импорта и нажмите кнопку «Обновить». Все будет хорошо. Повторите процесс с файлом Alaska_large.zip, и приложение зависнет.
Список контактов заполняется из базы данных, похоже, проблема не в этом, я также попытался воссоздать весь частичный ContactsView с нуля - замораживание все еще продолжается.
Когда я ограничиваю загрузку до 50 контактов, он загружает данные через 10 секунд, после того как я увеличиваю количество записей, которые он замораживает, я также пытался вручную определить только несколько столбцов для отображения, но сетка снова зависает. Похоже, это проблема рендеринга сетки ...





Я наконец решил эту проблему. Мне нужно было добавить свойство Height = "500" в сетку данных.
Удаление свойства Height приводит к зависанию DataGrid при отображении более 50 строк в моем случае. С добавленной высотой он загружает 5000 записей менее чем за секунду.
Просто предложение.
Я работал с сеткой с большими списками. Я бы посоветовал вам включить виртуализацию, и это может улучшить производительность пользовательского интерфейса.
вы можете обратиться по этой ссылке для получения дополнительной информации
1. Почему вы используете асинхронный метод в задаче (
Task.Run(() => _repository.GetContactsAsync()))? 2. Зачем загружаете контакты все? 3. Размер базы данных не имеет большого значения, о скольких записях идет речь? Он все еще зависает, когда вы загружаете меньше данных?