Questão
É possível definir uma restrição exclusiva em uma propriedade usando a sintaxe fluente ou um atributo? Caso contrário, quais são as soluções alternativas?
Eu tenho uma classe de usuário com uma chave primária, mas gostaria de garantir que o endereço de email também seja exclusivo. Isso é possível sem editar o banco de dados diretamente?
Solução (com base na resposta de Matt)
public class MyContext : DbContext {
public DbSet<User> Users { get; set; }
public override int SaveChanges() {
foreach (var item in ChangeTracker.Entries<IModel>())
item.Entity.Modified = DateTime.Now;
return base.SaveChanges();
}
public class Initializer : IDatabaseInitializer<MyContext> {
public void InitializeDatabase(MyContext context) {
if (context.Database.Exists() && !context.Database.CompatibleWithModel(false))
context.Database.Delete();
if (!context.Database.Exists()) {
context.Database.Create();
context.Database.ExecuteSqlCommand("alter table Users add constraint UniqueUserEmail unique (Email)");
}
}
}
}
ObjectContext
ouDbContext
.Respostas:
Até onde eu sei, não há como fazer isso com o Entity Framework no momento. No entanto, isso não é apenas um problema com restrições exclusivas ... você pode criar índices, verificar restrições e, possivelmente, gatilhos e outras construções também. Aqui está um padrão simples que você pode usar com sua configuração de primeiro código, embora seja certo que não é independente de banco de dados:
Outra opção é se o modelo de domínio for o único método de inserção / atualização de dados no banco de dados, você poderá implementar o requisito de exclusividade e deixar o banco de dados fora dele. Essa é uma solução mais portátil e obriga você a ter clareza sobre suas regras de negócios em seu código, mas deixa seu banco de dados aberto para que dados inválidos sejam recuperados.
fonte
SaveChanges()
), mas ainda há a possibilidade de outra inserção / atualização deslizar entre o horário da verificação de exclusividade e o horário deSaveChanges()
. Portanto, dependendo da missão crítica do aplicativo e da probabilidade de uma violação de exclusividade, provavelmente é melhor adicionar a restrição ao banco de dados.serializable isolation level
(ou bloqueio de tabela personalizado, ugh) realmente permitiria garantir a exclusividade em seu código. Mas a maioria das pessoas não usa issoserializable isolation level
por motivos de desempenho. O padrão no MS Sql Server éread committed
. Veja a série de 4 partes que começa em: michaeljswart.com/2010/03/…A partir do EF 6.1, agora é possível:
Isso fornecerá a você um índice exclusivo, em vez de uma restrição exclusiva, a rigor. Para os propósitos mais práticos, eles são os mesmos .
fonte
Não está realmente relacionado a isso, mas pode ajudar em alguns casos.
Se você deseja criar um índice composto exclusivo, digamos 2 colunas que funcionarão como uma restrição para sua tabela, a partir da versão 4.3, você pode usar o novo mecanismo de migração para alcançá-lo:
Basicamente, você precisa inserir uma chamada como essa em um dos seus scripts de migração:
Algo parecido:
fonte
DropIndex("TableName", new[] { "Column1", "Column2" });
Eu faço um hack completo para executar o SQL quando o banco de dados está sendo criado. Crio meu próprio DatabaseInitializer e herdo de um dos inicializadores fornecidos.
Esse é o único lugar que eu poderia encontrar para inserir em minhas instruções SQL.
Isso é do CTP4. Não sei como funciona no CTP5.
fonte
Apenas tentando descobrir se havia uma maneira de fazer isso, a única maneira que encontrei até agora era aplicá-la, criei um atributo a ser adicionado a cada classe em que você fornece o nome dos campos que precisa ser exclusivo:
Então, na minha turma, vou adicioná-lo:
Por fim, adicionarei um método no meu repositório, no método Add ou ao salvar alterações como esta:
Não é tão bom quanto precisamos confiar na reflexão, mas até agora é a abordagem que funciona para mim! = D
fonte
Também na 6.1, você pode usar a versão fluente da sintaxe da resposta de @ mihkelmuur da seguinte maneira:
O método fluente não é perfeito IMO, mas pelo menos é possível agora.
Mais informações sobre o blog Arthur Vickers http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/
fonte
Uma maneira fácil no visual basic usando as primeiras migrações de código EF5
Amostra de classe pública
Classe final
O atributo MaxLength é muito importante para o índice exclusivo do tipo de sequência
Execute o cmd: update-database -verbose
depois de executar o cmd: add-migration 1
no arquivo gerado
fonte
Semelhante à resposta de Tobias Schittkowski, mas em C # e tem a capacidade de ter vários campos nas restrições.
Para usar isso, basta colocar um [Único] em qualquer campo que você deseja que seja único. Para strings, você terá que fazer algo como (observe o atributo MaxLength):
porque o campo de sequência padrão é nvarchar (max) e isso não será permitido em uma chave.
Para vários campos na restrição, você pode:
Primeiro, o UniqueAttribute:
Em seguida, inclua uma extensão útil para obter o nome da tabela do banco de dados de um tipo:
Em seguida, o inicializador do banco de dados:
fonte
Eu resolvi o problema pela reflexão (desculpe pessoal, VB.Net ...)
Primeiro, defina um atributo UniqueAttribute:
Em seguida, aprimore seu modelo como
Por fim, crie um DatabaseInitializer personalizado (na minha versão, recrio as alterações de banco de dados no banco de dados somente se estiver no modo de depuração ...). Nesse DatabaseInitializer, os índices são criados automaticamente com base nos atributos exclusivos:
Talvez isso ajude ...
fonte
Se você substituir o método ValidateEntity na sua classe DbContext, poderá colocar a lógica também. A vantagem aqui é que você terá acesso total a todos os seus DbSets. Aqui está um exemplo:
fonte
Se você estiver usando o EF5 e ainda tiver essa dúvida, a solução abaixo resolveu o problema para mim.
Estou usando a primeira abordagem de código, portanto, colocando:
no script de migração fez o trabalho bem. Também permite valores NULL!
fonte
Com a abordagem EF Code First, é possível implementar o suporte a restrições exclusivas com base em atributos usando a seguinte técnica.
Crie um atributo de marcador
Marque as propriedades que você deseja que sejam únicas nas entidades, por exemplo
Crie um inicializador de banco de dados ou use um existente para criar restrições exclusivas
Defina o contexto do banco de dados para usar este inicializador no código de inicialização (por exemplo, em
main()
ouApplication_Start()
)A solução é semelhante à do mheyman, com uma simplificação de não suporte a chaves compostas. Para ser usado com EF 5.0+.
fonte
Solução Api fluente:
fonte
Enfrentei esse problema hoje e finalmente consegui resolvê-lo. Não sei se é uma abordagem correta, mas pelo menos posso continuar:
fonte
Use um validador de propriedade exclusivo.
ValidateEntity
não é chamado na mesma transação do banco de dados. Portanto, pode haver condições de corrida com outras entidades no banco de dados. Você precisa invadir o EF para forçar uma transação em torno doSaveChanges
(e, portanto,ValidateEntity
).DBContext
não pode abrir a conexão diretamente, masObjectContext
pode.fonte
De acordo com http://blogs.msdn.com/b/adonet/archive/2014/02/11/ef-6-1-0-beta-1-available.aspx , o EF 6.1 terá um IndexAttribute para nos ajudar .
fonte
Depois de ler esta pergunta, tive minha própria pergunta no processo de tentar implementar um atributo para designar propriedades como chaves exclusivas, como as respostas de Mihkel Müür , Tobias Schittkowski e mheyman sugerem: Mapear propriedades de código do Entity Framework para colunas de banco de dados (CSpace para SSpace)
Finalmente, cheguei a essa resposta, que pode mapear propriedades escalares e de navegação até as colunas do banco de dados e criar um índice exclusivo em uma sequência específica designada no atributo. Esse código pressupõe que você implementou um UniqueAttribute com uma propriedade Sequence e o aplicou às propriedades da classe de entidade EF que devem representar a chave exclusiva da entidade (exceto a chave primária).
Nota: Este código depende da EF versão 6.1 (ou posterior), que
EntityContainerMapping
não está disponível nas versões anteriores.fonte
Para aqueles que usam as primeiras configurações de código, você também pode usar o objeto IndexAttribute como um ColumnAnnotation e definir sua propriedade IsUnique como true.
No exemplo:
Isso criará um índice exclusivo chamado IX_name na coluna Nome.
fonte
Desculpe pela resposta tardia, mas achei bom tê-lo com você
Eu postei sobre isso no projeto de código
Em geral, depende dos atributos que você coloca nas classes para gerar seus índices exclusivos
fonte