Атрибуты сущностных классов

80

»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ

Сущностные классы определяются атрибутами и свойствами атрибутов, которые отображают такой класс на таблицу базы данных, а его свойства на столбцы таблицы базы данных. Атрибуты определяют наличие отображения, а свойства атрибутов указывают, как оно осуществляется. Например, есть атрибут Table, определяющий, что класс отображается на таблицу, но есть также его свойство Name, указывающее имя таблицы базы данных, на которую отображается класс.

Нет лучшего способа разобраться в атрибутах, их свойствах и в том, как они работают, чем изучить атрибуты, сгенерированные экспертами. Поэтому давайте проанализируем сущностный объект Customer, сгенерированный SQLMetal.

Ниже приведена часть сущностного класса Customer:

[Table(Name="dbo.Customers")]
	public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged
	{
        ...
        [Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
		public string CustomerID
		{
			get
			{
				return this._CustomerID;
			}
			set
			{
				if ((this._CustomerID != value))
				{
					this.OnCustomerIDChanging(value);
					this.SendPropertyChanging();
					this._CustomerID = value;
					this.SendPropertyChanged("CustomerID");
					this.OnCustomerIDChanged();
				}
			}
		}
        
        ...
        [Association(Name="FK_Orders_Customers", Storage="_Orders", OtherKey="CustomerID", DeleteRule="NO ACTION")]
		public EntitySet<Order> Orders
		{
			get
			{
				return this._Orders;
			}
			set
			{
				this._Orders.Assign(value);
			}
		}
        
        ...

Для краткости все части сущностного класса опущены за исключением тех, что содержат атрибуты LINQ to SQL. Также не показаны все излишние атрибуты.

А вот часть, содержащая хранимую процедуру и определенную пользователем функцию:

		[Function(Name="dbo.Get Customer And Orders")]
		[ResultType(typeof(GetCustomerAndOrdersResult1))]
		[ResultType(typeof(GetCustomerAndOrdersResult2))]
		public IMultipleResults GetCustomerAndOrders(
              [Parameter(Name="CustomerID", DbType="NChar(5)")] string customerID)
		{
			...
		}
		
		[Function(Name="dbo.MinUnitPriceByCategory", IsComposable=true)]
		[return: Parameter(DbType="Money")]
		public System.Nullable<decimal> MinUnitPriceByCategory(
              [Parameter(DbType="Int")] System.Nullable<int> categoryID)
		{
			...
		}

В предыдущих фрагментах кода атрибуты выделены полужирным. Эти фрагменты кода приведены для обеспечения контекста для дискуссии об атрибутах.

Database

Атрибут Database указывает для класса-наследника DataContext имя по умолчанию отображенной базы данных, если это имя не задано в строке подключения при создании экземпляра DataContext. Если атрибут Database не указан и база данных не приведена в строке подключения, то имя класса-наследника DataContext предполагается совпадающим с именем базы данных, к которой выполняется подключение.

Таким образом, для ясности: порядок приоритетов в определении имени базы данных, от высшего к низшему, выглядит следующим образом:

Ниже показана существенная часть сгенерированного SQLMetal класса-наследника DataContext по имени Northwind:

// Из класса Northwind, сгенерированного SQLMetal

public partial class Northwind : System.Data.iinq.DataContext 
{

Как видите, атрибут Database не задан в сгенерированном классе Northwind, производном от DataContext. Поскольку класс был сгенерирован Microsoft, можно предположить, что это сделано намеренно. Если вы собираетесь указывать атрибут Database и хотите, чтобы по умолчанию использовалась база данных по имени NorthwindTest, код должен выглядеть следующим образом:

[Database(Name="NorthwindTest")]
public partial class Northwind : System.Data.Linq.DataContext 
{

Нет никаких причин избегать атрибута Database. Возможно, это потому, что если указать базу данных в строке соединения, то она переопределит имя класса DataContext и атрибут Database. Возможно, разработчики из Microsoft думали, что если имя базы данных в строке соединения не задано, то будет использоваться имя производного от DataContext класса, что вполне удовлетворительно.

Идею подключения сгенерированного класса-наследника DataContext к базе данных по умолчанию нельзя считать удачной. В этом случае может быть случайно запущено приложение, которое не было однозначно сконфигурировано, и оно подключится к базе данных по умолчанию. Возникает возможность для очень болезненной ошибки, которая рано или поздно случится. Фактически можно согласиться лишь с указанием атрибута Database с намеренно нелепым именем, чтобы предотвратить подключение к базе данных по умолчанию. Возможно, это должно выглядеть примерно так, как показано ниже:

[Database(Name=" goopeygobezileywag ")]
public partial class Northwind : System.Data.Linq.DataContext
{

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

Name (типа string)

Свойство Name атрибута Database — это строка, указывающая имя базы данных, с которой нужно устанавливать соединение, если имя базы не задано явно в информации подключения при создании экземпляра класса-наследника DataContext. Если свойство атрибута Name опущено, а также не указано имя базы данных в информации подключения, тогда имя класса-наследника DataContext служит именем базы данных, к которой необходимо подключиться.

Table

Атрибут Table задает, в какой таблице базы данных будут сохраняться экземпляры сущностного класса. Имя сущностного класса не обязательно должно совпадать с именем таблицы. Ниже приведена часть сущностного класса, содержащая этот атрибут:

[Table(Name="dbo.Customers")]
	public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged
{
        ...

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

В рассматриваемом примере, поскольку при использовании SQLMetal для генерации сущностных классов Northwind была задана опция множественного числа, имя таблицы базы данных — Customers — преобразуется в форму единственного числа — Customer, которая и является именем класса. Поскольку имя класса не совпадает с именем таблицы базы данных, атрибут Name должен быть указан.

Свойство Name атрибута Table - строка, определяющая имя таблицы, на которую отображается данный сущностный класс. Если свойство атрибута Name не задано, то имя сущностного класса по умолчанию будет отображено на одноименную с ним таблицу базы данных.

Column

Атрибут Column указывает на то, что свойство сущностного класса отображается на поле базы данных. Ниже показана иллюстрирующая его часть сущностного класса:

[Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
public string CustomerID
{ ...

В рассматриваемом примере, поскольку указан атрибут Storage, LINQ to SQL сможет непосредственно обращаться к переменной-члену _CustomerID, минуя общедоступное средство доступа CustomerID. Если свойство атрибута Storage не задано, будет использовано средство доступа. Это может быть удобно для обхода специальной логики, которая может быть зашита в общедоступные средства доступа.

Тип базы данных для этого поля определен в атрибуте DbType как NCHAR длиной в пять символов. Поскольку атрибут CanBeNull указан со значением false, значение этого поля в базе данных не может быть NULL, а поскольку присутствует атрибут IsPrimaryKey со значением true, это поле представляет собой в таблице столбец идентичности.

Необязательно каждое свойство сущностного класса отображать на базу данных. Могут существовать свойства времени выполнения, которые не должны сохраняться в базе данных, и это вполне нормально. Для таких свойств просто не указывается атрибут Column.

Также могут быть хранимые столбцы, доступные только для чтения. Это обеспечивается отображением столбца и указанием свойства атрибута Storage на приватную переменную-член, без реализации метода set для свойства класса. DataContext может иметь доступ к приватному члену, но поскольку у данного свойства сущностного класса нет метода set, никто не может его изменять.

Ниже перечислены все свойства атрибута Column:

AutoSync

Свойство атрибута AutoSync — это перечислимый тип AutoSync, которое инструктирует исполняющую систему о необходимости извлекать значение отображаемого столбца сразу после операции вставки или обновления записи в базе данных. Попробуйте догадаться, какое именно значение используется по умолчанию. Согласно документации Microsoft, поведение по умолчанию — Never.

Установка атрибута свойства переопределяется, когда или свойство IsDbGenerated, или свойство IsVersion установлено в true.

CanBeNull

Булевское свойство атрибута CanBeNull определяет допустимость значения NULL для отображаемого столбца базы данных. По умолчанию это свойство атрибута равно true.

DbType

Свойство атрибута DbType представляет собой строку, определяющую тип столбца базы данных, на который отображается данное свойство сущностного класса. Если свойство DbType не указано, то тип столбца будет выведен из типа данных свойства сущностного класса. Это свойство атрибута используется для определения столбца только в случае вызова метода CreateDatabase.

Expression

Свойство атрибута Expression представляет собой строку, определяющую вычисляемый столбец базы данных. Он используется только в случае вызова метода CreateDatabase. По умолчанию имеет значение String.Empty.

IsDbGenerated

Булевское свойство атрибута IsDbGenerated указывает на то, что столбец таблицы базы данных, на который отображается свойство класса, генерируется автоматически базой данных. Если первичный ключ задан со свойством атрибута IsDbGenerated, равным true, то свойство атрибута DbType должно быть установлено в IDENTITY.

Свойство класса, у которого значение свойства атрибута IsDbGenerated установлено в true, немедленно синхронизируется после того, как запись вставлена в базу данных, независимо от установки атрибута AutoSync, и синхронизированное значение свойства класса будет видимым в свойстве класса после успешного завершения метода SubmitChanges. Значение этого атрибута по умолчанию — false.

IsDiecriminator

Свойство IsDiscriminator атрибута хранит булевское значение, указывающее что отображаемое свойство сущностного класса — это такое свойство, которое хранит значение дискриминатора для наследования сущностного класса. По умолчанию это свойство атрибута равно false.

IsPrimaryKey

Свойство IsPrimaryKey атрибута хранит булевское значение, указывающее на то, что столбец базы данных, на который отображается данное свойство класса, является частью первичного ключа.

В рассматриваемом случае все отображенные столбцы базы являются частями составного первичного ключа. Чтобы сущностный объект был обновляемым, по крайней мере, одно свойство сущностного класса должно иметь свойство атрибута IsPrimaryKey, установленное в true. В противном случае сущностный объект, отображенный на эту таблицу, будет доступен только для чтения. По умолчанию это свойство атрибута равно false.

IsVersion

Свойство IsVersion атрибута хранит булевское значение, указывающее, что отображенный столбец базы данных является либо номером версии, либо временной меткой, которая представляет информацию о версии для записи. За счет установки свойства атрибута IsVersion в true отображенный столбец базы данных будет инкрементирован, если это номер версии, и обновлен, если это временная метка, при всяком обновлении записи таблицы базы данных.

Свойство класса, атрибут IsVersion которого установлен в true, будет немедленно синхронизирован после вставки записи либо после ее обновления, независимо от установки атрибута AutoSync, и синхронизированное значение свойства класса будет видимо в свойстве класса немедленно после успешного завершения метода SubmitChanges. По умолчанию это свойство атрибута равно false.

Name

Свойство Name атрибута Column — это строка, указывающая имя столбца таблицы, на который отображается это свойство класса. Если свойство Name атрибута опущено, то имя свойства класса будет отображено по умолчанию на одноименный столбец базы данных.

Storage

Свойство Storage атрибута — это строка, указывающая приватную переменную-член, в которой хранится значение свойства сущностного класса. Это позволяет LINQ to SQL миновать общедоступное средство доступа свойства и содержащуюся в нем бизнес-логику, и позволяет получить непосредственный доступ к приватной переменной-члену. Если свойство Storage атрибута опущено, то по умолчанию будет использовано общедоступное средство доступа свойства.

UpdateCheck

Свойство атрибута UpdateCheck — это перечислимый тип (enum) UpdateCheck, который управляет поведением определения оптимистического обнаружения параллелизма для свойства класса и отображаемого на него столбца базы данных, если ни одно из отображаемых свойств сущностного класса не имеет свойства атрибута IsVersion, установленного в true. Три допустимых значения: UpdateCheck.Always, UpdateCheck.WhenChanged и UpdateCheck.Never.

Если ни одно из свойств сущностного класса не имеет свойства атрибута IsVersion, установленного в true, то значение свойства UpdateCheck атрибута по умолчанию будет Always.

Association

Атрибут Association используется для определения отношений между двумя таблицами, таких как отношение первичного и внешнего ключа. В этом контексте сущность чья отображенная таблица имеет первичный ключ, является родительской, а сущность чья отображенная таблица содержит внешний ключ — дочерней. Рассмотрим важные части двух сущностных классов, содержащих ассоциацию:

// Ассоциация от родительского (Customer) сущностного класса
[Association(Name="FK_Orders_Customers", Storage="_Orders", OtherKey="CustomerID", DeleteRule="NO ACTION")]
public EntitySet<Order> Orders
{
   ...
}

// Ассоциация от дочернего (Order) сущностного класса
[Association(Name="FK_Orders_Customers", Storage="_Customer", ThisKey="CustomerID", IsForeignKey=true)]
public Customer Customer
{
   ...
}

В этом обсуждении атрибута Association и его свойств используется сущностный класс Customer в качестве примера родительского элемента и сущностный класс Order в качестве примера дочернего элемента. Таким образом, будут представлены важные атрибуты Association, которые существуют в обоих сущностных классах — Customer и Order.

Если говорить о свойствах атрибута Association, то некоторые его свойства относятся к классу, в котором имеется атрибут Association, а другие свойства — к другому ассоциированному сущностному классу.

В этом контексте класс, в котором существует атрибут Association, можно назвать исходным классом, а другой ассоциированный сущностный класс — целевым классом. Поэтому, если речь идет о свойствах атрибута Association, указанного в сущностном классе Customer, то сущностный класс Customer является исходным классом, а сущностный класс Order — целевым классом. Если говорится о свойствах атрибута Association, указанных в классе Order, то исходным классом выступает Order, а целевым Customer.

Атрибут Association определяет, что исходный сущностный класс Customer имеет отношение с целевым сущностным классом Order.

В предшествующих примерах свойство Name атрибута указано для определения имени отношения. Значение этого свойства атрибута соответствует имени ограничения внешнего ключа в базе данных и будет использовано для создания этого ограничения при вызове метода CreateDatabase. Свойство Storage атрибута также присутствует. Оно позволяет LINQ to SQL обойти общедоступное средство доступа (accessor), чтобы получить доступ к значению свойства сущностного класса.

В варианте ассоциации первичного ключа к внешнему сущностный класс, являющийся родителем в отношении, будет хранить ссылку на дочерний сущностный класс в коллекции EntitySet<T>, поскольку дочерних классов может быть много. Дочерний сущностный класс будет хранить ссылку на родительский сущностный класс в EntityRef<Т>, поскольку он только один.

Ниже описаны свойства атрибута Association:

DeleteOnNull

Свойство DeleteOnNull атрибута принимает булевское значение, которое, будучи установленным в true, указывает что сущностный объект с дочерней стороны ассоциации должен быть удален в случае, если ссылка на него у родителя устанавливается.

Значение этого свойства атрибута определяется SQLMetal, если в базе данных задано правило удаления "CASCADE" для ограничения внешнего ключа и столбец внешнего ключа не допускает null.

DeleteRule

Свойство Delete.Rule атрибута принимает строку, которая указывает правило удаления (Delete Rule) для ограничения внешнего ключа. Оно используется только LINQ to SQL - когда создается ограничение в базе данных методом CreateDatabase.

Допустимые значения: "NO ACTION", "CASCADE", "SET NULL" и "SET DEFAULT". Определение всех этих значений ищите в документации по SQL Server.

IsForeignKey

Свойство IsForeignKey атрибута хранит булевское значение, которое, будучи установленным в true, говорит о том, что сущностный класс исходной стороны ассоциации содержит внешний ключ, т.е. является дочерней стороной этой ассоциации. По умолчанию это свойство атрибута установлено в false.

В примерах атрибута Association, показанных ранее для сущностных классов Customer и Order, поскольку данный атрибут указанный в сущностном классе Order, содержит свойство IsForeignKey атрибута, установленное в true, класс Order является дочерним в данном отношении.

IsUnique

Свойство IsUnique атрибута хранит булевское значение, которое, будучи установленным в true, указывает наличие ограничения уникальности на внешнем ключе, т.е. задает отношение "один к одному" между двумя сущностными классами. По умолчанию это свойство атрибута установлено в false.

Name

Свойство Name атрибута Association — это строка, указывающая имя ограничения внешнего ключа. Она используется для создания ограничения внешнего ключа при вызове метода CreateDatabase. Также оно будет применяться для различия нескольких отношений между одними и теми же сущностями. В этом случае, если обе стороны отношения определяют имя, оно должно быть одинаковым.

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

OtherKey

Свойство OtherKey атрибута Association — это строка, хранящая разделенный запятыми список всех свойств целевого сущностного класса, составляющих ключ, будь то внешний или первичный, в зависимости от того, какую сторону отношения занимает целевая сущность. Если это свойство атрибута не указано, то по умолчанию используются члены первичного ключа целевого сущностного класса.

Важно понимать, что атрибут Association, указанный на каждой стороне отношения ассоциации — Customer и Order — определяет, где находятся ключи каждой из сторон. Атрибут Association, заданный в сущностном классе Customer, говорит о том, какие свойства сущностных классов Customer и Order составляют ключ для отношения.

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

А на дочерней стороне свойство OtherKey атрибута не обязано указываться, т.к. по умолчанию применяется первичный ключ родителя. Таким образом, часто можно видеть свойство OtherKey определенным только на родительской стороне, а свойство ThisKey атрибута — только на дочерней стороне. Но благодаря значениям по умолчанию, и родительский, и дочерний элементы знают ключи на обеих сторонах.

Storage

Свойство Storage атрибута Association — это строка, указывающая приватную переменную-член, в которой хранится значение свойства сущностного класса. Это позволяет LINQ to SQL миновать общедоступное средство доступа и непосредственно обратиться к приватной переменной-члену, пропуская любую бизнес-логику из средств доступа. Если свойство Storage атрибута не задано, то по умолчанию используются общедоступные средства доступа данного свойства.

В Microsoft рекомендуют, чтобы оба члена отношения ассоциации были свойствами сущностного класса с отдельными переменными-членами для хранения данных с указанными свойствами Storage атрибута отношения.

ThisKey

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

Поскольку в ранее приведенном примере атрибута Association для сущностного класса Order имеется свойство IsForeignKey, известно, что сущностный класс Customer представляет родительскую сторону отношения — ту, что содержит первичный ключ. Поскольку атрибут Association не указывает свойства ThisKey, мы знаем, что значение первичного ключа таблицы Customer становится внешним ключом в ассоциированной таблице Orders.

Так как атрибут Association, показанный ранее для сущностного класса Order, определяет атрибут IsForeignKey со значением true, известно, что таблица Orders будет стороной ассоциации, содержащей внешний ключ. И, поскольку атрибут Association задает значение свойства ThisKey этого атрибута равным CustomerID, мы знаем, что столбец CustomerID таблицы Orders будет местом хранения внешнего ключа.

Важно понимать, что атрибут Association, указанный на каждой стороне отношения ассоциации — Customer и Order — определяет местонахождение ключей обеих сторон. Атрибут Association, заданный в сущностном классе Customer, указывает, какие свойства этого класса и класса Orders составляют ключ для отношения. Аналогично атрибут Association, определенный в классе Orders, указывает, какие свойства этого класса и класса Customer составляют ключ отношения.

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

А на дочерней стороне свойство OtherKey атрибута не обязано задаваться, т.к. по умолчанию используется первичный ключ родителя. Таким образом, часто можно видеть свойство OtherKey указанным только на родительской стороне, а свойство ThisKey атрибута — только на дочерней стороне. Но благодаря значениям по умолчанию, и родительский, и дочерний элементы знают ключи на обеих сторонах.

Function

Атрибут Function определяет что метод класса вызывает хранимую процедуру либо пользовательскую функцию со скалярным или табличным значением возврата. Ниже приведена важная часть класса, производного от DataContext, которая связана с хранимой процедурой:

[Function(Name="dbo.Get Customer And Orders")]
[ResultType(typeof(GetCustomerAndOrdersResult1))]
[ResultType(typeof(GetCustomerAndOrdersResult2))]
public IMultipleResults GetCustomerAndOrders(
        [Parameter(Name="CustomerID", DbType="NChar(5)")] string customerID)
{
			...
}

Здесь видно, что есть метод по имени GetCustomerAndOrders, который вызовет хранимую процедуру по имени Get Customer And Orders. Мы знаем, что этот метод отображается на хранимую процедуру, а не на определяемую пользователем функцию, поскольку свойство IsComposable атрибута не указано и потому по умолчанию равно false, что отображает метод на хранимую процедуру. Также можно видеть, что он возвращает множественные формы результатов, потому что указано два атрибута ResultTypes.

Написать класс-наследник DataContext, чтобы он мог вызывать хранимую процедуру — не такая тривиальная задача, как отображение сущностного класса на таблицу. В дополнение к соответствующим атрибутам понадобится также вызывать соответствующую версию метода ExecuteMethodCall класса DataContext.

Конечно, как правило, это необходимо только при написании собственного класса-наследника DataContext, поскольку это сделают SQLMetal и Object Relational Designer.

Существенная часть класса-наследника DataContext, ссылающаяся на определенную пользователем функцию, представлена ниже:

[Function(Name="dbo.MinUnitPriceByCategory", IsComposable=true)]
[return: Parameter(DbType="Money")]
public System.Nullable<decimal> MinUnitPriceByCategory(
       [Parameter(DbType="Int")] System.Nullable<int> categoryID)
{
		...
}

Здесь видно, что имеется метод по имени MinUnitPriceByCategory, который будет вызывать определенную пользователем функцию по имени MinUnitPriceByCategory. Известно, что метод отображается именно на определенную пользователем функцию, а не на хранимую процедуру, поскольку свойство IsComposable атрибута установлено в true.

Также можно видеть по атрибуту return, что определенная пользователем функция вернет значение типа Money.

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

Конечно, как правило, это необходимо при написании собственного класса-наследника DataContext, потому что SQLMetal и Object Relational Designer делают это сами.

Ниже перечислены основные свойства атрибута Function:

IsComposable

Свойство IsComposable атрибута содержит булевское значение, указывающее на то, что отображающий метод вызывает хранимую процедуру или определенную пользователем функцию.

Если значение IsComposable равно true, метод отображается на определяемую пользователем функцию. Если значение IsComposable равно false, метод отображается на хранимую процедуру. Это свойство атрибута по умолчанию имеет значение false, если не указано, т.е. метод, отображаемый атрибутом Function, по умолчанию отображается на хранимую процедуру, если свойство IsComposable атрибута не задано.

Name

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

return

Атрибут return используется для указания возвращаемого типа данных из хранимой процедуры или определяемой пользователем функции. Обычно содержит атрибут Parameter:

[Function(Name="dbo.MinUnitPriceByCategory", IsComposable=true)]
[return: Parameter(DbType="Money")]
public System.Nullable<decimal> MinUnitPriceByCategory(
       [Parameter(DbType="Int")] System.Nullable<int> categoryID)
{
		...
}

В приведенном коде видно, что определенная пользователем функция вернет значение типа Money, на что указывает атрибут return и свойство DbType встроенного атрибута Parameter.

ResultType

Атрибут ResultType отображает тип данных, возвращенный хранимой процедурой, на класс .NET, в котором сохраняются возвращенные данные. Хранимые процедуры, возвращающие множественные формы, указывают несколько атрибутов ResultType в соответствующем порядке:

[Function(Name="dbo.Get Customer And Orders")]
[ResultType(typeof(GetCustomerAndOrdersResult1))]
[ResultType(typeof(GetCustomerAndOrdersResult2))]
public IMultipleResults GetCustomerAndOrders(
       [Parameter(Name="CustomerID", DbType="NChar(5)")] string customerID)
{
		...
}

В приведенном коде видно, что хранимая процедура, на которую отображается этот метод, сначала возвратит форму тина GetCustomerAndOrdersResult1, за которой следует форма тина GetCustomerAnd0rdersResult2.

Parameter

Атрибут Parameter отображает параметр метода на параметр хранимой процедуры или пользовательской функции базы данных. Ниже приведена существенная часть кода класса-наследника DataContext:


public IMultipleResults GetCustomerAndOrders(
       [Parameter(Name="CustomerID", DbType="NChar(5)")] string customerID)
{
		...
}

Здесь видно, что метод GetCustomerAndOrders, отображаемый на хранимую процедуру базы данных по имени Get Customer And Orders, передает хранимой процедуре параметр типа NChar(5).

DbType

Свойство DbType атрибута — это строка, указывающая тип данных базы и модификаторы параметра хранимой процедуры или определенной пользователем функции.

Name

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

InheritanceMapping

Атрибут InheritanceMapping используется для отображения кода дискриминатора (классификатора) на базовый класс или подкласс базового класса. Код дискриминатора — это значение столбца сущностного класса, указанного в качестве дискриминатора, который определен как свойство сущностного класса, чье свойство IsDiscriminator атрибута установлено в true.

Например, рассмотрим следующий атрибут InheritanceMapping:

[InheritanceMapping(Code = "G", Type = typeof(Shape), IsDefault = true)]

Приведенный атрибут InheritanceMapping определяет, что если запись базы данных имеет значение "G" в столбце дискриминатора, т.е. ее код дискриминатора — "G", то нужно создать запись как объект Shape с использованием класса Shape. Поскольку свойство IsDefault атрибута установлено в true, если код дискриминатора записи не соответствует ни одному значению Code атрибута InheritanceMapping, эта запись будет создана в виде объекта класса Shape.

Чтобы использовать отображение наследования, при объявлении сущностного класса одному из его свойств назначается свойство IsDiscriminator атрибута Column, установленное в true. Это значит, что значение этого столбца будет определять по дискриминатору, какого класса экземпляр — базового или одного из его подклассов — хранит запись таблицы.

Атрибут InheritanceMapping указан в базовом классе для каждого из подклассов, а также для самого базового класса. Из всех этих атрибутов InheritanceMapping один и только один должен иметь свойство IsDefault со значением true. Также эта запись таблицы базы данных, дискриминатор которой не соответствует ни одному из кодов дискриминатора, указанных в атрибутах InheritanceMapping, может стать экземпляром этого класса. Наверно, чаще всего атрибут InheritanceMapping задают как атрибут InheritanceMapping по умолчанию.

Опять-таки, все атрибуты InheritanceMapping, указанные на одном из базовых классов, лишь ассоциируют код дискриминатора для базового класса или одного из его подклассов.

Совместимость типов данных

Некоторые атрибуты сущностного класса имеют свойство атрибута DbType, в котором можно задавать тип данных столбца таблицы. Это свойство атрибута используется только при создании базы данных методом CreateDatabase. Поскольку отображение между типами данных .NET и типами данных SQL Server не строится по схеме "один к одному", нужно указать свойство DbType атрибута, если планируется вызывать метод CreateDatabase.

Поскольку типы данных общеязыковой исполняющей среды .NET (Common Language Runtime — CLR), используемые в коде LINQ, не совпадают с типами данных, применяемыми базой данных, следует обратиться к документации MSDN, касающейся отображения типов между SQL и CLR (LINQ to SQL). В этой документации приведена матрица, которая определяет поведение при преобразовании между типа данных CLR и типа данных SQL.

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

Однако в большинстве случаев должно удовлетворить преобразование, и оно не составит проблемы. При создании примеров, посвященных LINQ to SQL, никогда не возникало проблем, вызванных преобразованием типов данных. Конечно, нужно придерживаться здравого смысла. Если вы попытаетесь отображать очевидно несовместимые типы, такие как числовые типы данных .NET на символьный тип данных SQL, то неизбежно столкнетесь с некоторыми проблемами.

Пройди тесты
Лучший чат для C# программистов