Зарегистрировано: 295




Помощь  Карта сайта

Текст дня

The International Dark-Sky Association

Дальше..

Фото дня

M31_Flea3_Sigma70-300_f95/4_L_95x32sec_bin1

M31_Flea3_Sigma70-300_f95/4_L_95x32sec_bin1

M31, 10.2015, Flea3 bin1 L, Sigma70-300 f95/4, 48min(95x32sec) , SW Star adventurer


Тексты. Прозариум

Тексты на сайте могут публиковаться как в составе книг, по которым они "разложены", так и по отдельности. Тексты можно публиковать на странице их владельца, в блогах, клубах или рубриках сайта, а так же в виде статей и объявлений. Вы можете публиковать на сайте не только собственные тексты, но и те, которыми хотите поделиться с читателями, соблюдая авторские права их владельцев.
Prozarium CMS | Реклама, сотрудничество | Разработка, продажа сайтов

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



Опубликовано в: Клуб: c#
<--Программирование
<--IT Информационные технологии
<--Бизнес по сферам деятельности
Клуб: Программное обеспечение
<--IT Информационные технологии
<--Бизнес по сферам деятельности
Сайт: Публичные рубрики
Блог: Создание сайтов с CMS на C# и ASP.Net

0





Практическое использование коллекций в C#
/pterodactilus vulgaris/
24.03.2019


Вы можете помочь проекту 'C Sharp Collection Manager' развиваться, оказав посильную материальную помощь. Спасибо!

Скачать исходный код

using System;using System.Reflection;using System.Data;using System.Data.SqlClient;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Xml;using System.Configuration;namespace CS_Collections_Mapper{        /*Copyright © 2010-2019 pterodactilus.vulgaris (p.v.),     aka Евгений Трифонов, Санкт-Петербург    mailto: pterodactilus@rambler.ru    Ниже представлен рабочий вариант рукописной ORM, работающей с БД и коллекциями С#.    В ORM использутся Reflection. Производительность довольно высокая, потребление ресурсов минимальное.    Данная ORM работает напрямую с хранимыми процедурами на SQL Server,     однако их легко можно заменить на динамические запросы, в т.ч. LINQ, если это кому то это ближе.    Код сопровождается описаниями и примерами, показывающими технику его использования.    Код, представляющий примеры, снабжен комментарием                       // **** Sample *****    Код, используемый для реальной работы, снабжен комментарием             // **** Usefull *****    Вступление.    Данные хранятся на SQL сервере и при многократном обращении к ним большого количества одновременно подключенных пользователей,     последние создают серьезную нагрузку на сервер БД. Кроме того, часто запросы к БД осуществляют выборку одних и тех же данных,     что не есть хорошо с точки зрения нагрузки на приложения и серверы. Одним из способов оптимизации связки БД-Приложение,     снижения трафика и нагрузки на сервер, является хранение уже полученных с сервера данных на клиенте.     Или хранение полученных данных на сервере приложения, особенно, когда сервера БД и приложения разнесены по сети.     Для этой цели как нельзя лучше подходят коллекции, хранимые в сессии или в уровне приложения.     Ниже предлагается решение для слоя управления данными в связке mssqlserver с C# приложением,     как в win, так и asp.net или WCF.     По сути, это маппер, проецирующий сущности бизнес-логики приложения на таблицы БД, с использованием хранимых процедур T-SQL.    В данном сдучае таблицы, процедуры и конструкторы сущностей прописываются руками.     При этом код сущности и процедуры минимален и полностью контролируется разработчиком.    Весь клиентский код сводится к написанию простейших вызовов методов класса маппера и созданию простейших конструкторов сущностей.     Библиотека позволяет реализовать в приложении довольно сложные структуры данных, не погружаясь в рутину организации     доступа и хранения данных, в большей степени позволяя уделять внимание разработчика бизнес логике и UI.    Не надо заботиться о миграциях, контексте и пр. При этом ORM полностью избавляет рабработчика от общения с ADO.    Приложение свободно для загрузки и распространяется на условиях Donation Ware.     Не забудьте поблагодарить разработчика, используя ссылку Donate. Успехов!     */        //**********************************************************************        // как этим пользоваться?        //**********************************************************************        // **** Sample *****        public class Test    {        public void OperateWithBooks()        {            Guid author = Guid.NewGuid();            CollectionBase books = GetBooks(author);            List<Book> booksList = books.OfType<Book>().ToList();            Book Book = GetBook(booksList[0].BookID, books);            Book.CreatorID = Guid.NewGuid();            UpdateBook(Book.BookID);            RemoveBook(Book.BookID, books);        }        public CollectionBase GetBooks(Guid creatorID)        {            Hashtable parameters = new Hashtable();            parameters["@CreatorID"] = creatorID;            BooksList books = (BooksList)CollectionManager.GetCollection            (        "GetBooks",        "ID",        "CmsDb",        new BooksList(),        typeof(Book),                parameters,        true            );        return books;        }        public static Book GetBook(int bookId, CollectionBase collection)        {        return (Book)CollectionManager.GetCollectionItem(bookId, "id", collection, typeof(Book));        }        public static void UpdateBook(int bookID)        {            CollectionManager.UpdateCollectionItem(bookID, "id", StateManager.UserBooks, typeof(Book));        }        public static void RemoveBook(int bookID, CollectionBase collection)        {            Book book = GetBook(bookID, collection);            Hashtable parameters = new Hashtable();            parameters["@BookID"] = bookID;            SQLManager.ExecuteNonQuery("RemoveBook", "CmsDb", parameters);            CollectionManager.RemoveCollectionItem(bookID, "BookID", collection, typeof(Book));            StateManager.UserBooks.Remove(book);        }    }        // ****************** Описание работы и примеры ******************        /*В качестве примера, здесь представлены все необходимые для работы с          коллекциями классы клиентского приложения, использующие библиотеку           CollectionManager. Библиотека универсальна и оперирует с любыми типами коллекций         пользовательских классов, описанных в приложении. Она позволяет загружать с          сервера посредством пользовательских хранимых процедур наборы данных,          формирующих экземпляры класса и форм
ировать из этих экземпляров коллекции.          Кроме того, в библиотеке реализованы механизмы извлечения конкретного          экземпляра класса из коллекции, его модификации и вставки обратно в коллекцию          на то же место (сортировка по ID). Помимо этого в библиотеке присутствуют          методы добавления и удаления экземпляров коллекции.           Something - пример прототипа класса, с экземплярами которого работает импровизированная          коллекция.  Класс описывает структуру таблицы БД.     */        // **** Sample *****        public class Something : Sortable    {        // наследование от Sortable нужно, если в коллекции нам потребуется                 // сортировка по ID                // доступ к БД                public const string conStrAlias = "Your_DB_Alias";        // поля класса                private int id;        /* При использовании класса прототипа, наследуемого от другого класса, приватное поле id может называться по-другому.         */        /*В таком случае, (речь об этом пойдет ниже)  имеет смысл посмотреть в                 рефлекторе список полей и скорректировать вызывающие методы. По этой                 же причине ключ SomethingID нельзя объявлять в виде                  public string SomethingID { get; set; }, но можно объявлять так: */        public int SomethingID        {        get            {        return id;            }        set            {                id = value;            }        }        //поскольку рефлектор гарарантированно найдет приватное поле "id"            public string SomethingName { get; set; }        public int SecondID { get; set; }        // конструкторы класса. их два. используются разными методами библиотеки                 // при соответствующих подходах в реализации сущности.           public Something(SqlDataReader reader, int sortID)        {        //этот конструктор нужен для заполнения всей коллекции одним запросом                        FillBody(reader, sortID);        }        public Something(int somethingID)        {        using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[conStrAlias].ConnectionString))            {        using (SqlCommand command = new SqlCommand("GetSomething", connection))                {                    command.CommandType = CommandType.StoredProcedure;                    command.Parameters.Add(new SqlParameter("@SomethingID", somethingID));                    connection.Open();        using (SqlDataReader reader = command.ExecuteReader())                    {        while (reader.Read())                        {                            FillBody(reader, -1);                        }                        reader.Close();                        reader.Dispose();                    }                    command.Dispose();                }                connection.Close();                connection.Dispose();            }        }        // общий для обоих конструкторов метод заполнения полей класса                private void FillBody(SqlDataReader reader, int sortID)        {        try            {        if (!(reader["SomethingID"] is DBNull))                    SomethingID = (int)reader["SomethingID"];        if (!(reader["SomethingName"] is DBNull))                    SomethingName = (string)reader["SomethingName"];        if (!(reader["SecondID"] is DBNull))                    SecondID = (int)reader["SecondID"];            }        catch            {            }        }    }        /* Вся работа с данными в БД приложения сводится к написанию простейших         хранимых процедур для каждой сущности:                 Create PROCEDURE GetSomething            @SomethingID int = null,            @SomeParameter int = null,            @ReturnAtOnce bit = null                AS            if @SomethingID is not null                     -- тут достаем конкретный экземпляр класса                     SELECT ID as SomethingID, SomethingName, SecondID FROM Somethings                      WHERE (ID = @SomethingID)            else                    begin                             -- тут достаем все записи коллекции (только ID)                                  if @SomeParameter is not null                                    SELECT ID as SomethingID FROM Somethings Where                                     SecondID = @SomeParameter                             else                                    -- тут достаем все записи                SELECT ID as SomethingID FROM Somethings                     end              и к созданию в приложении классов, описывающих коллекции и методы для          операций с ними (хранение,  извлечение, модификация)        ***************************************************************        Somethings* - это класс прототип, предоставляющий методы добавления,         удаления и идентификация элементов коллекции        на базе этого класса строим коллекции:    */        // **** Sample *****        public class SomethingsList : ListRealizator    {        public static SomethingsList GetInstance()        {        return SomeGlobalClass.Somethings;        // в данном случае это статическая переменная в сессии пользователя,         // хранящая коллекцию. Для каждого пользовате
ля она будет уникальна.        }    }        // класс ListRealizator предоставляет методы добавления, удаления элемента                 // коллекции и индексатор. Коллекции наследуют его, реализуя интерфейс IList             // **** Usefull *****           public class ListRealizator : CollectionBase    {        public void Add(object obj)        {            List.Add(obj);        }        public void Remove(object obj)        {        try            {                List.Remove(obj);            }        catch            {            }        }        public object this[int itemIndex]        {        get            {        return (object)List[itemIndex];            }        set            {                List[itemIndex] = value;            }        }    }        // класс Sortable, нужный для сортировки элементов в коллекциях при обновлении                 // или при вставке новых элементов             // **** Usefull *****           public class Sortable : IEnumerable    {        private int sortid;        protected int id;        public int SortID        {        get            {        return sortid;            }        set            {                sortid = value;            }        }        public Sortable()        {        }        public Sortable(IDataReader reader, int sortID)        {        //этот конструктор нужен для заполнения коллекции одним запросом.         //Вызывается из CollectionManager через CreateInstance()            FillBody(reader, sortID);        }        public Sortable(int id_key, int sortID, string alias, string procName, string paramName = "@ID")        {        //этот конструктор нужен для заполнения коллекции по одному экземпляру отдельными запросами.        //заполнение коллекции по одному экземпляру может быть полезно, если какие-то поля класса сущности являются вычисляемыми на клиенте         //Вызывается из CollectionManager через CreateInstance()        using (SqlConnection connection = new SqlConnection(StateManager.DbConnections[alias].ConnectionString))            {        using (SqlCommand command = new SqlCommand(procName, connection))                {                    command.CommandType = CommandType.StoredProcedure;                    command.Parameters.Add(new SqlParameter(paramName, id_key));                    connection.Open();        using (SqlDataReader reader = command.ExecuteReader())                    {        while (reader.Read())                        {                            FillBody(reader, sortID);                        }                        reader.Close(); reader.Dispose();                    }                    command.Dispose();                }                connection.Close(); connection.Dispose();            }        }        public virtual void FillBody(IDataReader reader, int sortID)        {        try            {                SortID = sortID;                id = (int)reader["ID"];            }        catch            {            }        }        IEnumerator IEnumerable.GetEnumerator()        {        throw new NotImplementedException();        }    }        // класс EntityComparer, используемый для сортировки коллекций по Id             // **** Usefull *****           public class EntityComparer : IComparer    {        public int Compare(object tx1, object tx2)        {        // возвращает                //    -1 x < y                //     0  x = y                //     1  x > y                    Sortable x = (Sortable)tx1;            Sortable y = (Sortable)tx2;        if (x == null)            {        if (y == null)        return 0;        else        return -1;            }        else            {        if (y == null)        return 1;        else                {        try                    {        int retval = x.SortID.CompareTo(y.SortID);        if (retval != 0)        return retval;        else        return 0;                    }        catch                    {        return 0;                    }                }            }        }    }        /*Коллекций, использующих прототип Somethings мы можем построить сколь         угодно много, при этом базовый класс используется один и тот же(куча).     В примерах  ниже использованы три разны
х коллекции. Важно не путать их между собой, 
во избежание появления призраков, т.е.экземпляров одной коллекции в другой.
Например, кг яблок разделен на 2 мешка.В одном мои, в другом твои.
Если положить яблоко не в тот мешок(SomethingsList), кто-то удивится, а кто-то
недосчитается.При этом важно, что в обоих мешках яблоки (Something) и между
собой они ничем не отличаются.И твои, и мои яблоки приобретаются в общем
магазине(в куче). При этом, если мои яблоки могут закончиться, можно сходить
в магазин и взять других(в куче), а можно вырастить новые(создать)
*************************** Somethings1*****************************
это коллекция, использующая класс прототип Somethings и хранящаяся в сессии
*/

// **** Usefull *****
public class Heap : ListRealizator
{
//базовый класс для коллекций, наследуемых от кучи
}
//**************************** SomethingsHeap **********************
// это вторая коллекция, также использующая класс прототип Somethings, но
// хранящаяся в уровне приложения. В общем случае, коллекция SomethingsList
// может обнуляться, а элементы при заполнении брать из коллекции SomethingsHeap
// или из БД.
// **** Sample *****
public class SomethingsHeapList : ListRealizator
{
public static SomethingsHeapList GetInstance()
{
// тут выбирается экземпляр того же типа Something, но уже из общей
// кучи. сама куча хранится в уровне приложения.
// описание механизма выборки из кучи см. в CollectionManager
// (метод AddCollectionItem)
return SomeGlobalClass.SomethingsHeap;
}
}
// обе коллекции хранят однотипные экземпляры класса Something (возможно, одни
// и те же), при этом сами коллекции хранятся в разных местах и имеют разный
// жизнеенный цикл.
// **** Sample *****
public class SomeGlobalClass
{
public const string conStrAlias = "Your_DB_Alias";
// еще одна коллекция того же типа, для примера
public static SomethingsList Somethings2 { get; set; }
//**************** эти коллекции хранится в уровне приложения
private static SomethingsList _Somethings;
public static SomethingsList Somethings
{
get
{
return _Somethings ?? (_Somethings = (SomethingsList)SomeGlobalClass.GetSomethings(new SomethingsList()));
}
set
{
_Somethings = value;
}
}

//куча
private static SomethingsHeapList _SomethingsHeap;
public static SomethingsHeapList SomethingsHeap
{
get
{
if (_SomethingsHeap == null)
_SomethingsHeap = (SomethingsHeapList)SomeGlobalClass.GetSomethings(new SomethingsHeapList());
return _SomethingsHeap;
}
set
{
_SomethingsHeap = value;
}
}
/*при изменении таблицы БД, на которой строится эта коллекция, потребуется
обновление этой коллекции или ее элементов. если это не реализовано,
то потребуется перезапуск всего приложения, с тем, чтобы элементы коллекции
обновились.
*/

//******************* пример метода извлечения элемента (экземпляра класса
// Something) из произвольной коллекции
public static Something GetSomething(int somethingID,
CollectionBase collection)
{
// тут происходит обращение к CollectionManager, откуда возвращается
// экземпляр коллекции с id = somethingID
return (Something)CollectionManager.GetCollectionItem(somethingID,
"id", collection, typeof(Something));
// если элемента в коллекции нет, он добавляется в нее из конструктора
// сущности
}
//******************* извлечение коллекции Somethings и пример
// построения на ее базе двумерной коллекции Somethings
public static void CopySomethings()
{
foreach (Something something in SomeGlobalClass.Somethings)
{
int somethingID = something.SomethingID;
int ID2 = something.SecondID;
/* Тут следует пояснить, что в CollectionManager есть переопределенный
метод, который использует конструктор с двумя ID. Это нужно в случае,
если процедура на сервере собирает записи из разных источников данных.
Например, параметр SecondID определяет таблицу, данные из которой
объединяются в результирующий запрос. Тогда экземпляр в коллекции
ищется сразу по двум id (по сути, это двумерный массив с двумя ключами)
*/

Something sms2 = (Something)CollectionManager.GetCollectionItem(somethingID, "id", ID2, "SecondID",
SomeGlobalClass.Somethings2, typeof(Something));
}
}
/******************* первичное заполнение коллекций элемент ами ******************* Процедура выдергивает список ID, по которым ищутся или создаются экземляры класса Something (в данном случае метод универсален для всех коллекций Somethings, SomethingsHeap, Somethings2). Получив id, CollectionManager смотрит, есть ли в переданной ему коллекции экземпляр сущности с этим id. Если его там нет, вызывается конструктор сущности и элемент добавляется в коллекцию. */ public static CollectionBase GetSomethings(CollectionBase collection) { //в метод передается коллекция, которую надо заполнить. заполнение коллекции // производится из хранимой процедуры с параметрами Hashtable parameters = new Hashtable(); int SomeParameter = 1; //параметры при обращении к процедуре могут быть необязательны, равно //как параметров можно передавать неограниченное количество любого типа. if (SomeParameter == 1) parameters["@SomeParameter"] = SomeParameter; //тут происходит обращение к CollectionManager, откуда возвращается коллекция. return CollectionManager.GetCollection("GetSomething", "SomethingID", conStrAlias, collection, typeof(Something), parameters); } public static CollectionBase GetSomethings(int SomeKeyID) { SomeGlobalClass.Somethings = null; Hashtable parameters = new Hashtable(); parameters["@SomeKeyID"] = SomeKeyID; /* обратите вниманеи на последний параметр true в методе GetCollection. его присутствие обязывает передать в конструктор класса готовый reader, а в SQL процедуру передается параметр @ReturnAtOnce, и тогда процедура вернет весь набор записей за один проход со всеми полями сущности. если этот параметр опущен, то по умолчанию процедура вызывается один раз для получения списка ID экземпляров класса, а после, при создании конкретного экземпляра класса вызывается еще раз, уже с параметром SomethingID для получения всех полей экземпляра с заданным ID. */ return (SomethingsList)CollectionManager.GetCollection("GetSomethings", "SomethingID", conStrAlias, new SomethingsList(), typeof(Something), parameters, true); } // или такой вариант, если нам надо вернуть типизированный список public List<Something> GetSomethings() { return GetSomethings().OfType<Something>().ToList(); } /*В случае, если в БД источник данных изменился (например, обновилась таблица), обновляем соответствующий экземпляр коллекции. При этом этот экземпляр класса будет удален из коллекции, создан заново и вставлен туда, где был (сортировка по SomethingID). В методе UpdateCollectionItem если элемента в коллекции нет, он не добавляется(!) */ public static void OperateCollectionItem(int somethingID, CollectionBase collection) { CollectionManager.UpdateCollectionItem(somethingID, "id", collection, typeof(Something)); /* переопределенный метод использует конструктор с двумя ID (см. выше). В качестве второго ключа передаем поле SecondID класса Something, а в качестве параметра процедуры переменную SecondID. Например: */ int SecondID = 100; CollectionManager.UpdateCollectionItem(somethingID, "id", SecondID, "SecondID", collection, typeof(Something)); // в случае, если нам надо добавить новый экземпляр в одномерную // коллекцию c id = somethingID CollectionManager.AddCollectionItem(collection, typeof(Something), somethingID, somethingID, -1); // переопределенный метод использует конструктор с двумя ID. CollectionManager.AddCollectionItem(collection, typeof(Something), somethingID, SecondID, -1); } } //********* собственно CollectionManager ********* // **** Usefull ***** public static class CollectionManager { internal static object locker = new object(); public static CollectionBase GetCollection(string ProcName, string idFieldName, string conStrAlias, CollectionBase collection, Type collElementType, Hashtable parameters, bool FillAtOnce) { // для одного ID и FillAtOnce return GetCollection(ProcName, idFieldName, string.Empty, conStrAlias, collection, collElementType, parameters, FillAtOnce); } public static CollectionBase GetCollection(string ProcName, string idFieldName, string conStrAlias, CollectionBase collection, Type collElementType, Hashtable parameters) { // для одного ID return GetCollection(ProcName, idFieldName, string.Empty, conStrAlias, collection, collElementType, parameters, false); } public static CollectionBase GetCollection(string ProcName, string idFieldName, string idFieldName2, string conStrAlias, CollectionBase collection, Type collElementType, Hashtable parameters) { // для двух ID return GetCollection(ProcName, idFieldName, idFieldName2, conStrAlias, collection, collElementType, parameters, false); } public static CollectionBase GetCollection(string ProcName, string idFieldName1, string idFieldName2, string conStrAlias, CollectionBase collection, Type collElementType, Hashtable parameters, bool FillAtOnce) { // для двух ID и FillAtOnce //пример задания параметров для процедуры //Hashtable parameters = new Hashtable(); //parameters["string"] = "строка"; //parameters["int"] = 21; // ********************* !!! Внимание!!! Коллекция автоматически не очищается. Нужно следить за этим самостоятельно, передавая при необходимости new() try { using (SqlConnection connection = new SqlConnection(SQLManager.GetConnectionString(conStrAlias))) { using (SqlCommand command = new SqlCommand(ProcName, connection)) { command.CommandType = CommandType.StoredProcedure; foreach (DictionaryEntry de in parameters) { command.Parameters.Add(new SqlParameter(de.Key.ToString(), de.Value)); } if (FillAtOnce) //говорит процедуре вернуть все сразу command.Parameters.Add(new SqlParameter("@ReturnAtOnce", true)); connection.Open(); using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { if (idFieldName2 == string.Empty) { if (FillAtOnce) { //метод использует готовый reader для формирования экземпляра класса. //при этом хранимая процедура возвращает полный набор полей по всем строкам(один запрос на коллекцию) //в конструкторе класса процедура уже не вызывается для создания экземпляров. //подходит для статических табличных данных. CollectionManager.AddCollectionItem(collection, collElementType, reader); } else //в данном методе используется отдельный запрос для создания каждого экземпляра коллекции. //данный метод подходит для вытаскивания сложных запросов с вычисляемыми полями. CollectionManager.GetCollectionItem((int)reader[idFieldName1], "id", collection, collElementType); } else // для коллекций с 2-мя ключами (двумерный массив). такие коллекции строятся из разных источников со схожей структурой. // например, когда SQL возвращается с использованием инструкции Union. // первый параметр передает ID записи, второй - ID источника записей CollectionManager.GetCollectionItem((int)reader[idFieldName1], "id", Int32.Parse(reader[idFieldName2].ToString()), "id2", collection, collElementType); } reader.Close(); reader.Dispose(); } command.Dispose(); } connection.Close(); connection.Dispose(); } } catch (Exception ex) { } return collection; } public static void SortCollection(IList collection, Type collElement) { // Теперь, если базовый класс коллекции наследуется от класса Sortable, надо отсортировать коллекцию по SortID, // чтобы изменяемый экземпляр занял свое место в коллекции после его обновления (остался в той же строке коллекции). // При создании экземпляра класса и добавлении его в коллекцию (AddCollectionItem), свойству SortID в его базовом классе присваивается // текущий порядковый номер в коллекции, в порядке получения записей из БД или из другого источника. // При обновлении экземпляра в коллекции, после его модификации в БД, он удаляется из коллекции и создается вновь, // после чего снова добавляется в конец коллекции. При этом ему присваивается старый SortID и // после сортировки коллекции, обновленный экземпляр встает на свое место. if (collElement.BaseType == typeof(Sortable)) { EntityComparer Comp = new EntityComparer(); IComparer comparer = (IComparer)Comp; ArrayList al = ArrayList.Adapter(collection); al.Sort(comparer); } } public static void SortCollectionBack(IList collection) { ArrayList al = ArrayList.Adapter(collection); al.Reverse(0, collection.Count); } public static void AddCollectionItem(IList collection, Type collElement, SqlDataReader reader) { //тут попадаем на переопределенный конструктор класса, который заполняет элемент коллеции данными ридера object lstItem = Activator.CreateInstance(collElement, reader, collection.Count); collection.Add(lstItem); } public static object AddCollectionItem(IList collection, Type collElement, int iD1, int iD2) { return AddCollectionItem(collection, collElement, iD1, iD2, collection.Count); } public static object AddCollectionItem(IList collection, Type collElement, int iD1, int iD2, int SortID) { object lstItem = null; try { //элемент добавляется if (iD2 == -1) { // для одномерного массива // возможны случаи, когда коллекция состоит из элементов, общих для разных коллекций. // например, коллекции Collection_A и Collection_B содержат одни и те же элементы типа Coll_Elements. // при этом Collection_A и Collection_B хранятся в сессии и могут изменяться и обнуляться при действиях пользователя. // коллекция Coll_Elements содержит прототипы для Collection_A и Collection_B, которые хранятся // в уровне приложения, и не должна обнуляться в течении жизни приложения. // при обнулении списков Collection_A и Collection_B, мы попадаем на создание нового экземпляра // класса Coll_Elements, т.е. сюда. Нам надо вместо создания нового экземпляра забрать в Coll_Elements (в куче) уже существующий // экземпляр и подсунуть его в Collection_A или Collection_B. // если коллекция наследуется от Heap, прототипы ее экземпляров хранятся в куче if (collection.GetType().BaseType.BaseType.Name == "Heap") { switch (collElement.Name) { // В случае с типом Book дочерняя коллекция типа BooksList будет использовать // уже существующие экземпляры Book из кучи, т.е. из коллекции StateManager.BooksHeap case "Book": lstItem = GetCollectionItem(iD1, "id", StateManager.BooksHeap, typeof(Book)); break; } } if (lstItem == null) { if (collElement.BaseType == typeof(Sortable) || (collElement.BaseType.BaseType == typeof(Sortable))) lstItem = Activator.CreateInstance(collElement, iD1, SortID); else lstItem = Activator.CreateInstance(collElement, iD1); } } else { // для двумерного массива. работа с кучей пока не реализована. if (collElement.BaseType == typeof(Sortable)) lstItem = Activator.CreateInstance(collElement, iD1, iD2, SortID); else lstItem = Activator.CreateInstance(collElement, iD1, iD2); } collection.Add(lstItem); } catch (Exception ex) { } return lstItem; } public static object GetCollectionItem(int iD, string idFieldName, IList collection, Type collElement) { // по умолчанию используем коллекции с одним ключом. 2-й передаем как заглушку return GetCollectionItem(iD, idFieldName, -1, string.Empty, collection, collElement); } public static object GetCollectionItem(int iD1, string idFieldName1, int iD2, string idFieldName2, IList collection, Type collElement) { try { if (!(iD1 > 0)) return null; // если элемента в коллекции нет, он добавляется if (collection.Count > 0) { int itemIndex = GetItemIndex(iD1, idFieldName1, iD2, idFieldName2, collection, collElement); if (itemIndex >= 0) return collection[itemIndex]; } return AddCollectionItem(collection, collElement, iD1, iD2); } catch { } return null; } public static void UpdateCollectionItem(int iD1, string idFieldName, IList collection, Type collElement) { UpdateCollectionItem(iD1, idFieldName, -1, string.Empty, collection, collElement); } public static void UpdateCollectionItem(int iD1, string idFieldName1, int iD2, string idFieldName2, IList collection, Type collElement) { if (!(iD1 > 0)) return; // если элемента в коллекции нет, он не добавляется if (collection.Count > 0) { // itemIndex - порядковый номер элемента в коллекции int itemIndex = GetItemIndex(iD1, idFieldName1, iD2, idFieldName2, collection, collElement); if (i temIndex >= 0) { collection.Remove(collection[itemIndex]); if (idFieldName2 == string.Empty && iD2 == -1) { if (collElement.BaseType == typeof(Sortable)) AddCollectionItem(collection, collElement, iD1, -1, itemIndex); else AddCollectionItem(collection, collElement, iD1, -1); } else { if (collElement.BaseType == typeof(Sortable)) AddCollectionItem(collection, collElement, iD1, iD2, itemIndex); else AddCollectionItem(collection, collElement, iD1, iD2); } } } SortCollection(collection, collElement); } public static void RemoveCollectionItem(int iD1, string idFieldName, IList collection, Type collElement) { RemoveCollectionItem(iD1, idFieldName, -1, string.Empty, collection, collElement); } public static void RemoveCollectionItem(int iD1, string idFieldName1, int iD2, string idFieldName2, IList collection, Type collElement) { if (!(iD1 > 0)) return; // если элемента в коллекции нет, он не добавляется if (collection.Count > 0) { // itemIndex - порядковый номер элемента в коллекции int itemIndex = GetItemIndex(iD1, idFieldName1, iD2, idFieldName2, collection, collElement); if (itemIndex >= 0) collection.Remove(collection[itemIndex]); } } internal static int GetItemIndex(int iD1, string idFieldName1, int iD2, string idFieldName2, IList collection, Type collElement) { int index = -1; int fld1 = -1; int fld2 = -1; Type collElemType = collection[0].GetType(); System.Reflection.FieldInfo[] elemClassFields = collElemType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (idFieldName2 == string.Empty && iD2 == -1) { // одномерный массив for (int j = 0; j < elemClassFields.Length; j++) { if (elemClassFields[j].Name == idFieldName1) { fld1 = j; for (int i = 0; i <= collection.Count - 1; i++) { try { if ((int)elemClassFields[fld1].GetValue(collection[i]) == iD1) index = i; } catch { break; } } break; } } } else { // двумерный массив for (int j = 0; j < elemClassFields.Length; j++) { if (elemClassFields[j].Name == idFieldName1) // j - индекс имени поля первого ключа fld1 = j; } for (int j = 0; j < elemClassFields.Length; j++) { if (elemClassFields[j].Name == idFieldName2) // j - индекс имени поля второго ключа fld2 = j; } for (int i = 0; i <= collection.Count - 1; i++) { try { if ((int)elemClassFields[fld1].GetValue(collection[i]) == iD1 && (int)elemClassFields[fld2].GetValue(collection[i]) == iD2) index = i; } catch { break; } } } return index; } } // ************************************************************************************ // Пример реализации класса сущности // ************************************************************************************ // **** Sample ***** public class Book : Sortable { public int BookID { get { return id; } set { id = value; } } public int TextID { get; set; } public bool ForeignBook { get; set; } public double Rating { get; set; } public int Position { get; set; } public int PhotoID { get; set; } public DateTime DateCreation { get; set; } public DateTime Modified { get; set; } public int Count { get; set; } public int CountAll { get; set; } public string Caption { get; set; } public Guid CreatorID { get; set; } public bool Published { get; set; } public Book() { } public Book(IDataReader reader, int sortID) : base(reader, sortID) { } public Book(int id_key, int sortID) : base(id_key, sortID, "DbConnectionAlias", "GetBookDetails") { } public override void FillBody(IDataReader reader, int sortID) { try { base.FillBody(reader, sortID); Hashtable hashT = new Hashtable(); for (int i = 0; i < reader.FieldCount; i++) { if (!(reader[i] is DBNull)) hashT.Add(reader.GetName(i), reader[i]); } // набор актуальных для сущности полей определяется в процедуре BookID = !hashT.Contains("BookID") ? -1 : (int)reader["BookID"]; DateCreation = !hashT.Contains("DateCreation") ? DateTime.Now : (DateTime)reader["DateCreation"]; Caption = !hashT.Contains("Caption") ? string.Empty : (string)reader["Caption"]; Published = !hashT.Contains("Published") ? false : (bool)reader["Published"]; } catch (Exception ex) { } } } // ************************************************************************************ // Пример определения коллекций экземпляров сущности // ************************************************************************************ // **** Sample ***** public class UserBooksList : ListRealizator { public static UserBooksList GetInstance() { //return new UserBooksList(); return StateManager.UserBooks; } } // **** Sample ***** public class BooksList : Heap { public static BooksList instance = new BooksList(); //коллекция одна на всех юзеров public BooksList() { Hashtable parameters = new Hashtable(); instance = (BooksList)CollectionManager.GetCollection("GetBooks", "BookID", "DbConnectionAlias", this, typeof(Book), parameters); } public static BooksList GetInstance() { return instance; } } //********************************************************************** // Классы для инкапсуляции операций с SQL SERVER //********************************************************************** // **** Usefull ***** public class SortedOutputParameter { public object Value { get; set; } // значение public int OrderId { get; set; } // порядковый номер } // **** Usefull ***** public class SQLManager { public static string GetConnectionString(string alias) { if (StateManager.DbConnections == null) SQLManager.AddDbConnections(); if (!StateManager.DbConnections.Any()) SQLManager.AddDbConnections(); string connString = string.Empty; switch (alias) { case "CmsDb": connString = StateManager.DbConnections["CmsDb"].ConnectionString; break; case "GeoDb": connString = StateManager.DbConnections["GeoDb"].ConnectionString; break; } return connString; } public static void AddDbConnections() { //***************************** добавляем коннекшены в глоб.коллекцию ********************************************** // все коннекшены, прописанные в конфиге, добавляем в глобальные переменные. обращаться к ним будем по алиасу foreach (ConnectionStringSettings connStrSettings in StateManager.Config.ConnectionStrings.ConnectionStrings) { if (!StateManager.DbConnections.ContainsKey(connStrSettings.Name)) { Db_Connection db = new Db_Connection(); SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connStrSettings.ConnectionString); db.Alias = connStrSettings.Name; db.ConnectionString = builder.ConnectionString; db.DataSource = builder.DataSource; db.InitialCatalog = builder.InitialCatalog; db.UserID = builder.UserID; db.Password = builder.Password; StateManager.DbConnections.Add(connStrSettings.Name, db); } } } public static void ExecuteNonQuery(string conStrAlias, string ProcName, Hashtable parameters) { using (SqlConnection connection = new SqlConnection(SQLManager.GetConnectionString(conStrAlias))) { using (SqlCommand command = new SqlCommand(ProcName, connection)) { command.CommandType = CommandType.StoredProcedure; foreach (DictionaryEntry de in parameters) { command.Parameters.Add(new SqlParameter(de.Key.ToString(), de.Value)); } connection.Open(); command.ExecuteNonQuery(); command.Dispose(); } connection.Close(); connection.Dispose(); } } public static object SQLExecuteScalar(string conStrAlias, string ProcedureName, Hashtable inputParams) { // вызов без выходных параметров. возвращает одно значение object[] obj = SQLManager.SQLExecuteScalar(conStrAlias, ProcedureName, inputParams, null); return obj[0]; } public static object[] SQLExecuteScalar(string conStrAlias, string ProcedureName, Hashtable inputParams, Hashtable outputParams) { // вызов процедуры с выходными параметрами. возвращает несколько значений в этих параметрах (через массив rets) object[] rets; if (outputParams != null) rets = new object[outputParams.Count]; else rets = new object[1]; using (SqlConnection connection = new SqlConnection(SQLManager.GetConnectionString(conStrAlias))) { using (SqlCommand command = new SqlCommand(ProcedureName, connection)) { try { connection.Open(); command.CommandType = CommandType.StoredProcedure; foreach (DictionaryEntry de in inputParams) { command.Parameters.Add(new SqlParameter(de.Key.ToString(), de.Value)); } if (outputParams != null) { foreach (DictionaryEntry de in outputParams) { // поскольку Hashtable обычным образом не сортируется, применяем класс-надстройку с сортировкой элементов по OrderID. // Иначе при работе в девелоперской версии происходит обратный порядок присвоения значений выходным параметрам, // а при работе через IIS прямой. Соответственно, переменные меняются местами. // Чтобы этого избежать, обращаемся к выходным параметрам метода через вспомогательный класс SortedOutputParameter, // содержащий сам SQL параметр и свойство для сортировки! SortedOutputParameter sop = (SortedOutputParameter)de.Value; if (sop.Value.GetType() == typeof(XmlDocument)) { // для случая, когда в параметрах передается XML command.Parameters.Add(new SqlParameter(de.Key.ToString(), SqlDbType.Xml, 2)); command.Parameters[de.Key.ToString()].Direction = ParameterDirection.Output; } } command.ExecuteScalar(); // выводим результат из заполненных в процедуре outputParams в массив rets foreach (DictionaryEntry de in outputParams) { SortedOutputParameter sop = (SortedOutputParameter)de.Value; rets[sop.OrderId] = command.Parameters[de.Key.ToString()].Value; } } else { // для случая без выходных параметров возвращаем одно значение object result = command.ExecuteScalar(); if (result != null)
rets[0] = result;
}
}
catch (Exception ex)
{
command.Dispose();
connection.Close();
connection.Dispose();
}
command.Dispose();
}
connection.Close();
connection.Dispose();
return rets;
}
}
}
public sealed class StateManager : Singleton<StateManager>
{
private static Dictionary<string, Db_Connection> _DbConnections;
public static Dictionary<string, Db_Connection> DbConnections
{
get
{
return _DbConnections ?? (_DbConnections = new Dictionary<string, Db_Connection>());
}
set
{
_DbConnections = value;
}
}

private static BooksList _BooksHeap;
public static BooksList BooksHeap
{
get
{
return _BooksHeap ?? (_BooksHeap = new BooksList());
}
set
{
_BooksHeap = value;
}
}
private static UserBooksList _UserBooks;
public static UserBooksList UserBooks
{
get
{
return _UserBooks ?? (_UserBooks = new UserBooksList());
}
set
{
_UserBooks = value;
}
}
private static Configuration _config;
public static Configuration Config
{
get
{
if (_config == null)
{
string localConfigPath = ConfigurationManager.AppSettings["LocalConfigPath"].ToString();
ExeConfigurationFileMap map = new ExeConfigurationFileMap { ExeConfigFilename = localConfigPath };
_config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
}
return _config;
}
set
{
_config = value;
}
}
}
public class Singleton<TClass> where TClass : class, new()
{
internal static TClass _instance;
protected Singleton()
{
_init();
}
protected virtual void _init()
{
}
internal static class Creator
{
internal static TClass GetInstance()
{
return new TClass();
}
}
public static TClass Instance
{
get { return _instance ?? (_instance = Creator.GetInstance()); }
}
}
public class Db_Connection
{
public string Alias { get; set; }
public string ConnectionString { get; set; }
public string DataSource { get; set; }
public string InitialCatalog { get; set; }
public string UserID { get; set; }
public string Password { get; set; }
public string Timeout { get; set; }
public Db_Connection(string alias, string connectionString)
{
Alias = alias;
ConnectionString = connectionString;
}
public Db_Connection()
{
}
}
}
/*
Вот собственно и все, что хотелось сказать по этому поводу.
Описанный здесь подход успешно используется на сайте prozarium.ru,
на сайте CityGuide(probki.net), в программе PGMania, а также в ряде других коммерческих проектов.
*/