Estrutura de entidades: tabela sem chave primária

165

Eu tenho um banco de dados existente com o qual gostaria de criar um novo aplicativo usando EF4.0

Algumas tabelas não têm chaves primárias definidas para que, quando eu crie um novo modelo de dados de entidade, receba a seguinte mensagem:

The table/view TABLE_NAME does not have a primary key defined 
and no valid primary key could be inferred. This table/view has 
been excluded. To use the entity, you will need to review your schema, 
add the correct keys, and uncomment it.

Se eu quiser usá-los e modificar dados, devo necessariamente adicionar uma PK a essas tabelas ou há uma solução alternativa para que eu não precise?

Cris
fonte
21
Para citar Joe Celko: se não tiver uma chave primária, não será uma tabela . Por que diabos alguém criaria uma tabela "regular" sem uma chave primária? Apenas adicione aqueles PK! Você precisará deles - mais cedo ou mais tarde ...
marc_s
4
Se é uma vista deste hava um olhar neste caso stackoverflow.com/a/10302066/413032
Davut Gürbüz
50
É perfeitamente válido que nem toda tabela precise de uma chave primária. Geralmente não é útil, mas é válido. A EF confusa é um bom motivo, não que seja preciso muito. ;-).
Suncat2000
25
Imagine que não posso modificar a estrutura do banco de dados na minha empresa e ela foi criada por alguém que não altera a estrutura da tabela, esse cenário é possível.
Tito
4
É exatamente onde estamos. Temos que trabalhar com um banco de dados Oracle de terceiros que não possua chaves primárias.
David Brower

Respostas:

58

O erro significa exatamente o que diz.

Mesmo se você pudesse resolver isso, confie em mim, não quer. O número de bugs confusos que podem ser introduzidos é assustador e assustador, sem mencionar o fato de que seu desempenho provavelmente será prejudicado.

Não trabalhe com isso. Corrija seu modelo de dados.

Edição: Eu vi que um número de pessoas estão votando esta questão. Tudo bem, suponho, mas lembre-se de que o OP perguntou sobre o mapeamento de uma tabela sem uma chave primária, não uma visualização . A resposta ainda é a mesma. Contornar a necessidade da EF de ter um PK nas tabelas é uma má ideia do ponto de vista de gerenciamento, integridade de dados e desempenho.

Alguns comentaram que não têm a capacidade de corrigir o modelo de dados subjacente porque estão mapeando para um aplicativo de terceiros. Essa não é uma boa ideia, pois o modelo pode mudar de baixo de você. Indiscutivelmente, nesse caso, você desejaria mapear para uma exibição, que, novamente, não é o que o OP pediu.

Dave Markle
fonte
44
Concorde em senários comuns, mas em senários raros, como a tabela LOG, você só precisa inserir registros o mais rápido possível. Ter PK pode ser um problema ao verificar a exclusividade e a indexação. Além disso, se sua PK for IDENTITY, retornar o valor gerado para a EF é outro problema. usando GUID? tempo de geração e indexação / classificação são outra questão! ... ASSIM, EM alguns senários críticos de OLTP (como Log) não ter PK é um ponto e tê-lo não tem nenhum ponto positivo!
Mahmoud Moravej
5
@MahmoudMoravej: Primeiro, não misture as idéias de índices de cluster e chaves primárias. Eles não são a mesma coisa. Você pode ter inserções de alto desempenho em tabelas com indicações em cluster nas colunas IDENTITY. Se você tiver problemas com a manutenção do índice, particione a tabela corretamente. Sair de uma tabela sem índice clusterizado também significa que você não pode desfragmentá-la efetivamente para recuperar espaço após exclusões. Tenho pena da pobre pessoa que tenta consultar sua tabela de log se ela não possui índices.
precisa saber é o seguinte
149
"Corrija seu modelo de dados" não é uma resposta real. Às vezes, temos que conviver com situações abaixo do ideal que não criamos e não podemos mudar. E, como o @Colin disse, existe uma maneira de fazer exatamente o que o OP estava pedindo.
TheSmurf #
13
Alterar o código para satisfazer EF é uma solução alternativa em si. Nem todas as tabelas precisam de uma chave primária nem devem ser forçadas. Por exemplo, você tem um tópico e possui 0 ou várias palavras-chave. A tabela de palavras-chave pode ter um ID de tópico pai e uma palavra-chave correspondente. Dizer que preciso remodelar meu banco de dados porque a EF me obriga a ser coxo.
Mrchief
14
Isso deve ser diminuído porque não responde à pergunta. Geralmente, precisamos trabalhar com bancos de dados de terceiros que não podem ser alterados.
Kurren
104

Eu acho que isso é resolvido por Tillito:

Estrutura de entidades e exibição do SQL Server

Vou citar sua entrada abaixo:

Tivemos o mesmo problema e esta é a solução:

Para forçar a estrutura da entidade a usar uma coluna como chave primária, use ISNULL.

Para forçar a estrutura da entidade a não usar uma coluna como chave primária, use NULLIF.

Uma maneira fácil de aplicar isso é agrupar a instrução select da sua visualização em outra seleção.

Exemplo:

SELECT
  ISNULL(MyPrimaryID,-999) MyPrimaryID,
  NULLIF(AnotherProperty,'') AnotherProperty
  FROM ( ... ) AS temp

respondeu Apr 26 '10 às 17:00 por Tillito

Colin
fonte
6
+1 Esta é a resposta certa. Em um mundo perfeito, seria ótimo acessar e modificar todos os bancos de dados herdados para ter integridade referencial, mas, na realidade, isso nem sempre é possível.
Dylan Hayes
9
Eu não recomendaria isso. Paricularmente a parte ISNULL. Se o EF detectar duas PKs iguais, pode não renderizar o (s) registro (s) exclusivo (s) e, em vez disso, retornar um objeto compartilhado. Isto aconteceu comigo antes.
Todd
@ Todd - como isso poderia acontecer se MyPrimaryID é uma coluna NOT NULL?
JoeCool
@ JoeCool, só porque NÃO é NULL, não significa que É ÚNICO. Eu votei em "ESTA SOLUÇÃO FUNCIONA ...", porque não importa em que contexto seja usado, você pode ter certeza de que é único. Embora esteja pensando sobre isso agora, se um registro for excluído, isso efetivamente mudará os "PK" dos seguintes registros.
Todd
1
-1, porque isso não responde como configurar o código do Entity Framework / C # para lidar com como mapear para uma tabela que não possui uma semente de identidade. Alguns softwares de terceiros são (por algum motivo) escritos dessa maneira.
Andrew Gray
27

Se eu quiser usá-los e modificar dados, devo necessariamente adicionar uma PK a essas tabelas ou há uma solução alternativa para que eu não precise?

Para aqueles que alcançam essa pergunta e estão usando o Entity Framework Core, não é mais necessário adicionar necessariamente uma PK a essas tabelas ou executar qualquer solução alternativa. Desde o EF Core 2.1, temos um novo recurso Tipos de Consulta

Os tipos de consulta devem ser usados ​​para:

  • Servindo como o tipo de retorno para consultas ad hoc FromSql ().
  • Mapeando para visualizações de banco de dados.
  • Mapeamento para tabelas que não possuem uma chave primária definida.
  • Mapeamento para consultas definidas no modelo.

Portanto, no seu DbContext, basta adicionar a seguinte propriedade do tipo, em DbQuery<T>vez de DbSet<T>como abaixo. Supondo que o nome da sua tabela seja MyTable:

public DbQuery<MyTable> MyTables { get; set; }
CodeNotFound
fonte
1
Melhor resposta se você estiver usando o EF Core!
Jay Jay
17

As chaves compostas também podem ser feitas com a API Fluente do Entity Framework

public class MyModelConfiguration : EntityTypeConfiguration<MyModel>
{
     public MyModelConfiguration()
     {
        ToTable("MY_MODEL_TABLE");
        HasKey(x => new { x.SourceId, x.StartDate, x.EndDate, x.GmsDate });
        ...
     }
}
Adrian Garcia
fonte
Nesse tipo de caso de 'mapeamento manual', descobri que especificar uma chave personalizada como você mostra é eficaz; Além disso, se você não tiver o benefício de uma chave composta (como mostrado nesta resposta), poderá marcar uma modelBuilder.Entity<T>()chamada em cadeia .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)para aquelas chaves tão especiais que não são naturais ou compostas, mas podem ser confiáveis em cima de ser único (geralmente, de qualquer maneira.)
Andrew Gray
5

No meu caso, tive que mapear uma entidade para uma View, que não tinha chave primária. Além disso, eu não tinha permissão para modificar essa visualização. Felizmente, este View tinha uma coluna que era uma string única. Minha solução foi marcar esta coluna como uma chave primária:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[StringLength(255)]
public string UserSID { get; set; }

EF enganado. Funcionou perfeitamente, ninguém notou ... :)

Boris Lipschitz
fonte
não .. Ele criará a UserIDcoluna se você usar a abordagem Code First ..!
Deepak Sharma 22/10
1
Você não trapaceou EF. Você acabou de comandar para desligar a Itentityfunção. Basicamente, se eu estiver correto, ele ainda criará uma UserIDcoluna para você como PK, mas não aumentará automaticamente UserIDquando você criar um novo registro, como seria por padrão. Além disso, você ainda precisa manter valores distintos UserID.
Celdor 27/01
1
@Celdor UserSID é uma string, nunca seria "aumentada automaticamente". Se fosse uma coluna de identidade inteira, o banco de dados o incrementaria na inserção, não no Entity Framework.
Reggaeguitar 7/07
4

O EF não requer uma chave primária no banco de dados. Caso isso aconteça, você não poderá vincular entidades a visualizações.

Você pode modificar o SSDL (e o CSDL) para especificar um campo exclusivo como sua chave primária. Se você não tem um campo único, acredito que você está em uma mangueira. Mas você realmente deve ter um campo exclusivo (e um PK), caso contrário, terá problemas mais tarde.

Erick

Erick T
fonte
Isso evita o hack ISNULL. Mas, dependendo da situação, outras respostas podem ser necessárias - sinto que alguns tipos de dados não são suportados para uma PK no EF, por exemplo.
Todd
3

Ter uma chave de identidade inútil às vezes é inútil. Acho que se o ID não é usado, por que adicioná-lo? No entanto, a Entidade não perdoa tanto, portanto, é melhor adicionar um campo de ID. Mesmo no caso de não ser usado, é melhor do que lidar com os erros incessivos da Entidade sobre a chave de identidade ausente.

IyaTaisho
fonte
3

ESTA SOLUÇÃO FUNCIONA

Você não precisa mapear manualmente, mesmo se não tiver um PK. Você só precisa informar ao EF que uma de suas colunas é index e a coluna index não é anulável.

Para fazer isso, você pode adicionar um número de linha à sua visualização com a função isNull, como a seguinte

select 
    ISNULL(ROW_NUMBER() OVER (ORDER BY xxx), - 9999) AS id
from a

ISNULL(id, number) é o ponto principal aqui, porque informa à EF que esta coluna pode ser a chave primária

Barny
fonte
2
Eu não sugeriria a parte ISNULL. Se o EF detectar duas PKs iguais, pode não renderizar o registro exclusivo e, em vez disso, retornar um objeto compartilhado. Isto aconteceu comigo antes.
21413 Todd
1
Você precisa usar isnull, caso contrário, a EF não acreditará que não seja anulável.
Archlight
2

As respostas acima estão corretas se você realmente não tiver um PK.

Mas se houver um, mas ele simplesmente não for especificado com um índice no banco de dados, e você não puder alterar o banco de dados (sim, eu trabalho no mundo de Dilbert), você pode mapear manualmente o (s) campo (s) para ser a chave.

Vilão do Poker
fonte
2

Esta é apenas uma adição à resposta de @Erick T. Se não houver uma única coluna com valores exclusivos, a solução alternativa é usar uma chave composta, da seguinte maneira:

[Key]
[Column("LAST_NAME", Order = 1)]
public string LastName { get; set; }

[Key]
[Column("FIRST_NAME", Order = 2)]
public string FirstName { get; set; }

Novamente, isso é apenas uma solução alternativa. A solução real é consertar o modelo de dados.

Desenvolvedor
fonte
2

Talvez seja tarde demais para responder ... no entanto ...

Se uma tabela não possui uma chave primária, existem poucos cenários que precisam ser analisados ​​para que o EF funcione corretamente. A regra é: A EF funcionará com tabelas / classes com chave primária. É assim que ele rastreia ...

Diga sua tabela 1. Os registros são únicos: a exclusividade é feita por uma única coluna de chave estrangeira: 2. Os registros são únicos: a exclusividade é feita por uma combinação de várias colunas. 3. Os registros não são exclusivos (na maior parte *).

Para os cenários 1 e 2, você pode adicionar a seguinte linha ao método OnModelCreating do módulo DbContext: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // quantas colunas forem necessárias para tornar os registros exclusivos.

Para o cenário nº 3, você ainda pode usar a solução acima (nº 1 + nº 2) depois de estudar a tabela (* o que torna todos os registros exclusivos de qualquer maneira). Se você precisar incluir TODAS as colunas para tornar todos os registros exclusivos, convém adicionar uma coluna de chave primária à sua tabela. Se esta tabela for de um fornecedor de terceiros, clone-a no banco de dados local (durante a noite ou quantas vezes você precisar) com a coluna de chave primária adicionada arbitrariamente por meio do script de clone.

Sam Saarian
fonte
1
  1. Altere a estrutura da tabela e adicione uma coluna principal. Atualizar o modelo
  2. Modifique o arquivo .EDMX no XML Editor e tente adicionar uma nova coluna sob a tag para esta tabela específica (NÃO FUNCIONARÁ)
  3. Em vez de criar uma nova tabela Coluna Primária para Saída, farei uma chave composta envolvendo todas as colunas existentes ( WORKED )

Estrutura de Entidades: Adicionando DataTable sem Chave Primária ao Modelo de Entidade.

Pratap
fonte
Tentei a abordagem de chave composta com o EF 4.0 e não funcionou.
Ralph Willgoss
Esta abordagem funcionou perfeitamente para mim, pode ser uma dor de trabalhar com sistemas legados "às vezes" ...
pinmonkeyiii
1

Atualizar para @CodeNotFound a resposta de .

No EF Core 3.0 DbQuery<T>foi preterido, você deve usar os tipos de entidade sem chave que supostamente fazem a mesma coisa. Eles são configurados com o HasNoKey()método ModelBuilder . Na sua classe DbContext, faça isso

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<YourEntityType>(eb =>
        {
            eb.HasNoKey();
        });

}

Existem restrições, porém:

  • Nunca são rastreados para alterações no DbContext e, portanto, nunca são inseridos, atualizados ou excluídos no banco de dados.
  • Suporta apenas um subconjunto de recursos de mapeamento de navegação, especificamente:
    • Eles podem nunca agir como o principal objetivo de um relacionamento.
    • Eles podem não ter navegações para entidades próprias
    • Eles podem conter apenas propriedades de navegação de referência apontando para entidades regulares.
    • As entidades não podem conter propriedades de navegação para tipos de entidade sem chave.

Isso significa que, para a questão de

Se eu quiser usá-los e modificar dados, devo necessariamente adicionar uma PK a essas tabelas ou há uma solução alternativa para que eu não precise?

Você não pode modificar os dados dessa maneira - no entanto, pode ler. No entanto, pode-se imaginar usar outra maneira (por exemplo, ADO.NET, Dapper) para modificar dados - isso pode ser uma solução nos casos em que você raramente precisa executar operações de não leitura e ainda gostaria de usar o EF Core para os casos de maioria.

Além disso, se você realmente precisa / deseja trabalhar com tabelas de pilha (sem chave) - considere abandonar o EF e use outra maneira de conversar com seu banco de dados.

Peter Lindsten
fonte
0

Do ponto de vista prático, toda tabela - mesmo uma tabela desnormalizada como uma tabela de armazém - deve ter uma chave primária. Ou, na sua falta, deve ter pelo menos um índice exclusivo e não nulo.

Sem algum tipo de chave exclusiva, os registros duplicados podem (e serão) exibidos na tabela, o que é muito problemático para as camadas ORM e também para a compreensão básica dos dados. Uma tabela que possui registros duplicados é provavelmente um sintoma de design incorreto.

No mínimo, a tabela deve ter pelo menos uma coluna de identidade. A adição de uma coluna de ID de geração automática leva cerca de 2 minutos no SQL Server e 5 minutos no Oracle. Por esse esforço extra, muitos, muitos problemas serão evitados.

Ash8087
fonte
Meu aplicativo está em uma configuração de armazém de dados (com Oracle) e você me convenceu a passar os 5 minutos para adicionar um índice. Realmente leva apenas 5 minutos (ou um pouco mais se você precisar procurar ou modificar ETLs).
Trent
0

Também encontramos esse problema e, embora tivéssemos uma coluna que tinha nulos, o importante era que tivéssemos uma coluna dependente que não tinha nulos e que a combinação dessas duas colunas era única.

Então, para citar a resposta de Pratap Reddy, funcionou bem para nós.

Azerax
fonte
0

Estou muito feliz que meu problema esteja resolvido.

Não foi possível atualizar o EntitySet - porque ele possui um DefiningQuery e nenhum elemento <UpdateFunction> existe

e faça o seguinte: olhe abaixo dessa linha e encontre a tag. Ele terá uma grande e velha declaração de seleção. Remova a tag e seu conteúdo.

agora sem alterar o DB TI pode inserir em uma tabela que não possui PK

obrigado a todos e obrigado Pharylon

aseman arabsorkhi
fonte
-8

A tabela só precisa ter uma coluna que não permita valores nulos

mister9
fonte