Статьи

Введение в ADO.NET

Заинтересованы в .NET? Не пропустите .NET Feature Guide — это отличный ресурс!

Появление ASP в конце 1996 года, когда был выпущен ADO 1.0, представляло собой новый способ динамического извлечения данных из базы данных. Хотя ADO тогда находился в зачаточном состоянии и был чем-то вроде ответвления DAO и RDO, тем не менее он представлял новое смелое направление. Каждая последующая версия технологии приближает нас к ADO 2.6, когда разработка подошла к концу. В этот момент из тени возникла самая революционная на сегодняшний день структура — .NET, с ее очень мощным и зрелым ADO.NET.

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

ADO, для своего времени, безусловно, был успешным. Хотя переиздания технологии не ознаменовали собой кардинальных изменений для ADO, она достигла того, что должна была сделать в своей собственной архитектуре. Тем не менее, его постоянно мучили проблемы, которые окружали его обработку отключенных хранилищ данных и правильное и точное функционирование с XML. Вот где вступил ADO.NET — это две вещи, которыми легко управляет новая технология, так как XML является основным компонентом всей .NET Framework! ADO.NET был просто создан, чтобы исправить все недостатки, обнаруженные в ADO, и предоставить разработчикам возможность достичь большего с меньшими затратами.

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

Пространства имен

При использовании классического ASP (или других методов доступа к базе данных) вам необходимо сначала подключиться к вашему хранилищу данных, прежде чем вы сможете даже начать задумываться о получении данных. Однако в ADO.NET есть два основных объекта, которые позволяют разработчикам изначально работать с данными: DataReader и DataSet. Мы скоро узнаем все о них.

На любой странице доступа к данным .NET, прежде чем подключаться к базе данных, сначала необходимо импортировать все необходимые пространства имен, которые позволят вам работать с необходимыми объектами. Поскольку мы собираемся работать с SQL Server, мы сначала импортируем нужные нам пространства имен. Пространства имен в .NET — это просто аккуратный и упорядоченный способ организации объектов, так что ничто не становится двусмысленным.

<%@ Import Namespace="System" %>  <%@ Import Namespace="System.Data" %>  <%@ Import Namespace="System.Data.SqlClient" %> 

Примечание. Если бы мы использовали базу данных, SQLClient от SQL, например MS Access, мы бы заменили SQLClient на OleDb . Если мы используем Oracle, .NET v 1.1 предоставляет System.Data.OracleClient имен System.Data.OracleClient , а для любого источника данных ODBC — System.Data.Odbc имен System.Data.Odbc . Вы найдете подробную информацию обо всех доступных методах и объектах, которые мы обсудим в документации .NET SDK Framework.

Связь

После того, как мы импортируем все необходимые пространства имен, мы готовы подключиться к нашей базе данных. Теперь, независимо от того, реализуете ли вы DataReader или Dataset, ваше первоначальное соединение с базой данных будет таким:

 SqlConnection objConnect = new SqlConnection (Your Connection String);   objConnect.Open(); 

Выше мы настроили наш объект SQLConnection Connection с нашей информацией о соединении с базой данных, а затем открыли его. Ниже перечислены распространенные методы объекта соединения, с которыми мы могли бы работать:

  • Open — открывает соединение с нашей базой данных.
  • Close — закрывает соединение с базой данных
  • Dispose — освобождает ресурсы объекта подключения. Используется для принудительного сбора мусора, обеспечивая отсутствие удержания ресурсов после использования нашего соединения. Между прочим, используя метод Dispose, вы также автоматически вызываете метод Close.
  • State — сообщает вам, в каком состоянии соединения находится ваш объект, часто используется для проверки того, использует ли ваше соединение какие-либо ресурсы. Ex. if (ConnectionObject.State == ConnectionState.Open)

Что касается открытия соединения с базой данных, это действительно так. Теперь мы должны решить, какой объект использовать для достижения конечных результатов, которые вы хотите представить. Теперь нам нужно выбрать, работать ли с Datareader или Dataset. Давайте начнем с рассмотрения DataReader.

DataReader

В классическом ASP, когда нам нужен был метод для извлечения данных, мы использовали соответствующий объект данных и устанавливали его курсоры для выполнения поставленной задачи. Если бы мы хотели быстрое чтение данных только вперед, мы бы установили CursorType нашего Recordset на adOpenForwardOnly, а его LockType на adLockReadOnly (часто называемый «пожарным шлангом»). Что ж, с .NET все, что вам нужно, это DataReader, который предлагает множество функций, с помощью которых вы можете еще больше повысить эффективность его вывода.

Методы объекта Command

Теперь, когда мы знаем, что делает DataReader, существует множество методов, которые можно использовать для достижения ваших конкретных целей. Вот несколько методов, с которыми DataReader работает через объект Command. Все четыре метода Execute относятся к действию, выполняемому объектом Command, а остальные методы используются для улучшения собственной функциональности объекта Command.

  • ExecuteReader — просто выполняет SQL-запрос к базе данных, используя метод Read() для перемещения по данным, как показано ниже
  • ExecuteNonQuery — используется всякий раз, когда вы работаете с хранимыми процедурами SQL с параметрами, как показано в разделе «Хранимые процедуры» ниже.
  • ExecuteScalar — возвращает молниеносное одиночное значение как объект из вашей базы данных Ex. object val = Command.ExecuteScalar(); Then check if != null. Ex. object val = Command.ExecuteScalar(); Then check if != null.
  • ExecuteXmlReader — выполняет SQL-запрос только к SQL Server, возвращая объект XmlReader . См. Документацию .NET для получения дополнительной информации
  • Prepare — эквивалент ADO Command.Prepared = True свойство. Полезно при кэшировании команды SQL, чтобы она выполнялась быстрее при вызове более одного раза. Ex. Command.Prepare();
  • Dispose — освобождает ресурсы объекта Command. Используется для принудительного сбора мусора, обеспечивая отсутствие удержания ресурсов после использования нашего соединения. Между прочим, используя метод Dispose вы также автоматически вызываете метод Close объекта Connection.

Таким образом, после того, как мы установим наше первоначальное соединение с базой данных, все, что нам нужно сделать для извлечения данных с помощью DataReader, — это использовать объект Command для запроса открытой базы данных. Метод Command.ExecuteReader, используемый через объект Command, создает наш DataReader.

 SqlCommand objCommand = new SqlCommand(Sql String, objConnect);   SqlDataReader objDataReader = objCommand.ExecuteReader(); 

Если вы не заметили, вышеуказанный объект подключения явно установлен для SQL Server. Для других, таких как Access, вы бы использовали объект OleDbConnection, который легко размещается путем замены части SQL в SqlCommand на OleDB, то есть OleDbCommand objCommand . Это относится и к SqlDataReader (который должен быть изменен на OleDbDataReader ). Эта взаимозаменяемость работает в среде .NET, так что имейте это в виду.

Получив наш объект DataReader, мы настраиваем метод objDataReader["Name"].ToString() Read() в цикле while и перемещаемся и отображаем результаты наших данных по позиции индекса, а не objDataReader["Name"].ToString() . Я предпочитаю этот метод, так как он немного быстрее, при условии, что вы можете запомнить имена столбцов!

 while (objDataReader.Read() == true) {   Response.Write (objDataReader[0].ToString() + "<BR>");   } 

Методы чтения данных

Вот несколько распространенных методов, доступных для Datareader:

  • Read — перемещает указатель записи в первую строку, что позволяет считывать данные по имени столбца или позиции индекса. Можно проверить наличие данных с условным, if (DataReader.Read() = true)
  • HasRows — Новое только с .NET v1.1. HasRows проверяет, существуют ли какие-либо данные, и используется вместо метода Read. Ex. if (DataReader.HasRows Ex. if (DataReader.HasRows ).
  • IsClosed — метод, который может определить, является ли DataReader закрытым. Ex. if (DataReader.IsClosed == false)
  • NextResult — Эквивалентен методу ADR NextRecordset, где пакет операторов SQL выполняется с помощью этого метода перед переходом к следующему набору результатов данных. Как и в случае с только что перечисленным циклом, вы можете добавить DataReader.NextResult() после первого цикла в нескольких операторах SQL, а затем начать новый цикл для следующего набора.
  • Close — закрывает DataReader

После всего, что сказано и сделано, всегда хорошая практика программирования — закрывать, освобождать и очищать все соединения и ресурсы:

 objDataReader.Close();  objDataReader = null;  objCommand.Dispose();  objCommand = null;  objConnect.Close();  objConnect= null; 

Поведение команды объекта DataReader

Но это не все! Объект DataReader также имеет несколько классных необязательных командных поведений, которые вы должны использовать всякий раз, когда можете, для дополнительной масштабируемости и повышения производительности вашего приложения. Они могут быть реализованы в объекте DataReader. Например:

 objCommand.ExecuteReader(CommandBehavior.CloseConnection);  or objCommand.ExecuteReader(CommandBehavior.SingleResult); 

Поведение:

  • SingleResult — аналогично ExecuteScalar, возвращает одно значение
  • SingleRow — как это звучит, возвращает нам одну строку
  • SequentialAccess — это очень эффективный метод упорядоченного доступа к данным, который можно использовать двумя способами. Во-первых, он используется с методами GetReader для DataReader, где вы можете возвращать результаты в последовательном порядке из базы данных, используя порядковые позиции столбцов. Это лучше (с точки зрения скорости) использовать objDataReader["Column1"].ToString() или objDataReader[0].ToString() методы. И, во-вторых, многие доступные методы чтения данных, такие как objDataReader.GetString(0) или objDataReader.GetInt32(0) , позволяют обойти любое начальное преобразование данных. Однако для его работы должны присутствовать ненулевые данные, и весь поиск данных должен осуществляться в последовательном порядке, в зависимости от вашего запроса SQL.
  • CloseConnectionCloseConnection DataReader и его объект Connection после прочтения данных.

Завершенный DataReader

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

 <%@ Page Language="C#" Debug="False" Explicit="True"%>  <%@ Import Namespace="System.Data" %>  <%@ Import Namespace="System.Data.SqlClient" %>   <html>  <head>  <title>ADO.NET</title>  </head>  <body>   <script runat="server" language="C#">   void Page_Load (Object Source, EventArgs E) {     SqlConnection objConnect = new SqlConnection  ("server=(local);uid=sa;pwd=;database=Northwind;");        objConnect.Open();     SqlCommand objCommand = new SqlCommand("Select * from  Suppliers Order by ContactName Asc", objConnect);    objCommand.Prepare(); SqlDataReader objDataReader =  objCommand.ExecuteReader CommandBehavior.CloseConnection);     while (objDataReader.Read() == true) {        Response.Write (objDataReader  [2].ToString() + "<BR>");    }     objDataReader.Close();        objDataReader = null;        objCommand.Dispose();        objCommand = null;        objConnect.Close();        objConnect= null;   }   </script>  </body>  </html> 

В приведенном выше примере мы настраиваем нашу страницу и импортируем наши необходимые пространства имен, которые позволяют нам работать с нашими данными (имейте в виду, что SqlClient чувствителен к регистру в C #). Затем мы помещаем наш код для чтения данных между тегами на стороне сервера, которые подключаются к нашей базе данных SQL, и, в свою очередь, отображаем наши результаты. После этого мы закрываем все наши объекты.

Хранимые процедуры

Сам Datareader, как и DataSet, более чем способен работать с хранимыми процедурами; они предлагают множество преимуществ производительности и возможность консолидации множества операций в одном месте. Тем не менее, подход набора данных к хранимым процедурам имеет тенденцию становиться немного затянутым, так как он тесно связан с моделью манипулирования данными, которую предлагает набор данных. Что касается быстрой и простой обработки хранимых процедур, то методы Command Object объекта DataReader более чем достаточны, как мы сейчас увидим.

Мы начнем с создания @txt хранимой процедуры, которая принимает один параметр — @txt , который передается в мой запрос.

 CREATE PROCEDURE ShowSuppliers (  @txt varchar(50)  )  AS  Select CompanyName, City, Country from Suppliers Where  Country like "%" + @txt + "%" 

Если вы хотите просто выполнить хранимую процедуру, передать ей значение, а затем прочитать его, как бы вы это сделали? Легко! Перед вызовом метода ExecuteReader замените строку SQL в конструкторе SqlCommand именем хранимой процедуры, а затем укажите свойству CommandType объекта Command Object, что вы работаете с хранимой процедурой. После того, как вы это сделаете, реализуйте коллекцию Parameters (через переменную Param SqlParameter), чтобы установить значение, которое вы хотите передать своей хранимой процедуре, например:

 SqlCommand objCommand = new SqlCommand("ShowSuppliers",  objConnect);  objCommand.CommandType = CommandType.StoredProcedure;   SqlDataReader objDataReader = objCommand.ExecuteReader  (CommandBehavior.CloseConnection);   SqlParameter Param = objCommand.Parameters.Add("@txt",  SqlDbType.VarChar, 50);  Param.Value = "US";   // ... Get Data 

Затем отобразите свои данные, используя методы DataReader, например, пример кода цикла while, приведенный в последнем разделе, или пример предстоящего DataGrid.

Хранимые процедуры — и альтернативный метод

Альтернативный метод работы с хранимыми процедурами в этом контексте — метод Command.ExecuteNonQuery() . Это полезно при работе с более многогранными хранимыми процедурами, которые имеют входные, выходные и возвращаемые значения. Использование их не намного сложнее; просто реализуйте коллекцию параметров, показанную ниже, то есть Param.Direction = ParameterDirection.Input или. OutPut or .ReturnValue; и применить. Value = , независимо от типа значения:

 CREATE PROCEDURE ShowSuppliers (  @txt varchar(50),  @Name varchar (50) output,  @Company varchar (50) output,  @Country varchar (50) output  )  AS  Select @Name = ContactName, @Company = CompanyName,  @Country = Country from Suppliers Where Country like "%" + @txt + "%"  GO 

Здесь мы создали немного более сложную хранимую процедуру, чтобы проиллюстрировать метод ExecuteQuery. Как видите, он не только содержит нашу начальную переменную @txt слова — @txt , но теперь мы можем получить несколько выходных значений.

 // ... Database Connection / Command here like above   SqlParameter Param = objCommand.Parameters.Add("@txt",  SqlDbType.VarChar, 50);  Param.Direction = ParameterDirection.Input;  Param.Value = "US";   Param = objCommand.Parameters.Add("@Name", SqlDbType.VarChar,50);  Param.Direction = ParameterDirection.Output;   Param = objCommand.Parameters.Add("@Company", SqlDbType.VarChar,50);  Param.Direction = ParameterDirection.Output;   Param = objCommand.Parameters.Add("@Country", SqlDbType.VarChar,50);  Param.Direction = ParameterDirection.Output;   objCommand.ExecuteNonQuery();   Response.Write (objCommand.Parameters["@Name"]  .Value.ToString() + "<BR>");  Response.Write (objCommand.Parameters["@Company"]  .Value.ToString() + "<BR>");  Response.Write (objCommand.Parameters["@Country"]  .Value.ToString() + "<BR>"); 

Единственное, что здесь отличается от нашего последнего примера Sproc, это то, что мы только что добавили новое направление параметров, чтобы отразить значения, которые мы хотим получить. В свою очередь, мы отвечаем. Запишите их так, как показано здесь. Для получения дополнительной информации об этом и работе с хранимыми процедурами с возвращаемыми значениями см. Документацию .NET.

Но теперь давайте представим, что вы хотите отобразить данные в сетке данных с помощью средства чтения данных, а не с помощью цикла while и форматирования таблицы с помощью HTML. Но в этом случае вам не требуются какие-либо общие свойства DataGrid, такие как подкачка страниц. Как это будет достигнуто? Аналогично коду примера DataReader в конце последнего раздела. Все остается прежним, за исключением того, что после запуска метода ExecuteReader () вы просто привязываете источник данных вашей DataGrid к Datareader, как показано.

 MyDataGrid.DataSource = objDataReader;   MyDataGrid.DataBind(); 

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

Помимо привязки нашего Datagrid, также легко связать другие серверные элементы управления. Например, чтобы связать элемент управления DropDownList, вы, очевидно, измените свой элемент управления DataGrid на DropDownList, хотя методы Datasource и Binding одинаковы. Единственное отличие зависит от используемого вами элемента управления, поскольку все они имеют свои уникальные свойства, которые можно назначать. Как сделать так, чтобы наш DropDownList отображал результаты наших запросов? Сразу после вашего метода ExecuteReader укажите его DataTextField — отображаемое значение, а DataValueField — значение, которое будет передано при DataValueField элемента. Вот оно:

 // ... our Data Access was here. Then we assign  our new control's properties   MyDropList.DataSource = objDataReader;   MyDropList.DataTextField = "Country";  MyDropList.DataValueField = "Country";   MyDropList.DataBind();   //And our control placed in our runat="server"  form tags on our page   <ASP:DropDownList id="MyDropList" runat="server" /> 

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

Набор данных

DataSet можно рассматривать как контейнер хранения в памяти для всех ваших данных. Это дает нам гораздо больше возможностей и возможностей для манипулирования и представления наших данных, вплоть до XML. Здесь DataAdapter выступает в качестве промежуточного объекта, ответственного за соединение нашей базы данных с нашим DataSet, как объект Command с DataReader.

В отличие от объекта DataReader, DataAdapter / DataSet не только дает вам возможность работать с отключенными хранилищами данных, но также может использоваться для общих функций редактирования, таких как вставка, удаление и обновление ваших данных, и даже настройка транзакционной логики.

Работа с DataSet

В этом примере мы продемонстрируем общий конечный результат при работе с DataAdapter и DataSet: серверный элемент управления DataGrid для единоличного представления данных.

Мы уже знаем, как открыть соединение с нашей базой данных. Однако, поскольку мы собираемся разобраться с нашим DataSet, порядок событий на этом этапе меняется. Вспомните, как мы использовали объект Command для запроса базы данных? При работе с наборами данных мы будем использовать DataAdapter для выполнения запросов и метод Fill() для заполнения нашего DataSet результатами:

 SqlDataAdapter objDataAdapter = new SqlDataAdapter ("Select  CompanyName, ContactName, City, Country, Region from  Suppliers", objConnect);   DataSet objDS = new DataSet();   objDataAdapter.Fill (objDS); 

Что тут происходит? Итак, мы используем SqlDataAdapter для выполнения оператора SQL, и после этого действия он возвращает данные для заполнения нашего DataSet. Как только наш объект DataSet создан, он, в свою очередь, предоставляет объект Datatable, а также коллекцию строк и столбцов, которые мы рассмотрим позже. Между прочим, вы могли бы, если захотите, дать имя вновь создаваемому Datatable следующим образом: objDataAdapter.Fill (objDS,"MyTable"); Примечание. Для базы данных OleDbDataAdapter используйте OleDbDataAdapter .

Наиболее распространенным методом предоставления данных при создании автономного хранилища данных является метод Fill() , о котором мы упоминали, и который мы продемонстрируем через мгновение. На данный момент вы можете быть удивлены, почему DataReader был исследован в таких деталях. Здесь мы сосредоточены на доступе к данным и их представлении, и DataReader чаще используется для этих целей. DataSet, который имеет примерно эквивалентное количество методов, больше ориентирован на управление данными. Мы на мгновение разобрались с его методом Fill() , поскольку именно это позволило нам легко получать данные. Дополнительная методология DataSet будет включать сложные методы для манипулирования данными. Я хотел бы направить вас к документации .NET на этом этапе, так как мы не будем раскрывать эту информацию здесь.

Элемент управления DataGrid в .NET, вероятно, является наиболее часто используемым элементом управления, поскольку он предлагает разбиение по страницам, фильтрацию и сортировку. Это является причиной общих отношений DataSet / Datagrid. Datareader, будучи объектом пересылки / только для чтения, не может поддерживать эти условия, если вы не умело запрограммируете его на это.

Отображение данных

Теперь, когда мы обсудили выбранные методы поиска данных, нам нужно посмотреть, как мы их отобразим. Мы можем сделать это, привязав наш DataSet к DataGrid. Мы присваиваем свойство DataGrid DataSource нашему DataSet, а затем связываем его, чтобы просмотреть наши результаты:

 MyDataGrid.DataSource = objDS  MyDataGrid.DataBind(); 

Мы узнали, что сам DataSet способен хранить несколько таблиц данных, каждая из которых является DataTable. С этого момента вы можете дополнительно манипулировать своими данными, прежде чем связывать их с объектом DataTable, как мы покажем далее.

Более того, используя DataView, мы можем создавать различные представления в DataTable нашего набора данных, что позволяет нам фильтровать или сортировать данные. Для этого вы должны присвоить свойству DataGrid свойство Datasource для DataView, а затем связать его.

 DataView objView = objDS.Tables[0].DefaultView;   objView.RowFilter = "Country like '%US%'"; 

или

 objView.Sort = "Country asc";   MyDataGrid.DataSource = objView;   MyDataGrid.DataBind(); 

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

Также имейте в виду, что на вашей .NET-странице все серверные элементы управления размещаются в runat="server" . Вот весь код:

 <script runat="server" language="C#">   void Page_Load(Object Source,EventArgs E) {   SqlConnection objConnect = new SqlConnection  ("server=(local);uid=sa;pwd=;database=Northwind;");   objConnect.Open();   SqlDataAdapter objDataAdapter = new SqlDataAdapter  ("Select CompanyName, ContactName, City, Country, Region from  Suppliers", objConnect);   DataSet objDS = new DataSet();   objDataAdapter.Fill (objDS);   MyDataGrid.DataSource = objDS;   //DataView objView = objDS.Tables[0].DefaultView;  //objView.RowFilter = "Country like '%US%'";  //MyDataGrid.DataSource = objView;   MyDataGrid.DataBind();   objDataAdapter.Dispose();  objDataAdapter = null;  objDS.Dispose();  objDS = null;  objConnect.Close();  objConnect = null;   }   </script>   <form runat="server">  <ASP:DataGrid id="MyDataGrid" runat="server" />  </form> 

Вспомните в последнем разделе, как я быстро проиллюстрировал привязку к DropDownList? То же самое можно применить к DataSet, за исключением того, что в этом случае он будет происходить после того, как будет выполнен метод Fill (в отличие от ExecuteReader), и каждому из них будет назначен отдельный источник данных.

Прежде чем мы закончим, давайте на минутку вернемся к нашей DataTable. Если вам нужен больший контроль над вашими данными, вы можете настроить DataTable из своего набора данных и проходить по нему, как вы делали с DataReader. Следовательно, после использования метода Fill() описанного выше, вы можете перебрать свои DataTable DataRows примерно так:

 DataTable dsReader = objDS.Tables[0];  foreach(DataRow row in dsReader.Rows) {   Response.Write (row[0] + "<BR>");   } 
Вывод

Таким образом, этот вводный урок должен был разжечь ваш аппетит, и, надеюсь, побудил вас сразу же приступить к созданию приложения или двух. Кроме того, обязательно ознакомьтесь с документацией .NET и учебными пособиями по QuickStart для получения дополнительной информации обо всем, что мы рассмотрели, а также о том, что мы упомянули в процессе (включая чтение XML с помощью DataReader, реализацию транзакций и вставку, редактирование и обновление ваших данных с помощью DataSet).

Теперь вы должны понимать простоту доступа к базе данных и представления данных, которые доступны в ADO.NET, и иметь представление о дальнейших направлениях, которые могут принимать эти объекты, особенно DataSet. Я надеюсь, что теперь у вас есть более четкое представление о возможностях .NET с более чем достаточной информацией, которая поможет вам начать работу.

До следующего раза, счастливого .NETing!