Usuário com melhor resposta
Erro: Relação Referencial resultará em referência cíclica - Entity Framework

Pergunta
-
Salve!
Aplicação web, VS2013 Express, usando EF 6.1.1 e SQL Server Compact Edition.
Na aplicação estou usando Code-First e tenho, dentre outras, essas 3 classes:
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Orcamento.Dominio.Models.Entidades.Suporte { [Table("ItensFinanceiros")] public class ItemFinanceiro { [Key] [Required(ErrorMessage="Por favor, informe o código do Item Financeiro.")] [Display(Name="Código")] public string ItemFinanceiroId { get; set; } [Required(ErrorMessage="Por favor, informe o título do Item Financeiro.")] [Display(Name="Título")] public string Titulo { get; set; } [Required(ErrorMessage="Por favor, informe a que tipo de orçamento se refere o Item Financeiro.")] [Display(Name="Tipo de Orçamento")] public int TipoOrcamentoId { get; set; } [Required(ErrorMessage="Por favor, informe o tipo de gasto do Item Financeiro.")] [Display(Name="Tipo de Gasto")] public int TipoGastoId { get; set; } [NotMapped] [Display(Name = "Item Financeiro")] public string IdTitulo { get { return ItemFinanceiroId + " - " + Titulo; } } [ForeignKey("TipoOrcamentoId")] public virtual TipoDeOrcamento TipoDeOrcamento { get; set; } [ForeignKey("TipoGastoId")] public virtual TipoDeGasto TipoDeGasto { get; set; } } }
using Orcamento.Dominio.Models.Entidades.Suporte; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Orcamento.Dominio.Models.Entidades.Negocio { [Table("ItensDoEstrutural")] public class ItemDeEstrutural { [Key] public int Id { get; set; } public int EstruturalId { get; set; } [Display(Name = "Exercício")] public int Exercicio { get; set; } [Display(Name = "Versão")] public string VersaoCod { get; set; } [Display(Name = "Órgão")] public string OrgaoSigla { get; set; } [Display(Name = "Centro de Custo")] [Required(ErrorMessage = "Por favor, informe o Centro de Custo.")] public string CentroDeCustoCod { get; set; } [Required(ErrorMessage = "Por favor, informe o valor.")] [Column(TypeName = "money")] public decimal Valor { get; set; } [Required(ErrorMessage = "Por favor, informe a descrição do item.")] [Display(Name = "Descrição")] public string Descricao { get; set; } [Required(ErrorMessage = "Por favor, informe a justificativa.")] [DataType(DataType.MultilineText)] public string Justificativa { get; set; } public string Impacto { get; set; } public string Status { get; set; } public string InformadoPor { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")] public DateTime DtInformacao { get; set; } [Required(ErrorMessage = "Por favor, informe a Classe de Custo.")] [Display(Name = "Classe de Custo")] public string ClasseDeCustoId { get; set; } [Required(ErrorMessage = "Por favor, informe o Item Financeiro.")] [Display(Name = "Item Financeiro")] public string ItemFinanceiroId { get; set; } [ForeignKey("VersaoCod")] public virtual Versao Versao { get; set; } [ForeignKey("OrgaoSigla")] public virtual Orgao Orgao { get; set; } [ForeignKey("CentroDeCustoCod")] public virtual CentroDeCusto CentroDeCusto { get; set; } [ForeignKey("ClasseDeCustoId")] public virtual ClasseDeCusto ClasseDeCusto { get; set; } [ForeignKey("ItemFinanceiroId")] public virtual ItemFinanceiro ItemFinanceiro { get; set; } [Required(ErrorMessage = "Por favor, informe o Tipo de Gasto.")] [Display(Name="Tipo de gasto")] public int TipoDeGastoId { get; set; } [ForeignKey("TipoDeGastoId")] public virtual TipoDeGasto TipoDeGasto { get; set; } } }
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Orcamento.Dominio.Models.Entidades.Negocio { [Table("Estrutural")] public class Estrutural { [Key] public int EstruturalId { get; set; } [ConcurrencyCheck] [Required(ErrorMessage = "Por favor, informe o ano a que se refere este orçamento.")] [Display(Name = "Exercício")] public int Exercicio { get; set; } [Required(ErrorMessage = "Por favor, informe a versão do orçamento.")] [Display(Name = "Versão")] public string VersaoCod { get; set; } [Display(Name = "Data de início")] [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")] public DateTime DtInicio { get; set; } [Display(Name = "Iniciado por")] public string InicioPor { get; set; } [Display(Name="Total Estimado")] [Column(TypeName = "money")] public decimal? SomaEstimada { get; set; } [Display(Name="Total Aprovado")] [Column(TypeName = "money")] public decimal? SomaAprovada { get; set; } [ForeignKey("VersaoCod")] public virtual Versao Versao { get; set; } public virtual ICollection<ItemDeEstrutural> Itens { get; set; } } }
A hierarquia desejada entre as classes é conforme a imagem a seguir (versão express - não tenho o visualizador do diagrama de classes):
Ou seja, 1 registro de Estrutural está associado a n registros de ItemDeEstrutural que está associado a 1 ItemFinanceiro, mas 1 ItemFinanceiro está associado a n ItemdeEstrutural.
Para gerar o banco a partir das classes pelo EF tenho a seguinte classe:
using Orcamento.Dominio.Models.Entidades.Negocio; using Orcamento.Dominio.Models.Entidades.Suporte; using System.Data.Entity; namespace Orcamento.Dominio.Models.Contexto { public class DbOrcamento : DbContext { #region Tabelas Suporte public DbSet<Versao> Versoes { get; set; } public DbSet<Orgao> Orgaos { get; set; } public DbSet<TipoDeGasto> TiposDeGasto { get; set; } public DbSet<Equipamento> Equipamentos { get; set; } public DbSet<CentroDeCusto> CentrosDeCusto { get; set; } public DbSet<ProgramaDeInvestimento> ProgramasDeInvestimento { get; set; } public DbSet<OrdemInterna> OrdensInternas { get; set; } public DbSet<TipoDeOrcamento> TiposDeOrcamento { get; set; } public DbSet<ItemFinanceiro> ItensFinanceiros { get; set; } public DbSet<ClasseDeCusto> ClassesDeCusto { get; set; } #endregion #region Tabelas Negócio public DbSet<Estrutural> Estrutural { get; set; } public DbSet<ItemDeEstrutural> ItensDeEstrutural { get; set; } #endregion } }
O que está acontecendo é que quando rodo a aplicação recebo o seguinte erro e o banco, logicamente, não é criado:
Erro de Servidor no Aplicativo '/'.
A relação referencial resultará em uma referência cíclica que não é permitida. [ Constraint name = FK_dbo.ItensDoEstrutural_dbo.ItensFinanceiros_ItemFinanceiroId ]
Agora, se, na classe Estrutural, eu excluo a propriedade de navegação referente aos itens do estrutural (linha abaixo):
public virtual ICollection<ItemDeEstrutural> Itens { get; set; }
e, na classe DbOrcamento (criação do banco) excluo a linha
public DbSet<ItemDeEstrutural> ItensDeEstrutural { get; set; }
o banco é criado e a aplicação roda.
Qual erro que estou cometendo? O que está errado na minha estrutura de classes?
Agradeço a atenção e desejo um Feliz Natal a todos.
Paulo Ricardo Ferreira
- Editado PRicardo Ferreira terça-feira, 23 de dezembro de 2014 15:06 correção
Respostas
-
Salve!
Na busca pela solução desse problema, acabei encontrando este tutorial.
Na nota que reproduzo abaixo encontrei o que acabou sendo a solução para o problema que enfrentava:
Note By convention, the Entity Framework enables cascade delete for non-nullable foreign keys and for many-to-many relationships. This can result in circular cascade delete rules, which will cause an exception when you try to add a migration. For example, if you didn't define the
Department.InstructorID
property as nullable, you'd get the following exception message: "The referential relationship will result in a cyclical reference that's not allowed." If your business rules requiredInstructorID
property to be non-nullable, you would have to use the following fluent API statement to disable cascade delete on the relationship:Assim, baseando-me nesta nota, acrescentei o método abaixo à minha classe DbOrcamento.cs:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<ItemDeEstrutural>() .HasRequired(d => d.ItemFinanceiro).WithMany().WillCascadeOnDelete(false); }
Agradeço a todos que leram a minha pergunta para tentar me ajudar e desejo a todos nós um Feliz Ano Novo!
Paulo Ricardo Ferreira
- Marcado como Resposta PRicardo Ferreira terça-feira, 30 de dezembro de 2014 10:11