Сопоставление производного класса с таблицей в 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» в сопоставлении не имеет соответствующего члена типа «Источник». Сопоставление элементов с указанным выше корневым типом не поддерживается.

В контексте моего уровня сохраняемости я не пытаюсь представить иерархию наследования как таковую, но в контексте моего приложения я просто использую базовый класс для предоставления свойств, необходимых для всех моих объектов домена. Из-за того, что я много возился с моим файлом сопоставления (включая сопоставление столбцов аудита с базовым типом AbstractAuditableEntity) и читая все вокруг, я не могу достичь того, что я считаю довольно простой задачей ORM.

Любые мысли или предложения приветствуются! Спасибо


person Pero P.    schedule 27.08.2009    source источник


Ответы (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 принесет всем нам!

Марк

person marc_s    schedule 28.08.2009
comment
Я все еще в шоке от того, что не могу легко сопоставить простой производный класс с таблицей в предполагаемом ORM ;-). Все, что мне было нужно, это подтверждение того, что я не могу этого добиться ... которое вы сейчас предоставили. Спасибо. Благодаря Келли также, поскольку решение, которое вы предоставили, - это то, чего я в конечном итоге пытался достичь, но в контексте Linq-to-SQL реализация свойств IAuditable в базовом классе невозможна. - person Pero P.; 28.08.2009

Я предполагаю, что вы пытаетесь имитировать поля аудита, такие как 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);
}

Надеюсь, это поможет! -Келли

person Kelly Adams    schedule 28.08.2009