Wpf-приложение зависает - головоломка с утечкой памяти - PullRequest
0 голосов
/ 19 мая 2018

У меня есть wvf-приложение mvvm, которое работало правильно все до одного момента, когда оно зависает и вызывает массовую утечку памяти.Файлы решения: Google Disk Solution Link

Приложение использует локальный файл mdf, использует Mahhaps и некоторые дополнительные ссылки (например, одну для отображения gif-файлов)

enter image description here

Это вызов метода в модели представления, который создает проблему, назначение контактов с await создает проблему - это используется в другой модели представления, где он создаетникаких проблем, даже здесь все работало нормально до одного момента.

public async  void OnLoad()
        {
            IsRefreshEnabled = false;
            IsRefreshProgressActive = true;
            Contacts =await Task.Run(() => _repository.GetContactsAsync()) ;
            IsRefreshEnabled = true;
            IsRefreshProgressActive = false;
        }

Это способ визуализации представления

<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>

Я попытался удалить библиотеки, для gifs (GIF-файлы не отображаются в этом представлении в другом представлении, которое не создает никаких проблем).

Тот же самый вызов в хранилище - для получения данных о контактах, которые у меня есть в одном другом представлении, и он не делаеткакие-либо проблемы.

Это представление "Контакты" работало хорошо, пока все не внезапно.

Я попытался отладить, но отладчик даже не пришел к коду.

После загрузки некоторых dАта в БД Я получаю замораживание.

Остановка производится методом OnLoad из ContactsViewModel - этот вызов хранилища используется в другом ViewModel, где он не имеет никаких проблем - он возвращает данные быстро

ContactsViewModel код:

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как и другие представления

ContactsView:

<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 - я попытался удалить эту ссылку, чтобы посмотреть, не создает ли это проблему, но она не останавливается ...

Я также попытался переписать свой класс репозитория для использования с помощью командыдля создания нового экземпляра базы данных для каждого метода, но это не то, что создает проблему.

Файлы решения: Ссылка на решение для Google Диска

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Просто предложение.

Я работал с сеткой с большими списками.Я бы посоветовал вам включить виртуализацию, и это может внести некоторые улучшения в производительность пользовательского интерфейса.

вы можете обратиться по этой ссылке для получения дополнительной информации

https://docs.microsoft.com/en-us/dotnet/framework/wpf/getting-started/whats-new#new-features-for-the-virtualizingpanel

0 голосов
/ 21 мая 2018

Я наконец решил эту проблему.Мне нужно было добавить свойство Height = "500" в сетку данных.

Удаление свойства Height приводит к зависанию DataGrid при отображении более 50 строк в моем случае.С добавленной высотой он загружает 5000 записей менее чем за секунду.

...