Подключение WPF к SQL Compact

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Привет. Помогите создать подключение к SQL Compact, извлечь из него информацию и вывести ее в окно WPF. Вот что я написал:

Window1.xaml.cs

===

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

namespace WpfApplication1

{

public partial class Window1: Window

{

public Window1 ()

{

InitializeComponent ();

}

private void cmdGetProduct_Click (object sender, RoutedEventArgs e)

{

int ID;

if (Int32.TryParse (txtID.Text, out ID))

{

try

{

gridProductDetails.DataContext = App.StoreDB.GetProduct (ID);

}

catch

{

MessageBox.Show (“Ошибка подключения к базе данных.”);

}

}

else

{

MessageBox.Show (“Неверный ID.”);

}

}

}
}

App.xaml.cs

===

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Data;

using System.Linq;

using System.Windows;

using System.Data.SqlServerCe;

namespace WpfApplication1

{

public partial class App: Application

{

private static StoreDB storeDB = new StoreDB ();

public static StoreDB StoreDB

{

get {return storeDB;}

}

}

public class StoreDB

{

private string connectionString = “Data Source=’Database1.sdf’”;

public Product GetProduct (int ID)

{

SqlCeConnection connection = new SqlCeConnection (connectionString);

SqlCeCommand cmd = new SqlCeCommand ( “GetProductByID”, connection);

cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.AddWithValue ( “ProductID”, ID);

try

{

connection.Open ();

SqlCeDataReader reader = cmd.ExecuteReader (CommandBehavior.SingleRow);

if (reader.Read ())

{

Product product = new Product ((string) reader [ “ModelNumber” ],

(string) reader [ “ModelName” ], (decimal) reader [ “UnitCost” ],

(string) reader [ “Description” ]);

return (product);

}

else

{

return null;

}

}

finally

{

connection.Close ();

}

}

}

public class Product

{

private string modelNumber;

public string ModelNumber

{

get {return modelNumber;}

set {modelNumber = value;}

}

private string modelName;

public string ModelName

{

get {return modelName;}

set {modelName = value;}

}

private decimal unitCost;

public decimal UnitCost

{

get {return unitCost;}

set {unitCost = value;}

}

private string description;

public string Description

{

get {return description;}

set {description = value;}

}

public Product (string modelNumber, string modelName,

decimal unitCost, string description)

{

ModelNumber = modelNumber;

ModelName = modelName;

UnitCost = unitCost;

Description = description;

}

}
}

Программа постоянно доходит до MessageBox.Show (“Ошибка подключения к базе данных.”), не знаю в чем дело.

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Немного в догонку: static StoreDB storeDB = new StoreDB () — это зло. Потому что использовать потенциально тяжеловесные объекты на девайсе как статики не следует — замечена проблема со сборщиком мусора, когда объекты не диспозятся вообще, даже после завершения приложения. Видимо, сборщик мусора работет в фоновом потоке, и ему не хватает приоритета для сбора, а потом он и вовсе забивает. Эту проблема не наблюдалась, когда приоритет основного потока был ниже, чем ThreadPriority.Normal, но по-хорошему следует сделать StoreDB как синглтон, и реализовать у него интерфейс IDisposable. И диспозить его явно (все экземпляры в этом классе, в которых не реализован интерфейс IDisposable — прибивать насильно, присвоив null, и желательно завершить метод принудительным вызовом сборщика мусора — GC.Collect (); GC.WaitForPedingFinalizers ();)

Хотя, с другой стороны, на девайсе не бывает WPF, логично предположить, что код выполнятся на дескопе, но, несмотря на это, как говорится, хоть мы суслика не видим, но он все равно есть:)

Я только сейчас эту тему увидел, позвольте и мне вставить свои 5 копеек: практически, класс никуда не годится (полагаю, что он выполняется на устройстве), потому что:
1) Обязательно следует кешировать command и ни в коем случае не пересоздавать (и не диспозить). Потому что затраты времени при выборке в основном — это создание плана выполнения запроса, который сервер создает при создании данного объекта.
2) ProductID LIKE (?), а в качестве параметра интовое значение — это для десктопного SQL сервера прокатит, а компакту тут нужен строковый параметр, и в данном случае поведет себя непредсказуемо (может ничего не вернуть, а может вернуть, а потом может снова не вернуть — есть у него такая проблема при несоответствии типов, вот лично наблюдал такое, когда провтыкал с типом данных — брал MAX (строковое_поле_но_в_нем_одни_цифры), ну и на других запросах при несоответствии типов)
3) ИМХО, нужно избегать выходов из методов в серединах, в разных местах и т.д. Выход должен быть один — в конце, по ходу просто меняется результат возвращаемой переменной, иначе код может быть нечитабельным, по мере его ожирения.
4) Ну и con.Close (); возьми в трай кэтч, а иначе есть риск юзеру показать анхалтэд эксепшн, если коннекция не будет открыта по какой-либо причине.
5) В строке соединения следует явно указать путь, по которому сервер на девайсе будет создавать временные файлы, а он их создает при выборке, и явно вынести его на флеш-карту из onboard памяти, иначе есть риск, что основная память загадится временными файлами базы, которые кстати сами не удаляются, и будет стрелять нехватка памяти (склероз, я его подзабыл)
6) Вместо SqlCeDataReader лучше использовать SqlCeResultSet, с параметром в конструкторе ResultSetOptions.Insensitive — это улучшит производительность.

Просто, есть некоторые нюансы, которые в десктопном приложении будут совсем незаметны, дажеесли они неоптимальны, а вот на девайсе, где в среднем 500 мегагерц процессор и основная память 128 мегабайт разделяется между ОЗУ и файловой системой многое будет вылазить, если не сегодня, то обязательно завтра, или послезавтра, по мере наращивания функциональности (и обычно это стреляет в пятницу к концу рабочего дня, или на ревью перед билдом:))

Можно в конструкции SELECT * FROM не использовать *, а заменить ее на имена колонок...

Всем спасибо. Задача решена. Вот как надо было сделать:

public class StoreDB
    {
        public Product GetProduct(int ID)
        {
            SqlCeConnection con = new SqlCeConnection("Data Source = Database1.sdf");
            //SqlCeCommand cmd = new SqlCeCommand ("SELECT * FROM Products WHERE (nume LIKE ?)", con);
            SqlCeCommand cmd = con.CreateCommand();
            cmd.CommandText = "SELECT * FROM Products WHERE (ProductID LIKE ?)";
            cmd.Parameters.Add("ProductID", ID);
            try
            {
                con.Open ();
                SqlCeDataReader reader = cmd.ExecuteReader();
                if (reader.Read())
                {
                    Product product = new Product((string)reader["ModelNumber"],
                    (string)reader["ModelName"], (decimal)reader["UnitCost"],
                    (string)reader["Description"]);
                    reader.Close();
                    cmd.Dispose();
                    return (product);
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                con.Close();
            }
        }
    }

давай все по очереди — база данных создана? — таблицы в них есть? данные в них есть? — Data Provider для этой базы?
вот там есть небольшое описание для connectionstring и data provider

www.connectionstrings.com/...-server-2005-ce

Пример взят из книги «WPF в.Net 3.5 с примерами на C# 2008». Но там используется SQL Server, а мне нужно переделать в SQL Compact. Вот оригинальный текст:

public class StoreDB
    {
        private string connectionString = Properties.Settings.Default.StoreDatabase;

public Product GetProduct(int ID)
        {
            SqlConnection con = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand ("GetProductBylD", con);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue ("@ProductID", ID) ;
            try
            {
                con.Open ();
                SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow);
                if (reader.Read())
                {
                    // Создать объект Product, помещающий
                    //в оболочку текущую запись.
                    Product product = new Product((string)reader["ModelNumber"],
                    (string)reader["ModelName"], (decimal)reader["UnitCost"],
                    (string)reader["Description"],
                    (string)reader["ProductImage"]);
                    return(product);
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                con.Close();
            }
        }
    }

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

Підписатись на коментарі