2014-01-07 3 views
0

Я ценю, если вы могли бы помочь мне понять, почему ObjectDisposedException выбрасывается, когда я удаляю проверку lodging.DestinationId != 0 с DbContext.ValidateLodging(), приведенную ниже.
Пользовательская проверка в DbContext.ValidateEntity throws ObjectDisposedException

Образец кода, приведенный ниже, представляет собой Программирование Entity Framework от Julie - DbContext, и его можно использовать для воспроизведения исключения.

Окружающая среда: Visual Studio 2012 с Entity Framework 6.0.2

РЕДАКТИРОВАНИЕ 7/1: Включено StackTrace из ObjectDisposedException.

DbContext реализация & тестовых данных

namespace DataAccess 
{ 

public class BagaContext : DbContext 
{ 
    public DbSet<Destination> Destinations { get; set; } 
    public DbSet<Lodging> Lodgings { get; set; } 

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) 
    { 
     var result = base.ValidateEntity(entityEntry, items); 

     if (result.IsValid) 
     { 
      ValidateLodging(result); 
     } 
     return result; 
    } 

    private void ValidateLodging(DbEntityValidationResult result) 
    { 
     var lodging = result.Entry.Entity as Lodging; 

     // PROBLEM: Removing lodging.DestinationId != 0 causes ObjectDisposedException. 
     if (lodging != null && lodging.DestinationId != 0) 
     { 
      if (Lodgings.Any(l => l.Name == lodging.Name && 
      l.DestinationId == lodging.DestinationId)) 
      { 
       result.ValidationErrors.Add(
       new DbValidationError(
       "Lodging", 
       "There is already a lodging named " + lodging.Name + 
       " at this destination.") 
       ); 
      } 
     } 
    } 

    public BagaContext() 
    { 
     Database.SetInitializer<BagaContext>(new InitializeBagaDatabaseWithSeedData()); 
    } 
} 



public class InitializeBagaDatabaseWithSeedData : DropCreateDatabaseAlways<BagaContext> 
    { 
     protected override void Seed(BagaContext context) 
     { 
      context.Destinations.Add(new Destination 
      { 
       Name = "Grand Canyon", 
       Lodgings = new List<Lodging> 
        { 
         new Lodging {Name = "Grand Hotel"}, 
         new Lodging {Name = "Dave's Dump"} 
        } 
      }); 
     } 
    } 

}

Код для проверки реализации DbContext.ValidateEntity

namespace Client 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     CreateDuplicateLodging(); 
     Console.ReadLine(); 
    } 

    private static void CreateDuplicateLodging() 
    { 
     using (var context = new BagaContext()) 
     { 
      var destination = context.Destinations.FirstOrDefault(d => d.Name == "Grand Canyon"); 
      try 
      { 
       context.Lodgings.Add(new Lodging 
       { 
        Destination = destination, 
        Name = "Grand Hotel" 
       }); 
       context.SaveChanges(); 
       Console.WriteLine("Save Successful"); 
      } 
      catch (DbEntityValidationException ex) 
      { 
       Console.WriteLine("Save Failed: "); 
       foreach (var error in ex.EntityValidationErrors) 
       { 
        Console.WriteLine(
        string.Join(Environment.NewLine, 
        error.ValidationErrors.Select(v => v.ErrorMessage))); 
       } 
      } 
     } 
    } 
} 

}

Модель классы

namespace Model 
{ 
[Table("Locations", Schema = "baga")] 
public class Destination 
{ 
    public Destination() 
    { 
     this.Lodgings = new List<Lodging>(); 
    } 

    [Column("LocationID")] 
    public int DestinationId { get; set; } 
    [Required, Column("LocationName")] 
    [MaxLength(200)] 
    public string Name { get; set; } 
    public virtual List<Lodging> Lodgings { get; set; } 
} 

public class Lodging 
{ 
    public int LodgingId { get; set; } 
    [Required] 
    [MaxLength(200)] 
    [MinLength(10)] 
    public string Name { get; set; } 
    [Column("destination_id")] 
    public int DestinationId { get; set; } 
    public Destination Destination { get; set; } 
} 

}

StackTrace из ObjectDisposedException

at System.Data.Entity.Core.Objects.ObjectContext.get_Connection() 
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) 
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0() 
at System.Lazy`1.CreateValue() 
at System.Lazy`1.LazyInitValue() 
at System.Lazy`1.get_Value() 
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext() 
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source) 
at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__3[TResult](IEnumerable`1 sequence) 
at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot) 
at System.Data.Entity.Core.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[TResult](Expression expression) 
at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression) 
at System.Linq.Queryable.Any[TSource](IQueryable`1 source, Expression`1 predicate) 
at DataAccess.BagaContext.ValidateLodging(DbEntityValidationResult result) in c:\Users\User\Documents\Visual Studio 2012\Projects\Learning\EF\StackOverflowQuestions\DataAccess\BreakAwayContext.cs:line 38 
at DataAccess.BagaContext.ValidateEntity(DbEntityEntry entityEntry, IDictionary`2 items) in c:\Users\User\Documents\Visual Studio 2012\Projects\Learning\EF\StackOverflowQuestions\DataAccess\BreakAwayContext.cs:line 26 
at System.Data.Entity.DbContext.GetValidationErrors() 
at System.Data.Entity.Internal.InternalContext.SaveChanges() 
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() 
at System.Data.Entity.DbContext.SaveChanges() 
at Client.Program.CreateDuplicateLodging() in c:\Users\User\Documents\Visual Studio 2012\Projects\Learning\EF\StackOverflowQuestions\Client\Program.cs:line 34 
at Client.Program.Main(String[] args) in c:\Users\User\Documents\Visual Studio 2012\Projects\Learning\EF\StackOverflowQuestions\Client\Program.cs:line 18 
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
at System.Threading.ThreadHelper.ThreadStart() 

Спасибо.

+0

Можете ли вы включить трассировку стека объекта ObjectDisposedException? –

+0

@SteveRuble: добавлен StackTrace. Благодарю. – RanC

+0

'lodging.DestinationId! = 0' - это красная селедка (DestinationId всегда будет 0, потому что вы только установили' lodging.Destination' и экземпляр 'Lodging' не проксирован (т. Е. Вы создали его с' new' так что это POCO, у которого нет логики для установки 'DestinationId', когда установлено свойство« Destination »)). Вместо этого вы должны проверить 'lodging.Destination! = Null'. Однако это не объясняет исключение. Я все еще пытаюсь понять это (я думаю, что это как-то связано с выдачей запроса из системы проверки). –

ответ

0

Я нашел несколько хакерское решение, но я точно не знаю, в чем проблема. К счастью, это, вероятно, ошибка, вызванная тестированием, поскольку вы, вероятно, не планируете использовать DropCreateDatabaseAlways вне контекста тестирования.

Проблема исчезает, если вы не выполняете никаких запросов во время процесса посева. Один из способов сделать это - предотвратить проверку до тех пор, пока SaveChanges не завершится хотя бы один раз в домене приложения (SaveShanges вызывается во время посева.) A (возможно, более) простой способ сделать это: изменить BagaContext следующим образом:

private static bool _seedingComplete; 
public override int SaveChanges() 
{ 
    var result = base.SaveChanges(); 
    _seedingComplete = true; 
    return result; 
} 

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) 
{ 
    var result = base.ValidateEntity(entityEntry, items); 

    if (_seedingComplete && result.IsValid) 
    { 
     ValidateLodging(result); 
    } 
    return result; 
} 

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

Изменить, чтобы добавить дополнительные пояснения:

Причина, что положение lodging.DestinationId != 0 в, если заявление в ValidateLodging имеет эффект предотвращения ошибки в том, что во время высева все назначения записи будут иметь идентификатор 0 , потому что они еще не были вставлены.Поскольку DestinationId всегда будет 0 во время посева, это условие предотвращает выполнение запроса Lodgings.Any(...).

Одной из причин этой проблемы было очень трудно для меня, чтобы понять, что Lodgings.Any(...) запрос во время высева не причины, за исключением быть выброшен, но делает разорвать Lodgings DbSet так, что она не может быть позже используется для реальных запросов. Мне потребовалось некоторое время, чтобы выяснить, где DbSet разрывается.

+0

@SteveRubble: Прежде всего, спасибо за внимание. Как вы сказали, это похоже на ошибку, вызванную процессом посева. Я думал, что это нормально, чтобы EF воссоздал БД (для целей тестирования) в одном и том же блоке использования. то есть. Эта ошибка может быть устранена путем воссоздания БД в другом блоке использования до выполнения вышеуказанного клиентского кода. Но я до сих пор не понимаю, как проверка на пребывание. DestinationId! = 0 предотвращает это исключение. Я рассмотрю это дальше и опубликую, если найду что-нибудь интересное. Благодарю. – RanC

+0

@RanC, хороший вызов - создание и удаление другого DbContext, вероятно, является лучшим решением, чем попытка отслеживать состояние посева внутри контекста. –

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