ATUALIZAÇÃO: recebi alguns votos positivos sobre isso ultimamente, então achei que deixaria as pessoas saberem que os conselhos que dou abaixo não são os melhores. Desde que comecei a mexer no Entity Framework em bancos de dados sem chave antigos, percebi que a melhor coisa que você pode fazer BY FAR é fazê-lo revertendo o código primeiro. Existem alguns bons artigos por aí sobre como fazer isso. Basta segui-los e, quando desejar adicionar uma chave, use anotações de dados para "falsificar" a chave.
Por exemplo, digamos que eu saiba que minha mesa Orders, embora não tenha uma chave primária, garante apenas um número de pedido por cliente. Como essas são as duas primeiras colunas da tabela, eu configuraria o código das primeiras classes para ficar assim:
[Key, Column(Order = 0)]
public Int32? OrderNumber { get; set; }
[Key, Column(Order = 1)]
public String Customer { get; set; }
Ao fazer isso, você basicamente finge que a EF acredita que há uma chave de cluster composta por OrderNumber e Customer. Isso permitirá que você faça inserções, atualizações, etc. na sua mesa sem chave.
Se você não está muito familiarizado com a reversão do Code First, encontre um bom tutorial sobre o Entity Framework Code First. Em seguida, encontre um no Reverse Code First (que está executando o Code First com um banco de dados existente). Depois, volte aqui e olhe para os meus principais conselhos novamente. :)
Resposta original :
Primeiro: como já foi dito, a melhor opção é adicionar uma chave primária à tabela. Ponto final. Se você puder fazer isso, não leia mais.
Mas se você não pode, ou simplesmente se odeia, há uma maneira de fazer isso sem a chave primária.
No meu caso, eu estava trabalhando com um sistema legado (originalmente arquivos simples em um AS400 portado para o Access e, em seguida, portado para o T-SQL). Então eu tive que encontrar uma maneira. Esta é a minha solução. O seguinte funcionou para mim usando o Entity Framework 6.0 (o mais recente no NuGet até o momento desta redação).
Clique com o botão direito do mouse no seu arquivo .edmx no Gerenciador de Soluções. Escolha "Abrir com ..." e selecione "Editor de XML (texto)". Vamos editar manualmente o código gerado automaticamente aqui.
Procure uma linha como esta:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" store:Schema="dbo" store:Name="table_nane">
Retire store:Name="table_name"do final.
Mude store:Schema="whatever"paraSchema="whatever"
Olhe abaixo dessa linha e encontre a <DefiningQuery>tag. Ele terá uma grande e velha declaração de seleção. Remova a tag e seu conteúdo.
Agora sua linha deve ficar assim:
<EntitySet Name="table_name" EntityType="MyModel.Store.table_name" store:Type="Tables" Schema="dbo" />
Temos mais alguma coisa para mudar. Acesse seu arquivo e encontre o seguinte:
<EntityType Name="table_name">
Nas proximidades, você provavelmente verá um texto comentado avisando que ela não possui uma chave primária identificada; portanto, a chave foi inferida e a definição é uma tabela / exibição somente leitura. Você pode deixá-lo ou excluí-lo. Eu deletei.
Abaixo está a <Key>tag. É isso que o Entity Framework usará para inserir / atualizar / excluir. ASSIM TENHA CERTEZA DE FAZER ISSO DIREITO. A propriedade (ou propriedades) nessa tag precisa indicar uma linha identificável exclusivamente. Por exemplo, digamos que eu saiba que minha mesa orders, embora não tenha uma chave primária, garante apenas um número de pedido por cliente.
Então o meu se parece com:
<EntityType Name="table_name">
<Key>
<PropertyRef Name="order_numbers" />
<PropertyRef Name="customer_name" />
</Key>
Sério, não faça isso errado. Digamos que, embora nunca deva haver duplicatas, de alguma forma duas linhas entram no meu sistema com o mesmo número de pedido e nome do cliente. Opa! É isso que recebo por não usar uma chave! Então, eu uso o Entity Framework para excluir um. Como sei que a duplicata é a única ordem feita hoje, faço o seguinte:
var duplicateOrder = myModel.orders.First(x => x.order_date == DateTime.Today);
myModel.orders.Remove(duplicateOrder);
Adivinha? Acabei de excluir a duplicata e o original! Isso porque eu disse ao Entity Framework que order_number / cutomer_name era minha chave primária. Então, quando eu disse para remover duplicateOrder, o que ele fez em segundo plano foi algo como:
DELETE FROM orders
WHERE order_number = (duplicateOrder's order number)
AND customer_name = (duplicateOrder's customer name)
E com esse aviso ... agora você deve ir!