none
Erro: Relação Referencial resultará em referência cíclica - Entity Framework RRS feed

  • 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




    terça-feira, 23 de dezembro de 2014 12:36

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 required InstructorID 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

    terça-feira, 30 de dezembro de 2014 10:11