— У вас это… юзабилити плохое.
— Мне надо, чтобы было просто и удобно.
— Я не моряк, чтобы разбираться, как его крутить.
— Мне жаль вас, у вас всё так сложно и неудобно.
Взято с http://malinnikov.ru
Thinking out loud about development and etc.
Как-то раз попросил знакомый решить такую вот задачку (цитирую):
смотри есть к примеру ряд переменных
перем1
перем2
перем3
можно ли как то обратиться
к ним в цикле например
Для сч = 1 по 3 Цикл
сообщить(Перем+Сч)
КонецЦикла
<?
for($i = 0; $i<10; $i++)
{
$commonVar = "varName".$i;
$$commonVar = "new value".$i; /*в этом месте переменной с именем выражения "varName".$i присваивается значение "new value*/
echo $$commonVar."<br />";
}
?>

25 августа сего года компания DevExpress выпустила совершенно free очень полезную надстройку для Visual Studio 8 CodeRushExpress for C# and VB, скачать который можно тут.
CodeRushExpress предлагает расширенный набор функций для выделения кода, навигации в коде и объявлений классов и тд в коде.
Кроме того, CodeRush Xpress включает в себя более 50 рефакторингов, что, конечно же, просто замечательно. Если вы не счастливый обладатель полной версии CodeRush, то определенно must have в арсенале разработчика!
Замечательная тема, посвещенная Дню Программиста http://www.techdays.ru/DevDay/
Разработчики всех стран, объединяйтесь!
Итак, дошло время до практической части. Которая меня, скажем прямо, огорчила.
Так как хотелось использовать в своем проекте LinQ, а при попытке создать dbml-файлик штатными средствами студии выскакивает окошечко с ошибочкой (см. рис. ниже), то будем генерировать dbml с помощью DbMetal, о котором упоминалось в предыдущей части цикла.
Для начала создадим нашу базу данных, которая будет состоять из двух таблиц - Journal, главная таблица, в которую будут записываться факты поставки по конкретной дате, и таблица Items, в которой будут детали поставок по конктретной дате.
CREATE TABLE "journal" ("ID" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , "Date" DATETIME DEFAULT CURRENT_DATE);
CREATE TABLE "items" ("ID" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , "journalID" INTEGER NOT NULL , "supplier" VARCHAR DEFAULT supplier1, "store" VARCHAR DEFAULT store1, "amount" FLOAT DEFAULT 0)
@echo on
DbMetal.exe -dbml:sample.dbml -provider:Sqlite -conn="data source=sample.sqlite"
dbMetal.exe -code:sample.designer.cs -namespace:SampleDataContext -provider:Sqlite -conn="data source=sample.sqlite"
Так как мы пишем тестовое приложение, которое, естественно, не может затронуть все аспекты, скажу о некоторых отрицательных моментах, с которыми мне довелось столкнуться. Во-первых, не поддерживается тип Boolean. Долго пришлось возиться в поисках проблемы - резко началось рушиться приложение, а оказалось, всего-навсего, не sqlite не поддерживает тип Boolean, который использовался в одной из таблиц базы данных. И, хоть альтернативу логическому типу очень легко найти (использовать тот же varchar (1)), все же очень неприятно. Во-вторых, (но это уже проблема не самого sqlite), при dbmetal почему-то не генерирует представления :( Вот так просто нас лишили крутой возможности. Опять же, ручками все делается. Но как-то неспортивно. После ковыряния в исходниках dbmetal, которые лежат в открытом доступе на svn, и перекомпиляции с включеной опцией views, стали генерироваться пустые классы. На этом решено было не тратить больше время на всякую ерунду. Ведь существуют платные хорошо работающие альтернативы, если кому надо.
Теперь, собственно, будем все это оживлять.
class DBClass
{
private Main _connection = null;
public DBClass()
{
_connection = new Main(new XSqlConnection("Data Source=sample.sqlite"));
}
public List GetJournal()
{
List items = new List();
items = _connection.Journal.ToList();
return items;
}
public List GetItems(int journalId)
{
List items = new List();
items = _connection.Items.Where(i => i.JournalID == journalId).ToList();
return items;
}
public Items AddItem(Items item)
{
_connection.Items.InsertOnSubmit(item);
_connection.SubmitChanges();
return item;
}
public Journal AddJournal(DateTime date)
{
Journal item = new Journal() { Date = date };
_connection.Journal.InsertOnSubmit(item);
_connection.SubmitChanges();
return item;
}
public void DeleteItem(int id)
{
_connection.Items.DeleteOnSubmit(
_connection.Items.SingleOrDefault(i => i.ID == id)
);
_connection.SubmitChanges();
}
public void DeleteJournal(int id)
{
_connection.Journal.DeleteOnSubmit(
_connection.Journal.SingleOrDefault(i => i.ID == id)
);
_connection.SubmitChanges();
}
}
private void InitTree()
{
foreach (Journal item in db.GetJournal())
{
AddJournalDate(item);
}
}
private void AddJournalDate(Journal item)
{
TreeNode node = new TreeNode(item.Date.Value.ToShortDateString());
node.Tag = item.ID;
tvJournal.Nodes[0].Nodes.Add(node);
}
private void tvJournal_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (tvJournal.SelectedNode != null && tvJournal.SelectedNode.Tag != null)
{
int journalId = 0;
if (int.TryParse(tvJournal.SelectedNode.Tag.ToString(), out journalId))
{
lvItems.Items.Clear();
foreach (Items item in db.GetItems(journalId))
{
AddItem(item);
}
}
}
}
private void AddItem(Items item)
{
ListViewItem lvi = new ListViewItem(new string[] { item.Supplier, item.Store, item.Amount.ToString() });
lvi.Tag = item.ID;
lvItems.Items.Add(lvi);
}
CREATE TRIGGER "main"."OnDelete" DELETE ON journal BEGIN Delete From items Where items.journalID = deleted.ID; END
private void button2_Click(object sender, EventArgs e)
{
int journalId = (int)tvJournal.SelectedNode.Tag;
db.DeleteJournal(journalId);
tvJournal.Nodes[0].Nodes.Remove(tvJournal.SelectedNode);
}
private void addItemToJournal_Click(object sender, EventArgs e)
{
int journalId = 0;
if (int.TryParse(tvJournal.SelectedNode.Tag.ToString(), out journalId))
{
Items item = new Items() {
Amount = float.Parse( tbAmount.Text ),
Store = tbStore.Text,
Supplier = tbSupplier.Text,
JournalID = journalId
};
AddItem(db.AddItem(item));
}
}
private void DeleteItemFromJournal_Click(object sender, EventArgs e)
{
if (lvItems.SelectedItems.Count > 0)
{
int itemID = (int)lvItems.SelectedItems[0].Tag;
db.DeleteItem(itemID);
lvItems.Items.Remove(lvItems.SelectedItems[0]);
}
}
Бывают проекты, в которых необходимо хранение данных, объем и структура которых делает хранение в обычных файлах неудобным, а использование СУБД - слишком громоздкое и дорогое решение. Представьте, для программы весом в пару мегабайт, которая спокойно умещается на флешке, ставить SQL Server. Немного утрировано, но всё же...
Для решения подобного рода проблем очень хорошо подходят embedded-БД (встраиваемые БД), например, sqlite, firebird или из области объектных бд - db4o. Конечно, им всем далеко до полноценных СУБД, но не для конкуренции с ними создавались встаиваемые БД, а для облегчения распространения приложений, использующих небольшие БД. Понятно, что для достижения этой цели пришлось чем-то жертвовать, и каждый жертвовал, чем мог :) Так как разговор в данной статье должен идти о первой из списка embedded-БД - sqlite, то давайте как раз посмотрим, чем же порадуют нас разработчики.
Как ожидается из названия, данная embedded-БД должна быть лёгкой. Это правда - размер подключаемой библиотеки со всеми фичами, как говорят разработчики, меньше 300 КБ. К тому же, с ней легко работать, ее легко внедрять в создаваемое ПО, ее легко администрировать и еще много таких "легко". Sqlite распространяется на условиях "открытого исходного кода", скачать ее можно с официального сайта http://www.sqlite.org.
Чем хороша sqlite:
Чем плоха sqlite:
Естественно, SQLite не предназначена, чтобы быть двигателем базы данных предприятия, это не вариант для использования в крупных проектах, разве как временную резервную БД (и звучит это даже как-то по-мазохистски). Как написано на официальном сайте движка, sqlite не предназначена, чтобы заменить Oracle, но для замены fopen().