Estrutura de entidades e exibição do SQL Server

132

Por várias razões sobre as quais não tenho liberdade de falar, estamos definindo uma exibição em nosso banco de dados do Sql Server 2005 da seguinte forma:

CREATE VIEW [dbo].[MeterProvingStatisticsPoint]
AS
SELECT
    CAST(0 AS BIGINT) AS 'RowNumber',
    CAST(0 AS BIGINT) AS 'ProverTicketId',
    CAST(0 AS INT) AS 'ReportNumber',
    GETDATE() AS 'CompletedDateTime',
    CAST(1.1 AS float) AS 'MeterFactor',
    CAST(1.1 AS float) AS 'Density',
    CAST(1.1 AS float) AS 'FlowRate',
    CAST(1.1 AS float) AS 'Average',
    CAST(1.1 AS float) AS 'StandardDeviation',
    CAST(1.1 AS float) AS 'MeanPlus2XStandardDeviation',
    CAST(1.1 AS float) AS 'MeanMinus2XStandardDeviation'
WHERE 0 = 1

A idéia é que o Entity Framework crie uma entidade com base nessa consulta, o que faz, mas a gera com um erro que afirma o seguinte:

Aviso 6002: A tabela / exibição 'Keystone_Local.dbo.MeterProvingStatisticsPoint' não possui uma chave primária definida. A chave foi inferida e a definição foi criada como uma tabela / exibição somente leitura.

E decide que o campo CompletedDateTime será a chave primária dessa entidade.

Estamos usando o EdmGen para gerar o modelo. Existe uma maneira de não fazer com que a estrutura da entidade inclua algum campo dessa visão como chave primária?

Sergio Romero
fonte

Respostas:

245

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
Tillito
fonte
2
Eu acho que este é o melhor que se pode esperar. Bottom line funciona.
MvcCmsJon
1
Obrigado! Funcionou perfeitamente. @sabanito Acho que analisa a definição. é por isso que você precisa agrupar especificamente as propriedades da chave em IsNull (). Eu tenho uma visão que não retorna nenhum nulo (e não pode retornar nenhum nulo), mas devido à maneira como a lógica foi escrita, a EF não pôde determinar que era esse o caso até que eu embrulhasse as chaves em IsNull ().
Rabino
3
O único problema que vejo aqui é que a exibição pode legitimamente precisar retornar uma string vazia ''. O que eu fiz foi simplesmente converter a coluna de volta para seu próprio tipo de dados. por exemplo, se AnotherProperty tivesse um tipo de dados varchar (50), eu o converteria como 'CONVERT (VARCHAR (50), AnotherProperty) AS [AnotherProperty]'. isso mascara a nulidade do EF e também permite cadeias vazias.
1155 Bart
2
Sim, este obras, por exemplo para fazer EF usar a coluna como um isnull chave primária (CONVERT (VARCHAR (50), newid ()), '') AS [PK]
dc2009
2
Além de haver apenas uma mensagem irritante na solução, há algum mal em não corrigir isso? Concordo com a sua solução, mas, francamente, não acho que deva fazer isso - acho que todos podemos concordar que isso é um bug, certo?
Dislexicanaboko
67

Consegui resolver isso usando o designer.

  1. Abra o Navegador de modelos.
  2. Encontre a vista no diagrama.
  3. Clique com o botão direito na chave primária e verifique se "Chave da entidade" está marcada.
  4. Selecione várias vezes todas as chaves não primárias. Use as teclas Ctrl ou Shift.
  5. Na janela Propriedades (pressione F4, se necessário, para vê-lo), altere o menu suspenso "Chave da entidade" para Falso.
  6. Salve as alterações.
  7. Feche o Visual Studio e abra-o novamente. Estou usando o Visual Studio 2013 com EF 6 e tive que fazer isso para que os avisos fossem embora.

Não precisei alterar minha visão para usar as soluções alternativas ISNULL, NULLIF ou COALESCE. Se você atualizar seu modelo a partir do banco de dados, os avisos reaparecerão, mas desaparecerão se você fechar e reabrir o VS. As alterações feitas no designer serão preservadas e não afetadas pela atualização.

Casey Plummer
fonte
9
Confirmado. É necessário reiniciar o VS2013 para fazer o aviso desaparecer.
Michael Logutov
5
"Você já tentou desligar e ligar?" ;-) Obrigado, funciona como um encanto!
Obl Tobl
4
Quando estou criando visualizações, elas nem chegam ao diagrama do modelo. Eles são comentados no arquivo xml
ggderas
Solução simples e fácil e não parece tanto uma correção não-hacky quanto manipular a exibição! Obrigado.
LuqJensen
2
O VS2017 confirmado também precisa ser reiniciado para que o aviso desapareça.
21418 Marc
46

Concorde com o @Tillito, no entanto, na maioria dos casos, ele prejudicará o otimizador de SQL e não usará índices corretos.

Pode ser óbvio para alguém, mas eu queimei horas resolvendo problemas de desempenho usando a solução Tillito. Digamos que você tenha a tabela:

 Create table OrderDetail
    (  
       Id int primary key,
       CustomerId int references Customer(Id),
       Amount decimal default(0)
    );
 Create index ix_customer on OrderDetail(CustomerId);

e sua visão é algo como isto

 Create view CustomerView
    As
      Select 
          IsNull(CustomerId, -1) as CustomerId, -- forcing EF to use it as key
          Sum(Amount) as Amount
      From OrderDetail
      Group by CustomerId

O otimizador sql não usará o índice ix_customer e executará a verificação de tabela no índice primário, mas se em vez de:

Group by CustomerId

você usa

Group by IsNull(CustomerId, -1)

fará com que o MS SQL (pelo menos 2008) inclua o índice correto no plano.

E se

Val Bakhtin
fonte
2
Este deve ser um comentário sobre a resposta de Tillito, não uma resposta em si, pois não fornece uma solução para a pergunta do OP.
Zimdanen
6
O cara tem um representante de 1, ainda não pode adicionar um comentário.
Jrcs3
@zimdanen Não há como você encaixar todas essas informações em um comentário, faz mais sentido tê-las em uma resposta separada.
Contango 25/03
2
@ Contango: Esta resposta foi editada seis dias após a publicação e eu postei meu comentário. Veja o histórico de revisões.
Zimdanen
9

Este método funciona bem para mim. Uso ISNULL () para o campo da chave primária e COALESCE () se o campo não deve ser a chave primária, mas também deve ter um valor não anulável. Este exemplo gera um campo de ID com uma chave primária não anulável. Os outros campos não são chaves e têm (Nenhum) como atributo Anulável.

SELECT      
ISNULL(P.ID, - 1) AS ID,  
COALESCE (P.PurchaseAgent, U.[User Nickname]) AS PurchaseAgent,  
COALESCE (P.PurchaseAuthority, 0) AS PurchaseAuthority,  
COALESCE (P.AgencyCode, '') AS AgencyCode,  
COALESCE (P.UserID, U.ID) AS UserID,  
COALESCE (P.AssignPOs, 'false') AS AssignPOs,  
COALESCE (P.AuthString, '') AS AuthString,  
COALESCE (P.AssignVendors, 'false') AS AssignVendors 
FROM Users AS U  
INNER JOIN Users AS AU ON U.Login = AU.UserName  
LEFT OUTER JOIN PurchaseAgents AS P ON U.ID = P.UserID

se você realmente não possui uma chave primária, pode falsificá-la usando ROW_NUMBER para gerar uma pseudo-chave que é ignorada pelo seu código. Por exemplo:

SELECT
ROW_NUMBER() OVER(ORDER BY A,B) AS Id,
A, B
FROM SOMETABLE
SpazDude
fonte
Sim, acabei trapaceando NEWID() as id, mas é a mesma ideia. E existem casos de uso legítimos - se você tem uma exibição somente leitura, por exemplo. Feio, EF, feio.
ruffin
4

O atual gerador de Entity Framework EDM criará uma chave composta de todos os campos não nulos na sua exibição. Para obter controle sobre isso, você precisará modificar a exibição e as colunas da tabela subjacente, definindo as colunas como nulas quando não desejar que elas façam parte da chave primária. O oposto também é verdadeiro, como eu descobri, a chave gerada pelo EDM estava causando problemas de duplicação de dados, então tive que definir uma coluna anulável como não anulável para forçar a chave composta no EDM a incluir essa coluna.

Annagram
fonte
Temos o mesmo problema com a PK inferida, a entidade retorna registros duplicados e é completamente irritante. Se você executar Context.Entity.ToList()registros duplicados, mas se você executar a Consulta SQL gerada pelo EF diretamente (obtida com o LINQPad), nenhuma duplicação de registro ocorrerá. Parece ser um problema ao mapear os registros do banco de dados para os objetos de entidade (POCO) retornados, pois a PK é inferida usando a lógica explicada (colunas não anuláveis).
David Oliván Ubieto 22/02
3

Parece que é um problema conhecido com o EdmGen: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/12aaac4d-2be8-44f3-9448-d7c659585945/

RBarryYoung
fonte
Isso faz sentido. Portanto, existe uma maneira de definir uma coluna como não nula ou nula em uma exibição da maneira como a definimos?
Sergio Romero
1
Desculpe, já estou além do meu nível de conhecimento no Entity Framework. :-)
RBarryYoung
1
Alguém sabe quando esse problema será corrigido? É irritante ter que contornar isso quando você tiver colunas não nulas que não são chaves primárias.
live-love
3

Para obter uma visualização, eu tinha que mostrar apenas uma coluna de chave primária. Criei uma segunda visualização que apontava para a primeira e usei NULLIF para tornar os tipos anuláveis. Isso funcionou para eu fazer a EF pensar que havia apenas uma única chave primária na exibição.

Não tenho certeza se isso irá ajudá-lo, já que não acredito que a EF aceite uma entidade sem chave primária.

Nick Gotch
fonte
3

Se você não quiser mexer com a chave primária, recomendo:

  1. Incorpore ROW_NUMBERem sua seleção
  2. Defina como chave primária
  3. Defina todas as outras colunas / membros como não principais no modelo
Santhos
fonte
1

Devido aos problemas mencionados acima, prefiro funções de valor de tabela.

Se você tem isso:

CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something

crie isso:

CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView])

Então você simplesmente importa a função e não a vista.

Raio
fonte
2
Como você criaria associações entre entidades que adotam essa abordagem? É possível?
ggderas