Отображение производного класса в таблицу в Linq-to-SQL
У меня есть абстрактный базовый класс для свойств аудита. Для краткости скажу, что у него есть одно свойство
Public MustInherit Class AbstractAuditableEntity
...
Public Property CreatedTime() As DateTimeOffset
...
End Class
И тогда мои проверяемые доменные объекты наследуются от этого класса
Public Class Source
Inherits AbstractAuditableEntity
...
Public Property SourceId() As String
...
End Class
У меня есть следующая таблица DDL, с которой я хочу сопоставить свой объект домена "Источник". По сути, отношение между каждым (конкретным) объектом домена и таблицей составляет 1-1, причем каждая таблица имеет необходимый столбец аудита.
CREATE TABLE Source
(
SourceID VARCHAR(10) NOT NULL,
CreatedTime DATETIMEOFFSET(3) NOT NULL,
CONSTRAINT PK_Source PRIMARY KEY (SourceID))
GO
Используя внешний файл сопоставления, моя первая попытка сопоставить класс с таблицей была бы глупой:
<?xml version="1.0" encoding="utf-8"?>
<Database Name="" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="Source" Member="Sources">
<Type Name ="Source">
<Column Name="SourceID" Member="SourceID" IsPrimaryKey="true" CanBeNull="false"/>
<Column Name="CreatedTime" Member="CreatedTime" />
</Type>
</Table>
</Database>
Однако это создает следующее исключение:
Столбец или ассоциация "CreatedTime" в отображении не имели соответствующего члена в типе "Source". Отображение членов сверху корневого типа не поддерживается.
В контексте моего уровня персистентности я не пытаюсь представить иерархию наследования как таковую, но в контексте моего приложения я просто использую базовый класс для обеспечения свойств, требуемых всеми объектами моего домена. Из-за большого количества возни с моим файлом сопоставления (включая сопоставление столбцов аудита с базовым типом AbstractAuditableEntity) и чтения вокруг, я не могу достичь того, что я воспринимаю как довольно простую задачу ORM.
Любые мысли или предложения будут приветствоваться! Спасибо
2 ответа
Келли показал отличный пример того, как это сделать, но вы в основном столкнулись с одним из ограничений Linq-to-SQL.
Это прекрасно работает, если ваша таблица базы данных более или менее соответствует 1:1 объектам вашего домена. Но он слабый и требует много дополнительной работы, если это уже не так.
В таком случае, как только у вас появится наследование объекта домена и другие вещи, которые необходимо сопоставить с таблицами базы данных, лучше всего вместо этого проверить ADO.NET Entity Framework. EF специально разработан для того, чтобы справляться с этими вещами - если вы когда-нибудь думаете "мне нужно нанести на карту мои объекты…", тогда вам следует подумать о EF!:-)
Конечно, текущая поставка EF в.NET 3.5 SP1 имеет свои плюсы и неприятности, но EF 4, являющийся частью волны.NET 4.0 (которая должна появиться до конца 2009 года), должен решить многие из них. бородавки!
Загляните в блог команды ADO.NET Entity Framework, чтобы узнать, что принесет нам EF4!
Марк
Я предполагаю, что вы пытаетесь эмулировать поля аудита, такие как Ruby on Rails updated_on
, created_on
, Если так, то вот как я сделал нечто подобное, используя этот пост в качестве отправной точки http://weblogs.asp.net/stevesheldon/archive/2008/02/23/a-method-to-handle-audit-fields-using-linq-to-sql.aspx
Я реализовал интерфейс в пространстве имен Models следующим образом:
public interface IAuditable
{
DateTime CreatedOn { get; set; }
string CreatedBy { get; set; }
DateTime? ChangedOn { get; set; }
string ChangedBy { get; set; }
}
А затем расширили частичные классы объектов данных, которые имели эти поля:
public partial class DataModelIWantToAudit : IAuditable
{
}
А потом перебор SubmitChanges
на DataContext
проверить реализацию интерфейса с помощью магии Linq OfType<>
:
public override void SubmitChanges(ConflictMode failureMode)
{
//Updates
foreach (var updatedModel in GetChangeSet().Updates.OfType<IAuditable>())
{
updatedModel.ChangedOn = DateTime.Now;
updatedModel.ChangedBy = Membership.GetUser().UserName;
}
//Inserts
foreach (var insertedModel in GetChangeSet().Inserts.OfType<IAuditable>())
{
insertedModel.CreatedOn = DateTime.Now;
insertedModel.CreatedBy = Membership.GetUser().UserName;
}
base.SubmitChanges(failureMode);
}
Надеюсь, это поможет! -Kelly