Контекст структуры объекта и расположение карты структуры - PullRequest
3 голосов
/ 08 сентября 2011

У меня странная проблема с удалением соединения с платформой сущностей в приложении asp.net mvc.

У меня есть простая структура, например:

Entity:

public class EmployeeReport
{
    public int EmployeeReportId { get; set; }

    public DateTime Created { get; set; }

    public Decimal Hours { get; set; }

    public string Comment { get; set; }

    public int EmployeeId { get; set; }

    public int ContractId { get; set; }

    public int ServiceId { get; set; }

    public virtual ReportContract Contract { get; set; }

    public virtual ReportService Service { get; set; }

    public virtual Employee Employee { get; set; }

}

Entitymapper:

public class EmployeeReportMapper : EntityTypeConfiguration<EmployeeReport>
{
    public EmployeeReportMapper()
    {
        ToTable("intranet_employee_reports");
        HasKey(x => x.EmployeeReportId);

        Property(x => x.Created).HasColumnName("Created").IsRequired();
        Property(x => x.Comment).HasColumnName("Comment").IsOptional();
        Property(x => x.Hours).HasColumnName("Hours").IsRequired();

        HasRequired(x => x.Employee).WithMany().HasForeignKey(x => x.EmployeeId);
        HasRequired(x => x.Service).WithMany().HasForeignKey(x => x.ServiceId);
        HasRequired(x => x.Contract).WithMany().HasForeignKey(x => x.ServiceId);
    }
}

DbContext - интерфейс

public interface IDbContext : IDisposable
{
    IDbSet<EmployeeReport> EmployeeReports { get; }
}

DbContext - реализация

public class IntranetDbContext : DbContext,IDbContext
{
    public IDbSet<EmployeeReport> EmployeeReports { get; set; }
    ...


    public IntranetDbContext() : base("IntranetDb")
    {
        Database.SetInitializer<IntranetDbContext>(null); 
    }

    public void Commit()
    {
        SaveChanges();
    }

    public void ChangeEntityState(object entity, EntityState entityState)
    {
        Entry(entity).State = entityState;
    }

    public void ExecuteSql(string query, SqlParameterCollection parameterCollection)
    {
        Database.ExecuteSqlCommand(query, parameterCollection);
    }


    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        /* Register custom mapping class */
        modelBuilder.Configurations.Add(new EmployeeReportMapper());
         ....
        base.OnModelCreating(modelBuilder);
    }

}

Наконец, моя конфигурация структурной карты:

public class CoreRegistry : Registry
{
    public CoreRegistry()
    {
        For<IDbContext>().HttpContextScoped().Use<IntranetDbContext>();
        ...
    }
}

и Global.asax:

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
    }

Хорошо, теперь проблема, в моем приложении я использую стандартное конструирование внедрения зависимостей или вызываю ObjectFactory.GetInstance ().

В одном из моих контроллеров я вызываюкласс обслуживания, который имеет доступ к dbcontext и извлекает некоторые объекты.

К сожалению, я получаю классическое исключение:

Экземпляр ObjectContext был удален и больше не может использоваться для операций, которые требуютсоединение.

Это странно, потому что служба вызывается во время запроса и все данные передаются клиенту в контроллере ...

Любая идея, где я ошибаюсьвзять?

РЕДАКТИРОВАТЬ:

Сервисный код:

public class EmployeeService : IEmployeeService
{
    /// <summary>
    /// IDbContext reference
    /// </summary>
    private readonly IDbContext _dbContext;

    public EmployeeService(IDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public List<Employee> GetSubordinateEmployees(Employee employee)
    {
        List<Employee> employees = new List<Employee>();

        foreach (var unit in employee.OrganizationUnits.ToList()) /* throw exception*/
        {
            foreach (var childrenUnit in unit.ChildrenUnits)
            {
                employees.AddRange(childrenUnit.Employees);
            }
        }
        return employees.Distinct().ToList();
    }

Контроллер:

    private readonly IEmployeeService _employeeService;

    public EmployeeReportController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    [HttpGet]
    public ActionResult SearchReports()
    {
        List<Employee> employees = _employeeService.GetSubordinateEmployee(IntranetSession.Current.LoggedAccount.Employee).ToList(); // Exception!
       ...

        return View();
    }


}   

1 Ответ

1 голос
/ 08 сентября 2011

Ваш код вообще не использует текущий DbContext.Проблема:

IntranetSession.Current.LoggedAccount.Employee

Затем:

employee.OrganizationUnits.ToList()

Ваш сотрудник, сохраненный в сеансе, был загружен с контекстом, который уже удален, но он все еще сохраняет ссылку на этот контекст.Когда вы загрузили этого сотрудника, вы не стали загружать его организации, поэтому, как только вы получите к ней доступ OrganizationUnits, он попытается запустить отложенную загрузку в удаленном контексте.

Есть два способа избежать этой проблемы:

  • Стремитесь загрузить всю информацию, которую вам нужно использовать, от вашего сотрудника, хранящуюся в сеансе.Это означает получение сотрудника, например context.Employees.Include(e => e.OrganizationUnits).Single(...)
  • Сохранение только идентификатора сотрудника в сеансе и загрузка необходимых данных по требованию

Если вы хотите кэшировать всего сотрудника в сеансе, убедитесь, что выотключит создание прокси для объектов, хранящихся в сеансе, вызвав:

 context.Configuration.ProxyCreationEnabled = false;

Это гарантирует, что кэшированные данные не будут сохранять ссылку на удаленный контекст (что, между прочим, не позволит сборщику мусора собирать контекст и все объекты, на которые он ссылается).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...