oronsultan Ответов: 1

Исключение Wpf instance builder exception: nullreferenceexception: ссылка на объект не установлена на экземпляр объекта


Привет Ребята,
У меня есть sln внутри .Сеть которая основана на 2 проектах:
- Приложение Wpf под названием "Installer" и библиотека классов под названием "InstallerCore".
- "InstallerCore" отвечает за доступ к базе данных, и в основном это уровень данных.

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

Моя проблема-это конструктор экземпляров XAML. Как только я загружаю один из пользовательских элементов управления, я получаю исключение: "не удается создать экземпляр uc".

Так вот, это не совсем так. Если я просматриваю трассировку стека, то вижу, что внутреннее исключение скрывает более глубокую ошибку.:
"NullReferenceException: ссылка на объект не установлена на экземпляр объекта".

Позвольте мне показать вам, что происходит в коде:

Допустим, у меня есть класс с именем 'uc_file_system'.
В коде позади я загружаю его контекст данных из модели представления, подобной этой:
public partial class uc_file_system : UserControl
    {
        FileSystemViewModel vm;
        public uc_file_system()                                                         
        {
            InitializeComponent();
            vm = new FileSystemViewModel();
            this.DataContext = vm;
        }        
    }

Модели представления конструктор выглядит так:
public class FileSystemViewModel : BaseDA, INotifyPropertyChanged
{
public FileSystemViewModel() : base(GV.InstallerConnectionString)
        {
            GetSomeDataFromDataBase();
        }
void GetSomeDataFromDataBase()
        {
            List<p> prms = new List</p><p>();
            prms.Add(new P() { ParamName = "@description", ParamValue = "InstallationFolder" });
            DataTable res = ExecuteReader(InstallerConsts.GetGlobalParams, prms);
            InstallationBaseFolder = res.Rows[0][0].ToString();
        }
}

На ГВ.InstallerConnectionString-это свойство, которое возвращает строку подключения из статического класса:
public static class GV // {Global Variables}
    {
        #region Members        
        private static List<connectionstringinfo> connectionStrings;
        #endregion

        #region Properties
        
        public static string InstallerConnectionString
        {
            get { return ConnectionStringInfoHandler.GetConnectionString(ConnectionStrings, ConnectionStringType.Installer); }
        }
        public static List<connectionstringinfo> ConnectionStrings
        {
            get
            {
                return connectionStrings;
            }
            set
            {
                if (connectionStrings == null)
                {
                    connectionStrings = value;
                }
            }
        }
    }


ConnectionStrings устанавливается в файле app.xaml.cs следующим образом:
protected override void OnStartup(StartupEventArgs e)
        {
            GV.ConnectionStrings = (List<connectionstringinfo>)ConfigurationManager.GetSection(ConnectionStringInfoHandler.SectionName);
        }


Класс BaseDa в рамках проекта "InstallerCore" выглядит следующим образом:
public class BaseDA
{
public BaseDA(string connectionString)
        {
            ConnectionString = connectionString;
        }
}

А строка подключения поступает из файла app.config приложения wpf:

<configuration>
	<configsections>
		</p><section name="connectionStringInfos">
	
	<connectionstringinfos>
		<connectionstringinfo name="Installer" type="Installer" connectionstring="Data Source=localhost\sqlexpress;Initial Catalog=InstallerDB;Integrated Security=True;" providername="System.Data.SqlClient">
	
	<startup>
		<supportedruntime version="v4.0" sku=".NETFramework,Version=v4.5">

Теперь очень важно упомянуть, что как только я заменю ' GV.InstallerConnectionString' непосредственно со строкой, которая на самом деле является строкой подключения (жестко закодированной), исключение в xaml исчезает.
Также важно отметить, что код работает без ошибок во время выполнения, несмотря ни на что, и мне удается получить данные из базы данных.
Это трассировка стека:
at InstallerCore.Configuration.ConnectionStringInfoHandler.GetConnectionString(List`1 lst, ConnectionStringType type)
at Installer.Settings.GV.get_InstallerConnectionString()
at Installer.ViewModels.FileSystemViewModel..ctor()
at Installer.UserControls.uc_file_system..ctor()


Что я уже пробовал:

Почти все.................................

1 Ответов

Рейтинг:
2

Garth J Lancaster

хммм .. Я не так хорошо разбираюсь в xaml и т. д., Как следовало бы ... но глядя на это

public static class GV // {Global Variables}
    {
        #region Members        
        private static List<connectionstringinfo> connectionStrings;
        #endregion

        #region Properties
        
        public static string InstallerConnectionString
        {
            get { return ConnectionStringInfoHandler.GetConnectionString(ConnectionStrings, ConnectionStringType.Installer); }
        }

Я бы сказал, что есть разница между объявлением списка и фактическим созданием его экземпляра с помощью new (), а new() отсутствует

Я счастлив, что ошибся, это было просто из-за быстрого наблюдения. Но куда же девать новый ()? это более глубокий вопрос .. Я мог бы попробовать
public static string InstallerConnectionString
        {
            if (null == connectionstrings)
            {
              connectionstrings = new List<connectionstringinfo>();
            }
            get { return ConnectionStringInfoHandler.GetConnectionString(ConnectionStrings, ConnectionStringType.Installer); }
        }
но я все еще чувствую, что это может быть не самый лучший подход по сравнению с использованием конструктора для вашего класса GV и делать это в нем ....

[Правка] : определенно
public static GV()
{
  connectionstrings = new List<connectionstringinfo>();
}
это правильный путь, я просто пытался указать на "намерение" раньше


oronsultan

Дорогой Гарт,
Большое вам спасибо за Ваш быстрый ответ.
Хотя исключение все еще остается в силе, благодаря вашему ответу я теперь могу указать на объект, вызывающий проблему. Статическое свойство 'InstallerConnectionString' говорит о том, что список<connectionstringinfo> ConnectionStrings не инициализируется. Просто чтобы доказать это, если я перепишу код на это, все будет отлично работать:
публичная статическая строка InstallerConnectionString
{
получить
{
List<connectionstringinfo> cs = новый список<connectionstringinfo>();
cs.Add(new ConnectionStringInfo() { Type = ConnectionStringType.Installer, ConnectionString = @"источник данных=localhost\sqlexpress;начальный каталог=InstallerDB;Интегрированная безопасность=True;" });
возврат ConnectionStringInfoHandler.GetConnectionString(cs, ConnectionStringType.Установщик);
}
//get { return ConnectionStringInfoHandler.GetConnectionString(ConnectionStrings, ConnectionStringType.Установщик); }
}
Проблема в том, что, по словам компилятора, он не уверен, что ConnectionStrings действительно содержат что-либо.

Garth J Lancaster

Ну, я не совсем понимаю, почему статический конструктор init не работает

public static GV()
{
  connectionstrings = new List<connectionstringinfo>();
}

oronsultan

дорогой друг,
Инициализация списка внутри конструктора не приводит к его разрешению.
Только когда я пишу строку соединения жестко закодированную, ошибка останавливается, вот так:
статический ГВ()
{
connectionStrings = новый список<connectionstringinfo>();
соединительные нити.Add(new ConnectionStringInfo() { Type = ConnectionStringType.Installer, ConnectionString = @"источник данных=localhost\sqlexpress;начальный каталог=InstallerDB;Интегрированная безопасность=True;" });
}
Но если конструктор просто инициализирует список, то ошибка все равно возникнет:
статический ГВ()
{
connectionStrings = новый список<connectionstringinfo>();
}
Кстати, статический конструктор не может быть публичным :-)