Открыть Электронные книги
Категории
Открыть Аудиокниги
Категории
Открыть Журналы
Категории
Открыть Документы
Категории
C# 9 и платформа
.NET 5
ОСНОВНЫЕ ПРИНЦИПЫ И ПРАКТИКИ ПРОГРАММИРОВАНИЯ
10- Е ИЗДАНИЕ
v\ АЦДЛЕКПНЛСА Apress*
www.dialektika.com www.apress.com
Язык программирования
С# 9
и платформа NET 5: .
ОСНОВНЫЕ ПРИНЦИПЫ И ПРАКТИКИ
ПРОГРАММИРОВАНИЯ
10-е издание
Pro C# 9 with .NET 5
Tenth Edition
Andrew Troelsen
Philip Japikse
Apress
Язык программирования
С# 9
и платформа NET 5: .
ОСНОВНЫЕ ПРИНЦИПЫ И ПРАКТИКИ
ПРОГРАММИРОВАНИЯ
10-е издание
лпт
Эндрю Троелсен
Филипп Джепикс
КиУв
Комп'ютерне видавництво
"Д 1 АЛЕКТИКА"
2022
УДК 004.432
Т70
Глава 31. Создание приложений MVC с помощью ASP . NET Core 555
Введение в представления ASP. NET Core 555
Экземпляры класса ViewResult и методы действий 555
Механизм визуализации и синтаксис Razor 558
Представления 561
Компоновки 565
Частичные представления 566
Обновление компоновки с использованием частичных представлений 567
Отправка данных представлениям 568
Вспомогательные функции дескрипторов 570
Включение вспомогательных функций дескрипторов 570
Вспомогательная функция дескриптора для формы 576
Вспомогательная функция дескриптора для действия формы 577
Вспомогательная функция дескриптора для якоря 578
Вспомогательная функция дескриптора для элемента ввода 578
Вспомогательная функция дескриптора для текстовой области 579
Вспомогательная функция дескриптора для элемента выбора 579
Вспомогательные функции дескрипторов для проверки достоверности 580
Вспомогательная функция дескриптора для среды 581
Содержание 17
Вспомогательная функция дескриптора для ссылки 582
Вспомогательная функция дескриптора для сценария 582
Вспомогательная функция дескриптора для изображения 584
Специальные вспомогательные функции дескрипторов 584
Подготовительные шаги 584
Создание базового класса 585
Вспомогательная функция дескриптора для вывода сведений об элементе 586
Вспомогательная функция дескриптора для удаления элемента 587
Вспомогательная функция дескриптора для редактирования
сведений об элементе 588
Вспомогательная функция дескриптора для создания элемента 588
Вспомогательная функция дескриптора для вывода списка элементов 589
Обеспечение видимости специальных вспомогательных
функций дескрипторов 590
Вспомогательные функции HTML 590
Вспомогательная функция DisplayFor() 591
Вспомогательная функция DisplayForModel() 591
Вспомогательные функции EditorFor ( ) и EditorForModel() 591
Управление библиотеками клиентской стороны 591
Установка диспетчера библиотек как глобального инструмента . NET Core 592
Добавление в проект AutoLot.Mvc библиотек клиентской стороны 592
Завершение работы над представлениями CarsController и Cars 596
Класс CarsController 596
Частичное представление списка автомобилей 597
Представление Index 598
Представление ВуМаке 599
Представление Details 600
Представление Create 602
Представление Edit 604
Представление Delete 606
Компоненты представлений 607
Код серверной стороны 608
Построение частичного представления 609
Вызов компонентов представлений 610
Вызов компонентов представлений как специальных
вспомогательных функций дескрипторов 610
Обновление меню 610
Пакетирование и минификация 611
Пакетирование 611
Минификация 611
Решение WebOptimizer 611
Шаблон параметров в ASP. NET Core 613
Добавление информации об автодилере 613
Создание оболочки службы 615
Обновление конфигурации приложения 615
Создание класса ApiServiceSettings 616
18 Содержание
Файловый ввод-вывод
и сериализация объектов
При создании настольных приложений возможность сохранения информации
между пользовательскими сеансами является привычным делом. В настоящей главе
рассматривается несколько тем , касающихся ввода -вывода , с точки зрения платфор-
мы . NET Core . Первая задача связана с исследованием основных типов, определенных
в пространстве имен System . 10 , с помощью которых можно программно модифи-
цировать структуру каталогов и файлов. Вторая задача предусматривает изучение
разнообразных способов чтения и записи символьных , двоичных , строковых и нахо-
дящихся в памяти структур данных.
После изучения способов манипулирования файлами и каталогами с использовани-
ем основных типов ввода -вывода вы ознакомитесь со связанной темой — сериализаци-
ей объектов. Сериализацию объектов можно применять для сохранения и извлечения
состояния объекта с помощью любого типа , производного от System . 10 . Stream.
На заметку! Чтобы можно было успешно выполнять примеры в главе, IDE-среда Visual Studio
должна быть запущена с правами администратора ( для этого нужно просто щелкнуть пра-
вой кнопкой мыши на значке Visual Studio и выбрать в контекстном меню пункт Запуск от
имени администратора). В противном случае при доступе к файловой системе компью-
тера могут возникать исключения, связанные с безопасностью.
.
Таблица 20.3 Основные члены типа Directorylnfo
Член Описание
Create() Создает каталог ( или набор подкаталогов) по заданному
CreateSubdirectory() путевому имени
Delete() Удаляет каталог и все его содержимое
GetDirectories() Возвращает массив объектов Directorylnfo, которые
представляют все подкаталоги в текущем каталоге
GetFiles() Извлекает массив объектов Filelnfo, представляющий
набор файлов в заданном каталоге
MoveTo() Перемещает каталог со всем содержимым в новый путь
Parent Извлекает родительский каталог данного каталога
Root Получает корневую часть пути
}
}
Name: М:\
Type: Network
Free space: 4711871942656
Format: NTFS
Label: DigitalMedia
К этому моменту вы изучили несколько основных линий поведения классов
Directory, Directorylnfо и Drivelnfo. Далее вы ознакомитесь с тем, как созда-
вать, открывать, закрывать и удалять файлы, находящиеся в заданном каталоге.
Метод Filelnfo.Create()
Следующий набор примеров находится в проекте консольного приложения по име -
ни Simple F i l e 10 . Один из способов создания дескриптора файла предусматривает
применение метода F i l e l n f o . Create ( ) :
using System;
using System.10;
Console.WriteLine( ** ***** Simple 10 with the File Type *****\n") ;
// Измените это на папку на своей машине , к которой вы имеете доступ
// по чтению/записи или запускайте приложение от имени администратора ,
var fileName = $@"С { Path.VolumeSeparatorChar}
{Path.DirectorySeparatorChar}temp{Path.DirectorySeparatorChar}Test.dat";
// Создать новый файл на диске С:.
Filelnfo f = new Filelnfo(fileName);
FileStream fs = f.Create();
// Использовать объект FileStream...
// Закрыть файловый поток.
fs.Close();
На заметку! Почти все примеры в этой главе содержат операторы using. Можно также ис -
пользовать новый синтаксис объявлений using, но было решено придерживаться опера-
торов using, чтобы сосредоточить примеры на исследуемых компонентах System.10.
30 Часть VI . Работа с файлами , сериализация объектов и доступ к данным
Метод Filelnfо.Open()
С помощью метода FileInfo.Open ( ) можно открывать существующие файлы , а
также создавать новые файлы с более высокой точностью представления , чем обеспе-
чивает метод Filelnfo.Create ( ) , поскольку Open ( ) обычно принимает несколько
параметров для описания общей структуры файла , с которым будет производиться
работа. В результате вызова Open ( ) возвращается объект FileStream. Взгляните на
следующий код:
var fileName =
$@"С {Path.VolumeSeparatorChar}{Path.DirectorySeparatorChar}Test.dat";
—
Наконец, третий параметр метода Open() значение перечисления FileShare —
указывает, каким образом файл может совместно использоваться другими файловыми
дескрипторами:
public enum FileShare
{
None,
Read,
Write ,
ReadWrite,
Delete ,
Inheritable
}
Метод Filelnfо.OpenText()
Еще одним членом типа Filelnfo, связанным с открытием файлов, является
OpenText(). В отличие от Create(), Open(), OpenRead() и OpenWrite() метод
OpenText() возвращает экземпляр типа StreamReader, а не FileStream. Исходя
из того, что на диске С : имеется файл по имени boot.ini, вот как получить доступ
к его содержимому:
var fileName =
$@"С {Path.VolumeSeparatorChar}{Path.DirectorySeparatorCharJTest.dat";
Методы Filelnfo.CreateText()
и Filelnfo.AppendText()
Последними двумя методами, представляющими интерес в данный момент, явля-
ются CreateText() и AppendText(). Оба они возвращают объект StreamWriter:
var fileName =
$@"С {Path. VolumeSeparatorChar}{Path.DirectorySeparatorChar}Test.dat";
Приведенные в табл . 20.6 методы типа File можно использовать для реали-
зации чтения и записи пакетов данных посредством всего нескольких строк кода.
Еще лучше то, что эти методы автоматически закрывают лежащий в основе файло-
вый дескриптор. Например, следующий проект консольного приложения (по имени
SimpleFilelO)сохраняет строковые данные в новом файле на диске С : ( и читает их
в память) с минимальными усилиями (здесь предполагается , что было импортировано
пространство имен System.10):
Console.WriteLine( '» * * * * * Simple I/O with the File Type \n");
string[] myTasks = {
"Fix bathroom sink", "Call Dave",
"Call Mom and Dad", "Play Xbox One"};
// Записать все данные в файл на диске С:.
File.WriteAllLines(@"tasks.txt", myTasks);
// Прочитать все данные и вывести на консоль.
foreach (string task in File.ReadAllLines(@"tasks.txt"))
{
Console.WriteLine("TODO: {0}", task);
}
Console.ReadLine() ;
File.Delete("tasks.txt");
.
Таблица 20.10 Основные члены BinaryWriter
Член Описание
BaseStream Это свойство только для чтения обеспечивает доступ к лежащему
в основе потоку, используемому с объектом BinaryWriter
Close ( ) Этот метод закрывает двоичный поток
Flush ( ) Этот метод выталкивает буфер двоичного потока
Seek ( ) Этот метод устанавливает позицию в текущем потоке
Write ( ) Этот метод записывает значение в текущий поток
42 Часть VI . Работа с файлами , сериализация объектов и доступ к данным
Добавьте еще один файл класса по имени Car.cs и приведите его содержимое к
такому виду:
using System;
using System.Text.Json.Serialization;
48 Часть VI . Работа с файлами , сериализация объектов и доступ к данным
using System.Xml ;
using System.Xml.Serialization;
namespace SimpleSerialize
{
public class Car
{
public Radio TheRadio = new Radio();
public bool IsHatchBack;
public override string ToStringO
=> $"IsHatchback:{IsHatchBack} Radio:{TheRadio.ToString()}";
}
}
Затем добавьте очередной файл класса по имени JamesBondCar.cs и поместите
в него следующий код:
using System;
using System.Text.Json.Serialization;
using System.Xml ;
using System.Xml.Serialization;
namespace SimpleSerialize
{
public class JamesBondCar : Car
{
public bool CanFly;
public bool CanSubmerge;
public override string ToStringO
=> $"CanFly:{CanFly}, CanSubmerge:{CanSubmerge} {base.ToString()}";
}
}
Ниже показан код финального файла класса Person.cs:
using System;
using System.Text.Json.Serialization ;
using System.Xml;
using System.Xml.Serialization;
namespace SimpleSerialize
{
public class Person
{
// Открытое поле.
public bool IsAlive = true;
// Закрытое поле ,
private int PersonAge = 21;
// Открытое свойство/закрытые данные ,
_
private string fName = string.Empty;
public string FirstName
{
_
get { return fName; }
set { fName = value; }
}
public override string ToStringO =>
$"IsAlive:{IsAlive} FirstName:{FirstName} Age:{PersonAge}";
}
}
Глава 20. Файловый ввод - вывод и сериализация объектов 49
В заключение модифицируйте содержимое файла Program , cs, добавив следую-
щий стартовый код:
using System.Collections.Generic;
using System.10;
using System.Text.Json;
using System.Text.Json.Serialization ;
using System.Xml;
using System.Xml.Serialization;
using SimpleSerialize;
Console.WriteLine(!» * * ** Fun with Object Serialization * \n ");
// Создать объект JamesBondCar и установить состояние.
JamesBondCar jbc = new()
{
CanFly = true ,
CanSubmerge = false ,
TheRadio = new()
{
StationPresets = new() {89.3, 105.1, 97.1},
HasTweeters = true
}
};
Person p = new()
{
FirstName = "James" ,
IsAlive = true
};
Итак , все готово для того, чтобы приступить к исследованию сериализации XML
и JSON.
.
Атрибут NET Core Описание
[XmlAttribute] Этот атрибут .NET Core можно применять к полю или свойству для
сообщения XmlSerializer о необходимости сериализировать
данные как атрибут XML (а не как подэлемент)
[XmlElement] Поле или свойство будет сериализировано как элемент XML,
именованный по вашему выбору
[XmlEnum] Этот атрибут предоставляет имя элемента члена перечисления
[XmlRoot] Этот атрибут управляет тем, как будет сконструирован корневой
элемент ( пространство имен и имя элемента)
[XmlText] Свойство или поле будет сериализировано как текст XML ( т.е. со-
держимое , находящееся между начальным и конечным дескрипто -
рами корневого элемента )
[XmlType] Этот атрибут предоставляет имя и пространство имен типа XML
На заметку! Класс XmlSerializer требует, чтобы все сериализируемые типы в графе объ-
ектов поддерживали стандартный конструктор ( поэтому обязательно добавьте его обрат -
но, если вы определяете специальные конструкторы ).
}
Вот как будет выглядеть результирующий XML-документ (обратите внимание на
открывающий элемент < JamesBondCar > ):
<?xml version="1.0 ii и и ?>
<JamesBondCar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
CanFly="true" CanSubmerge="false" xmlns="http://www.MyCompany.com">
</JamesBondCar>
Исследуйте содержимое файла PersonData.xml:
<?xml version=" l.0"?>
<Person xmlns:xsi="http://www.w3.org /2001/XMLSchema -instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<IsAlive> true</IsAlive >
< FirstName>James</FirstName>
</Person>
Важно отметить, что свойство PersonAge не сериализируется в XML. Это под-
тверждает, что сериализация XML учитывает только открытые свойства и поля.
52 Часть VI. Работа с файлами , сериализация объектов и доступ к данным
Включение полей
Включить открытые поля в генерируемые данные JSON можно двумя способами.
Первый способ предусматривает использование класса JsonSerializerOptions
для сообщения JsonSerializer о необходимости включить все поля. Второй способ
предполагает модификацию классов за счет добавления атрибута [ Jsonlnclude] к
каждому открытому полю, которое должно быть включено в вывод JSON. Обратите
внимание, что при первом способе (применение JsonSerializationOptions)будут
включаться все открытые поля в графе объектов. Чтобы исключить отдельные откры-
тые поля с использованием такого приема, вам придется использовать для этих полей
атрибут JsonExclude.
Модифицируйте метод SaveAsJsonFormat ( ) , как показано ниже:
static void SaveAsJsonFormat<T>(T objGraph , string fileName)
{
var options = new JsonSerializerOptions
{
IncludeFields = true,
};
File.WriteAllText(fileName,
System.Text.Json.JsonSerializer.Serialize(objGraph, options));
}
// Car.cs
public class Car
{
[Jsonlnclude]
public Radio TheRadio = new Radio();
[Jsonlnclude]
public bool IsHatchBack ;
// JamesBondCar.es
public class JamesBondCar : Car
{
Глава 20. Файловый ввод - вывод и сериализация объектов 55
[XmlAttribute]
[Jsonlnclude]
public bool CanFly;
[XmlAttribute]
[Jsonlnclude]
public bool CanSubmerge;
}
// Person.cs
public class Person
{
// Открытое поле.
[Jsonlnclude]
public bool IsAlive = true;
Теперь в результате запуска кода любым способом все открытые свойства и поля
записываются в файл. Однако , заглянув содержимое файла , вы увидите , что данные
JSON были записаны в минифицированном виде , т.е . в формате , в котором все незна-
чащие пробельные символы и разрывы строк удаляются. Формат является стандар-
тным во многом из-за широкого использования JSON для служб REST и уменьшения
размера пакета данных при передаче информации между службами по HTTP / HTTPS.
На заметку! Поля для сериализации JSON обрабатываются точно так же , как для десериа-
лизации JSON. Если вы выбирали вариант включения полей при сериализации JSON, то
также должны делать это при десериализации JSON.
"HasTweeters": true ,
"HasSubWoofers": false,
"StationPresets": [
89.3,
105.1,
97.1
],
"Radiold": "XF-552RR6"
},
"IsHatchBack": false
}
NumberHandling = JsonNumberHandling.AllowReadingFromString
& JsonNumberHandling.WriteAsString
};
При таком изменении данные JSON , созданные для класса Саг , будут выглядеть
так:
{
"canFly": true,
"canSubmerge": false ,
"theRadio": {
"hasTweeters": true ,
"hasSubWoofers": false,
"stationPresets": [
"89.3",
"105.I й ,
"97.1"
],
"radiold": "XF-552RR6"
),
"isHatchBack": false
}
JamesBondCar savedJsonCar =
ReadAsJsonFormat <JamesBondCar>(options, "CarData.json");
Console.WriteLine("Read Car: {0}", savedJsonCar.ToString());
List <JamesBondCar> savedJsonCars =
ReadAsJsonFormat <List <JamesBondCar>>(options,
"CarCollection.json");
Console.WriteLine("Read Car: {0}", savedJsonCar.ToString());
Резюме
Глава начиналась с демонстрации использования типов D i r e c t o r y
(Directorylnfo) и File(Filelnfo). Вы узнали, что эти классы позволяют мани-
пулировать физическими файлами и каталогами на жестком диске. Затем вы озна-
комились с несколькими классами, производными от абстрактного класса Stream.
Поскольку производные от Stream типы оперируют с низкоуровневым потоком бай-
тов, пространство имен System.10 предлагает многочисленные типы средств чте-
ния / записи ( например, StreamWriter, StringWriter и BinaryWriter), которые
упрощают процесс. Попутно вы взглянули на функциональность типа DriveType,
научились наблюдать за файлами с применением типа FileSystemWatcher и выяс-
нили , каким образом взаимодействовать с потоками в асинхронной манере.
В главе также рассматривались службы сериализации объектов. Вы видели, что
платформа . NET Core использует граф объектов, чтобы учесть полный набор связан-
ных объектов , которые должны сохраняться в потоке. В заключение вы поработали с
сериализацией и десериализацией XML и JSON.
ГЛАВА
Доступ к данным
с помощью ADO. NET
На заметку! Когда речь идет об объекте подключения в ADO.NET, то на самом деле имеется
в виду специфичный тип, производный от DbConnection; не существует класса с бук -
вальным именем “Connection”. Та же идея остается справедливой в отношении объекта
команды, объекта адаптера данных и т. д. По соглашению об именовании объекты в кон-
кретном поставщике данных снабжаются префиксом в форме названия связанной СУБД
( например, SqlConnection, SqlCommand и SqlDataReader).
Клиентская
сборка
Объект Command Команда вставки
Коллекция параметров
Команда обновления
с:
t
База данных
L
Рис. 21.1. Поставщики данных ADO.NET предоставляют доступ к конкретным СУБД
IDbCommand CreateCommand();
void Open();
void Dispose();
}
namespace MyConnectionFactory
{
// Пакет OleDb предназначен только для Windows и в .NET Core
// н е поддерживается ,
enum DataProviderEnum
{
SqlServer,
# if PC
OleDb,
# endif
Odbc,
None
}
}
Если на своей машине обработки вы работаете в среде Windows, тогда модифици-
руйте файл проекта, чтобы определить символ условной компиляции PC:
<PropertyGroup>
<DefineConstants>PC</DefineConstants>
</PropertyGroup>
В случае использования Visual Studio щелкните правой кнопкой мыши на имени
проекта и выберите в контекстном меню пункт Properties (Свойства) . В открывшемся
диалоговом окне Properties (Свойства) перейдите на вкладку Build (Сборка) и введите
нужное значение в поле Conditional compiler symbols (Символы условной компиляции).
Следующий пример кода позволяет выбирать специфический объект подключения
на основе значения из специального перечисления. В целях диагностики мы просто
выводим лежащий в основе объект подключения с применением служб рефлексии.
using System;
using System.Data ;
using System.Data.Odbc;
# if PC
using System.Data.OleDb;
# endif
using Microsoft.Data.SqlClient;
using MyConnectionFactory;
Console.WriteLine( »» **** Very Simple Connection Factory \n");
Setup(DataProviderEnum.SqlServer);
# if PC
Setup(DataProviderEnum.OleDb); // H e поддерживается в macOS.
#endif
Setup(DataProviderEnum.Odbc);
Setup(DataProviderEnum.None);
Console.ReadLine();
void Setup(DataProviderEnum provider)
{
// Получить конкретное подключение.
IDbConnection myConnection = GetConnection(provider);
Console.WriteLine($"Your connection is a {myConnection?.GetType().Name
?? "unrecognized type"}");
// Открыть, использовать и закрыть подключение...
}
Глава 21. Доступ к данным с помощью ADO . NET 71
// Этот метод возвращает конкретный объект подключения
// н а основе значения перечисления DataProvider.
IDbConnection GetConnection(DataProviderEnum dataProvider)
= > dataProvider switch
{
DataProviderEnum.SqlServer => new SqlConnection(),
# if PC
/ / H e поддерживается в macOS.
DataProviderEnum.OleDb = > new OleDbConnection(),
# endif
DataProviderEnum.Odbc => new OdbcConnection(),
=> null,
};
На заметку! Если ваша машина для разработки функционирует под управлением Windows
и вы установили Visual Studio 2019, то уже имеете установленный экземпляр SQL Server
Express (под названием localdb ) , который можно использовать для всех примеров в на-
стоящей книге. В случае согласия работать с указанной версией можете сразу переходить
в раздел " Установка IDE-среды SQL Server ”.
На заметку! Выбранный вариант контейнера (Windows или Linux ) — это ОС, функционирую -
щая внутри контейнера, а не ОС, установленная на рабочей станции.
docker о * в
.
О sea Sort by V
• Docker running
В £ Wfkomt X Ш -
P Preview О
Resources
History
Connection
§= Clear List
Connection Details
User name sa
Password
Q Remember password
Database < Default > V/
Advanced-
Connect Cancel
Connection
2= Clear List
Connection Details
Server (localdb)\mssqllocaldb
User name
Password
Remember password
Advanced -
Connect Cancel
На заметку! GitHub по умолчанию игнорирует файлы с расширением bak. Прежде чем вос -
станавливать базу данных, вам придется переименовать расширение Ьа в bak.
• A tail-log backup of the source database will be taken View this setting on the Options page
Select a page Д Script * © Help
> General
> Options
Files Source
> О Database
(S) Device /var /opt/mssql/backup/AutoLotDocker bak
Database AutoLot
Destination
Database
’ДНИ!
Restore to: . .
The last backup taken ( Sunday December 20 2020 07.57.10) Timeline
Restore plan
Backup sets to restore
Restore Name Component Type Server Database Position First LSN L
0 -
AutoLot Fuli Database Backup Database Full afUb9a 1b 78 AutoLot 1 37000000171200001 3
Connection
VT W 33 |sa]
Progress
OK Cancel Help