2016-02-17 6 views
0

Сценарий: приложение для интрасети. Проверка подлинности Windows. EF 6.1.3. Базы данных: SQL Server Compact Edition и MS Access. VS-студия 2013.Проблема Entity Framework при сохранении объекта объекта

Решение имеет 3 проекта:

EnqueteWeb.UI - веб-приложение ASP.NET; EnqueteWeb.Dominio - библиотека классов для домена приложения; Библиотека ControleDeAcessoGeral для получения данных пользователя, зарегистрированного в Active Directory, и включает/обновлять/удалять/перечислять некоторые пользователи, которые выполняют некоторые специальные действия в приложении. Поскольку управление доступом к приложению основано на базе данных SQL Server Compact Edition, у меня есть EntityFramework, установленный в ControleDeAcessoGeral. Я хочу иметь все методы, касающиеся пользователей в классе в этом проекте. И я сделал это.

Это 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); 
     } 
    } 
} 

Действие, которое пытается сохранить пользователь (Usuarios.cs) в базе данных SQL Server CE в 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

Я просмотрел ваше сообщение несколько раз и не мог понять, какую ошибку вы видите. Можете ли вы быть более ясными с ошибкой? – Visser

+0

@ Виссер, извините за это. Я просто забыл написать сообщение об ошибке. Спасибо за внимание –

ответ

0

Проблема возникает из-за того, что вы не выбрасывайте первый DbContext экземпляра (из которого вы загружаете объекты в профиле) до присоединения указанных объектов на второй DbContext инстанции.

Чтобы исправить (и некоторые дополнительные предложения):

  1. имеет UsuariosApp реализовать IDisposable и утилизировать свой экземпляр DbContextdb при утилизации UsuariosApp
  2. обернуть недавно одноразовый UsuariosApp экземпляр в using заявлении и использовании этот единственный экземпляр для вашей Perfil загрузки и Usuario экономия логики
  3. оптимизация Perfil загрузки при загрузке все значения с одним вызовом
  4. валидации ModelState.IsValid сразу

Что-то вроде этого:

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); 
} 

Позвольте мне знать, если это не поможет решить вашу проблему.

+0

большое спасибо. Он отлично работал. И у меня все еще есть бонус: этот метод LocalizarPerfiles. –

Смежные вопросы