Отговори на някои често срещани въпроси
I. Общи въпроси:
Въпрос: Кога е нужно да слагам в класовете си свойства, и кога мога да слагам публични полета? Не е ли вторият вариант по-удобен, особено когато едно поле не се валидира по никакъв начин?
Отговор: Винаги е по-добре да се ползват свойства, както и да се спазва конвенцията по именуването. Това води до много по-лесна поддръжка на кода. Ако аксесоарите методи не извършват допълнителни операции, то скоростта на изпълнение не намалява.
Въпрос: Имам конзолно приложение и не мога да изпиша нищо на български например:
"Натиснете някой клавиш за продължение ..." , дава ми само ????.
Отговор: Windows конзолата по принцип не подържа Unicode. За да ползваш кирилица, трябва от Regional Options в Control Panel да зададеш Bulgarian в Language for non-Unicode programs полето.
Въпрос: Трябва ли проекта да е на български език?
Отговор: Приема се и английски език.
Въпрос: Как най-лесно мога да създавам обекти и техните properties от даден клас по време на изпълнение без да зная какви са те по време на компилация - или искания клас и свойствата ги чета от файл или потребителят ги вкарва по време на изпълнение да кажем в TextBox?
Отговор: За тази цел се използва Reflection.
Въпрос: Как лесно мога да променям конфигурационния файл по време на изпълнение? Има ли клас, както е за четенето от него.
Отговор: В .NET Framework 1.1 не е предвидено лесното записване в конфигурационния файл. Можем да направим собствен клас, който променя app.config чрез XML парсера.
Ето пример как да се промени даден ключ:
XmlDocument doc = new XmlDocument();
string fileName = "App.config";
try
{
doc.Load(fileName);
foreach (XmlNode node in doc["configuration"]["appSettings"])
{
if(node.Name == "add")
{
if (node.Attributes.GetNamedItem("key").Value == key)
{
node.Attributes.GetNamedItem("value").Value = value;
break;
}
}
}
doc.Save(fileName);
doc = null;
}
catch (XmlException e)
{
Console.WriteLine(e.Message);
}
II. Въпроси за Windows Forms
Въпрос: Имам форма, която вика друга форма (нито една от тях не е MDI контейнер) обаче като опитам да затворя първата форма (тази която вика другата) не мога.
Отговор: Ако втората форма е модална, не може. С Show() ще може да се затвори приложението, с ShowDialog() няма да може.
Въпрос: Имам си един ListBox и при всяка нова селекция в TreeView-то трябва да му refresh-вам данните. До тук добре. Ето кода:
cds = srv.GetCDFromCategory(path);
lstBoxCDs.DataSource = cds.Tables["CDs"];
lstBoxCDs.DisplayMember = "name";
Първите два пъти като цъкам из TreeView-то всичко е ОК. После спира да refresh ListBox-а. При дебъг всичко е наред, в DataSet-а си има таблица със съответните cd-та в нея.
Отговор: Това е добре известен проблем. Трябва да се махне binding-а и да се сложи наново.
От типа на:
lstBoxCDs.DataSource = null; // Remove binding
cds = srv.GetCDFromCategory(path);
lstBoxCDs.DataSource = cds.Tables["CDs"];
lstBoxCDs.DisplayMember = "name";
Въпрос: Трябва ми в Windows Form да запомням една int стойност като влизам от една в друга форма. Пробвах да направя public, но като вляза в друга форма и то винаги е 0.
Отговор: Най-правилно е да се предава стойността в конструктора на новосъздадената форма.
Въпрос: Искам да направя така че в ComboBox да могат да се избират само items от неговия списък, т.е да не може да се пише друго. Има ли някое специално свойство за тази цел?
Отговор: Да: DropDownStyle = DropDownList
Въпрос: Windows Forms:
Имам две форми и искам да премина от едната в другата.
Отговор: Ако едната форма е родителска и от нея се вика другата, може да се скрие първата. Ако се унищожи, ще се унищожат и двете форми, но можеш да се скрие с Hide() и когато се приключи с дъщерната форма, да се затвори и родителската. Може да стане ето така:
childForm.Show();
Hide();
В Windows Forms има разлика между Form.Hide() и Form.Close().
Ако скриеш всички отворени форми на приложението, то няма да спре. За да спре приложението трябва всички негови форми да за затворени. Hide() просто скрива формата - тя си остава активна, получава си съобщенията и т.н., но е невидима.
Close() затваря формата и унищожава всички ресурси, свързани с нея (най-вече освобождава hWND хендъла) и спира обработката на съобщения.
III. Въпроси за бази данни
Въпрос: Как се escape-ва символът ' (апостроф) при SQL заявка?
Отговор: Пишат се два апострофа един след друг:
select 'aaa''a' -> aaa'a
Това не се налага, когато се ползват параметризирани заявки чрез ADO.NET Data Provider. Това е препоръчителният начин.
Въпрос: Съхранявам картинките в базата. При достъп до тях не могат да се визуализират коректно. Има ли ограничение на размера за тип image? Полето е 16 байта.
Отговор: При взимането на картинките от базата трябва да бъде възстановено оригиналното им разширение: .png, .jpg и т.н. Картинките се пазят извън таблицата, полето от 16 байта е указател към тях и не представлява ограничение.
Въпрос: Искам да си променя вече съществуваща картинка в SQL база данни. Как трябва да ми изглежда SQL командата с UPDATE. Възможно ли е да стане с INSERT и с параметър и дали INSERT-а няма да омаже вече съществуващите неща.
Отговор: Ето пример за такъв UPDATE:
sqlCommand.Connection=....
sqlCommand.CommandText = "update TableImage set TableImage_Image = @newImage where TableImage_ImageID = @oldImageID"
byte[] image = ... картинката е в този масив
sqlCommand.Parameters.Add( "@newImage", image );
int id = 10
sqlCommand.Parameters.Add( "@oldImageID", id )
sqlCommand.ExecuteNonQuery();
В примера:
1. TableImage е името на таблицата за която стажа дума
2. TableImage_Image е колоната с image-а
3. TableImage_ImageID е ID, по което да можеш да различаваш картинките.
Въпрос: Защо, когато имам активна транзакция в SqlConnection и не мога да правя заявки, които не участват в транзакцията? Това става само докато е активна транзакцията. Например, следният код не работи:
IDbTransaction tran;
tran.BeginTransaction();
IDbCommand cmd = aSqlConnection.CreateCommand();
cmd.Transaction = tran;
cmd.CommandText = ...
cmd.ExecuteNonQuery();
IDbCommand cmd1 = aSqlConnection.CreateCommand()
cmd1.CommandText = ...
cmd1.ExecuteNonQuery();
tran.Commit();
Отговор: За да се правят заявки извън транзакцията, трябва да отвори нова връзка.
Въпрос: Обхождам таблица с автори с IDataReader и за всеки автор обхождам публикациите му с друг IDataReader. Получавам следното изключение:
Unhandled Exception: System.InvalidOperationException: There is already an open DataReader associated with this Connection which must be closed first.
Отговор: Това е добре известна слабост на SqlClient DataProvider - за всяка връзка с базата се позволява един отворен курсор. В случая проблема може да се реши като се отваря нова връзка при обхождане на публикациите.
Въпрос: Как да се съхраняват дати в базата данни?
Отговор: За работа с дати се прави така:
1) Всички колони, които съдържат дати, трябва да се от тип дата в базата данни (DateTime в SQL Server)
2) Всички методи, които приемат като параметър или връщат дати по някакъв начин трябва да приемат/връщат типът System.DateTime, а не стринг!!
3) Винаги, когато се прави преобразуване от дати към стринг или обратното, трябва да се задава култура или точен формат. Иначе приложението не е преносимо и в друга среда не може да работи правилно.
Въпрос: Как да си пренеса MSSQL базата данни от един компютър на друг? Например за защитата?
Отговор: Има 2 начина да стане това:
1 начин - backup/restore
За backup на MS SQL база данни се прави така:
SQL Server Enterprise Manager -> Databases -> твоята база -> All Tasks -> Backup Database -> задаваш някакво име на файл и натискаш бутона "OK"
За restore на MS SQL база данни се прави така:
SQL Server Enterprise Manager -> All Tasks -> Restore Database -> From device -> Select devices -> избираш файла -> избираш името на базата, под което ще възстановяваш и натискаш бутона "OK"
2 начин - копиране на файловете
Прави се така:
1) SQL Server Enterprise Manager -> All Tasks -> Detach Database
2) Копират се файловете на базата данни от директория:
C:\Program Files\Microsoft SQL Server\MSSQL\Data
За всяка база има по 2 файла - базата.mdf и базата.ldf
3) На новата машина след като се копират базата.mdf и базата.ldf в C:\Program Files\Microsoft SQL Server\MSSQL\Data се атачва базата:
SQL Server Enterprise Manager -> All Tasks -> Attach Database
Въпрос: Като добавям запис на кирилица в базата данни и след това като чета от него ми излизат ????.
Отговор: Полето в базата данни трябва да е от тип nvarchar за да се използва коректно кирилица. Ако е varchar се наблюдава описаният проблем.
Въпрос: Имам реализирана дървовидна структура, като в таблицата всеки запис има PK и съответно FK, който сочи към родителя му – в същата таблица. Как да подходя за да изтрия даден елемент и всичките му деца?
Отговор: Тук не може да се използва CASCADE DELETE, нито рекурсивна записана процедура с курсор, тъй като курсорът е глобален. Рекурсивното триене и като цяло операциите свързани с дървовидни структури в SQL Server са доста разискван проблем. Добри примери има в групите на Гугъла – като тази тема (виж http://groups-beta.google.com/group/microsoft.public.sqlserver.programming/browse_thread/thread/bab5ab169f26dc23/824c2bf26c2fad94?q=recursive+delete+T-SQL&_done=%2Fgroups%3Fq%3Drecursive+delete+T-SQL%26start%3D0%26hl%3Den%26lr%3D%26&_doneTitle=Back+to+Search&&d#824c2bf26c2fad94).
Като цяло обаче, смятам, че този (http://www.winnetmag.com/SQLServer/Forums/messageview.cfm?catid=1666&threadid=116492) подход също е доста добър. В частност той използва променливи от тип table, които също се съхраняват в tempDB.
Въпрос: Как да направя полето ID, което е цяло число автоматично да се увеличава при добавяне на нов запис?
Отговор: Използвай AutoNumber (в MS Access) или Identity (в MS SQL Server). За да извлечеш автоматично-генерираният primary key, виж слайда със заглавие "Първичен ключ – извличане" в лекцията за ADO.NET - там е описано как се прави - извиква се "SELECT @@IDENTITY".
Въпрос: Мога ли да достъпя една стойност от базата данни, или всеки път трябва да работя с таблици?
Отговор: Може да се достъпи една стойност чрез SqlCommand. Върнатата стойност от ExecuteScalar() задължително трябва да се преобразува към очакваният тип:
SqlCommand ordersCMD = new SqlCommand("SELECT Count(*) FROM Authors", nwindConn);
Int32 count = (Int32)ordersCMD.ExecuteScalar();
Това е примерна in-line заявка
Ето и пример за записана процедура, която връща единствена стойност:
CREATE PROC usp_InsertAuthor
(
@au_fname varchar(50),
@au_lname varchar(50)
)
AS
INSERT Authors (au_fname, au_lname, active)
VALUES (@au_fname, @au_lname, 1)
SELECT @@identity
GO
Въпрос: Използвам следната SQL заявка за добавяне на запис:
insert [Table] values (@UserID,@ImageID,@Hyperlink,@Type,@RemainingShow)
Където Table има Primary Key TableID, който се увеличава автоматично с единица. Въпроса ми е как мога да разбера стойността на TableID за стойностите които се добавят.
Отговор: То автоматично се задава на променливата @@Identity. Можеш да използваш нея след INSERT.
Въпрос: Какво точно трябва да съдържа Connection string-a за връзка с базата данни?
Отговор: При MS SQL Server база от данни можеш да ползваш нещо такова:
SqlConnection dbConn = new SqlConnection(@"server=localhost; database=Northwind; Integrated Security=SSPI; Persist Security Info=false");
При MS Access база данни можеш да ползваш нещо такова:
OleDbConnection dbConn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\photos.mdb;Persist Security Info=False");
Сподели с приятели: |