Mapeando uma classe derivada para uma tabela em Linq para SQL
-
20-09-2019 - |
Pergunta
Eu tenho uma classe base abstrata para propriedades de auditoria. Para a brevidade, diga que tem uma propriedade
Public MustInherit Class AbstractAuditableEntity
...
Public Property CreatedTime() As DateTimeOffset
...
End Class
E então meus objetos de domínio auditável herdam desta classe
Public Class Source
Inherits AbstractAuditableEntity
...
Public Property SourceId() As String
...
End Class
Eu tenho a tabela seguinte DDL para a qual quero mapear meu objeto de domínio "fonte". Essencialmente, a relação entre cada objeto de domínio (concreto) e a tabela é 1-1, com cada tabela com a coluna de auditoria necessária.
CREATE TABLE Source
(
SourceID VARCHAR(10) NOT NULL,
CreatedTime DATETIMEOFFSET(3) NOT NULL,
CONSTRAINT PK_Source PRIMARY KEY (SourceID))
GO
Usando um arquivo de mapeamento externo, minha primeira tentativa de mapear a classe para a tabela seria tolamente:
<?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>
No entanto, isso gera a seguinte exceção:
A coluna ou associação 'CreatedTime' no mapeamento não possuía membro correspondente no tipo 'fonte'. Os membros do mapeamento do tipo de raiz acima não são suportados.
No contexto da minha camada de persistência, não estou tentando representar uma hierarquia de herança como tal, mas no contexto do meu aplicativo, estou simplesmente usando uma classe base para fornecer propriedades exigidas por todos os meus objetos de domínio. Com muitas mexendo com o meu arquivo de mapeamento (incluindo mapeamento das colunas de auditoria para o tipo de base abstrata e leitura de base) e leitura, não consigo conseguir o que considero uma tarefa ORM bastante reta.
Quaisquer pensamentos ou sugestões seriam bem -vindos! Obrigado
Solução
Kelly mostrou uma ótima amostra de como fazê-lo-mas você basicamente atingiu uma das limitações do LINQ para SQL.
Funciona muito bem se o mapa da tabela de banco de dados mais ou menos 1: 1 nos seus objetos de domínio. Mas é fraco e causa muito trabalho extra quando esse não é mais o caso.
Nesse caso, assim que você tiver herança de objetos de domínio e outras coisas que precisam ser mapeadas para tabelas de banco de dados, sua melhor aposta seria verificar a estrutura da entidade ADO.NET. A EF é projetada especificamente para lidar com essas coisas - se você pensa "eu preciso mapa Meus objetos ...... "Então você deve pensar ef! :-)
É verdade que o envio atual da EF no .NET 3.5 SP1 tem suas verrugas e aborrecimentos, mas o EF 4 que faz parte da onda .NET 4.0 (que deve ser enviada antes do final deste ano de 2009) deve resolver muitos deles Verrugas!
Confira o Blog da equipe da estrutura da entidade ADO.NET Para alguns teasers do que o EF4 trará a todos nós!
Marc
Outras dicas
Acho que você está tentando imitar campos de auditoria como Ruby on Rails updated_on
, created_on
. Nesse caso, aqui está como eu realizei algo semelhante usando este post como ponto de partidahttp://weblogs.asp.net/stevesheldon/archive/2008/02/23/a-method-to-handle-audit-fields-using-linq-to-sql.aspx
Eu implementei uma interface no espaço de nome dos modelos como assim:
public interface IAuditable
{
DateTime CreatedOn { get; set; }
string CreatedBy { get; set; }
DateTime? ChangedOn { get; set; }
string ChangedBy { get; set; }
}
E depois estendeu as classes parciais das entidades de dados que tinham esses campos:
public partial class DataModelIWantToAudit : IAuditable
{
}
E depois substituiu SubmitChanges
no DataContext
Para verificar a implementação da interface com a magia de 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);
}
Espero que ajude! -Kelly