保存实体对象时的实体框架问题

时间:2016-02-17 17:25:17

标签: c# entity-framework

场景:Intranet应用。 Windows身份验证。 EF 6.1.3。数据库:SQL Server Compact Edition和MS Access。 VS Studio 2013。

该解决方案有3个项目:

EnqueteWeb.UI - ASP.NET Web应用程序; EnqueteWeb.Dominio - 应用程序域的类库; ControleDeAcessoGeral - 用于获取从Active Directory记录的用户数据的类库,并包含/更新/删除/列出对应用程序执行某些特殊操作的一些用户。 由于应用程序的访问控制基于SQL Server Compact Edition数据库,因此我在 ControleDeAcessoGeral 中安装了EntityFramework。我希望在这个项目中拥有关于类中用户的所有方法。所以我做到了。

ControleDeAcessoGeral 项目的定义如下:

Aplicacao
  - Corp.cs(处理Active Directory的方法)
  - UsuariosApp.cs(处理SQL Server CE数据库的方法)
Contexto
  - DBControleDeAcesso.cs(定义上下文)
  - InicializaControleDeAcesso.cs(填写初始数据到    DBControleDeAcesso数据库)
Entidades
  - Perfil.cs(用户可以在应用程序上拥有的配置文件)
  - Usuarios.cs(可能对应用程序执行某些操作的用户)
  - UsuarioAD.cs(Active Directory用户及其数据)

DBControleDeAcesso.cs类具有以下代码:

using ControleDeAcessoGeral.Models.Entidades;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ControleDeAcessoGeral.Models.Contexto
{
    public class DBControleDeAcesso : DbContext
    {
        public DBControleDeAcesso() : base("ControleDeAcessoContext") { }

        public DbSet<Perfil> Perfis { get; set; }
        public DbSet<Usuario> Usuarios { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }
}  

实体类如下:
    使用System.Collections.Generic;     使用System.ComponentModel.DataAnnotations;

namespace ControleDeAcessoGeral.Models.Entidades
{
    public class Usuario
    {
        [Key]
        public string Logon { get; set; }
        public string Nome { get; set; }
        [Display(Name="Órgão")]
        public string Orgao { get; set; }
        public string Email { get; set; }

        [StringLength(maximumLength: 4)]
        public string Depto { get; set; }

        [Display(Name = "Perfis")]
        public virtual List<Perfil> Perfis { get; set; }

        public Usuario()
        {
            this.Perfis = new List<Perfil>();
        }
    }
}  

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ControleDeAcessoGeral.Models.Entidades
{
    public class Perfil
    {
        [Key]
        public int Id { get; set; }

        [Required(ErrorMessage = "Por favor, informe o NOME DO perfil.")]
        [StringLength(maximumLength: 25)]
        public string Nome { get; set; }

        [StringLength(maximumLength: 255)]
        [Display(Name = "Descrição")]
        public string Descricao { get; set; }

        public virtual List<Usuario> Usuarios { get; set; }

        public Perfil()
        {
            this.Usuarios = new List<Usuario>();
        }
    }
}  

UsuariosApp.cs类如下(为了简洁起见,我只展示与该问题有关的方法):

using ControleDeAcessoGeral.Models.Contexto;
using ControleDeAcessoGeral.Models.Entidades;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;

namespace ControleDeAcessoGeral.Models.Aplicacao
{
    public class UsuariosApp
    {
        private DBControleDeAcesso db { get; set; }

        public UsuariosApp()
        {
            db = new DBControleDeAcesso();
        }

        public void SalvarUsuario(Usuario usuario)
        {
            db.Usuarios.Add(usuario);
            db.SaveChanges();
        }

        public Perfil LocalizarPerfil(int id)
        {
            return db.Perfis.Find(id);
        }
    }
}  

尝试在SQL Server CE数据库中保存用户(Usuarios.cs)的操作位于 AdministracaoController 中,并具有以下代码:

using ControleDeAcessoGeral.Models.Aplicacao;
using ControleDeAcessoGeral.Models.Entidades;
using EnqueteWeb.UI.Models;
using EnqueteWeb.UI.ViewModels;
using System.Linq;
using System.Web.Mvc;

namespace EnqueteWeb.UI.Controllers
{
    public class AdministracaoController : Controller
    {

        [HttpPost]
        public ActionResult CriarUsuarioNaApp(UsuarioViewModel model)
        {
            foreach (var item in model.PerfisSelecionados)
            {
                Perfil perfil = new UsuariosApp().LocalizarPerfil(item);
                model.Usuario.Perfis.Add(perfil);
            }
            if (ModelState.IsValid)
            {
                new UsuariosApp().SalvarUsuario(model.Usuario);
                return RedirectToAction("Usuarios");
            }
            return View(model);
        }
    }
}  

因此,当调用此操作 CriarUsuarioNaApp 并运行方法 SalvarUsuario(model.Usuario)时,会发生以下错误:

IEntityChangeTracker的多个实例无法引用实体对象

我在网上看过一些关于此事的内容,但不幸的是,我仍然无法使其发挥作用。

希望一个聪明善良的灵魂会指引我。

感谢您的关注。

Paulo Ricardo Ferreira

1 个答案:

答案 0 :(得分:0)

问题源于这样一个事实:在将所述实体附加到第二个DbContext实例之前,您没有丢弃第一个DbContext实例(从中加载配置文件实体)。

修复(及其他一些建议):

  1. UsuariosApp实施IDisposable并在处置DbContext
  2. 时处置您的db UsuariosApp实例
  3. 将新的一次性UsuariosApp实例包装在using语句中,并将此单个实例用于Perfil加载和Usuario保存逻辑
  4. 通过使用单个调用
  5. 加载所有值来优化Perfil加载
  6. 立即验证ModelState.IsValid
  7. 这样的事情:

    public class UsuariosApp : IDisposable
    {
        private DBControleDeAcesso db { get; set; }
    
        public UsuariosApp()
        {
            db = new DBControleDeAcesso();
        }
    
        public void SalvarUsuario(Usuario usuario)
        {
            db.Usuarios.Add(usuario);
            db.SaveChanges();
        }
    
        public Perfil LocalizarPerfil(int id)
        {
            return db.Perfis.Find(id);
        }
    
        public IEnumerable<Perfil> LocalizarPerfiles( IEnumerable<int> ids )
        {
            return db.Perfils.Where( p => ids.Contains( p.Id ) )
                .ToArray();
        }
    
        private bool _disposed = false;
    
        protected virtual void Dispose( bool disposing )
        {
            if( _disposed )
            {
                return;
            }
    
            if( disposing )
            {
                db.Dispose();
            }
    
            _disposed = true;
        }
    
        public void Dispose()
        {
            Dispose( true );
            GC.SuppressFinalize( this );
        }
    }
    
    public ActionResult CriarUsuarioNaApp( UsuarioViewModel model )
    {
        // validate model state first
        if( ModelState.IsValid )
        {
            // use single, disposable repo/uow instance
            using( var uapp = new UsuariosApp() )
            {
                // get all profiles in a single call, no loop required
                var perfils = uapp.LocalizarPerfiles( model.PerfisSelecionados );
    
                model.Usuario.Perfis.AddRange( perfils );
    
                uapp.SalvarUsuario( model.Usuario );
            }
    
            return RedirectToAction( "Usuarios" );
        }
    
        return View( model );
    }
    

    如果这不能解决您的问题,请告诉我。