2009-10-08 17 views
44

Napisałem własną niestandardową warstwę danych, aby utrwalić się w określonym pliku i wyodrębniłem ją za pomocą niestandardowego wzorca DataContext.Deklaracja zwrotu plonów wewnątrz bloku using() {} Usuwa przed wykonaniem

Wszystko opiera się na strukturze .NET 2.0 (dane ograniczenia dla serwera docelowego), więc nawet jeśli niektóre z nich mogą wyglądać jak LINQ-SQL, to nie! Właśnie zaimplementowałem podobny wzorzec danych.

Zobacz przykład poniżej, na przykład sytuacji, której nie mogę jeszcze wyjaśnić.

Aby uzyskać wszystkie instancje Animal - mogę to zrobić i działa metodę drobnych

public static IEnumerable<Animal> GetAllAnimals() { 
     AnimalDataContext dataContext = new AnimalDataContext(); 
      return dataContext.GetAllAnimals(); 
} 

A realizacja GetAllAnimals() w AnimalDataContext() poniżej

public IEnumerable<Animal> GetAllAnimals() { 
     foreach (var animalName in AnimalXmlReader.GetNames()) 
     { 
      yield return GetAnimal(animalName); 
     } 
} 

AnimalDataContext () implementuje IDisposable, ponieważ mam tam XmlTextReader i chcę się upewnić, że zostanie szybko oczyszczony.

Teraz gdybym owinąć pierwszego połączenia wewnątrz użyciu instrukcji jak tak

public static IEnumerable<Animal> GetAllAnimals() { 
     using(AnimalDataContext dataContext = new AnimalDataContext()) { 
      return dataContext.GetAllAnimals(); 
     } 
} 

i umieścić punkt przerwania na pierwszej linii AnimalDataContext.GetAllAnimals() metody i inny punkt przerwania w pierwszym linia w metodzie AnimalDataContext.Dispose() i wykonać ...

metoda Dispose() jest wywoływana PIERWSZA tak że AnimalXmlReader.GetNames() daje „nie odwołanie do obiektu ustawione na wystąpienie obiektu” wyjątek, ponieważ ma AnimalXmlReader został ustawiony na null w Dispose() ???

Jakieś pomysły? Mam przeczucie, że jego powrót związany wydajność nie pozwala się nazywać wewnątrz bloku try-catch, który korzystając skutecznie reprezentuje, gdy skompilowany ...

+0

Jest to w zasadzie jeden z problemów, z jaką się spotkałem, a także, patrz na moje pytanie tutaj: http://stackoverflow.com/questions/1524367 –

Odpowiedz

50

Po wywołaniu GetAllAnimals to faktycznie nie wykonaj dowolny kod, dopóki nie wyliczysz zwróconego IEnumerable w pętli foreach.

Kontekst danych jest usuwany, gdy tylko zwróci się metoda opakowania, przed wyliczeniem IEnumerable.

Najprostszym rozwiązaniem byłoby a sposób Wrapper iterację jak, na przykład:

public static IEnumerable<Animal> GetAllAnimals() { 
    using (AnimalDataContext dataContext = new AnimalDataContext()) { 
     foreach (var animalName in dataContext.GetAllAnimals()) { 
      yield return GetAnimal(animalName); 
     } 
    } 
} 

ten sposób using będą sporządzane w zewnętrznej iteracyjnej, i będzie umieszczony tylko po zewnętrzny iterator jest umieszczony.

Innym rozwiązaniem byłoby wyliczenie IEnumerable w opakowaniu. Najprostszym sposobem na to byłoby zwrócić List<Animal> coś takiego:

public static IEnumerable<Animal> GetAllAnimals() { 
    using (AnimalDataContext dataContext = new AnimalDataContext()) { 
     return new List<Animal>(dataContext.GetAllAnimals()); 
    } 
} 

Zauważ, że ten traci korzyści z realizacji odroczony, więc będzie uzyskać wszystkie zwierzęta, nawet jeśli ich nie potrzebują.

+1

Dzięki SLaks - druga opcja zrobiła sztuczkę i jej schludne - mniej linii kodu, mniej błędów !. To wywołanie przechodzi do "AnimalDataContextAdapter", który znajduje się w warstwie prezentacji dla projektu WebForms - w szczególności w celu wyliczenia kolekcji, więc nie ma prawdziwej straty w przypadku opóźnionego wykonania. –

+0

Wyjaśniłbym to jako: Nigdy nie akceptuj parametrów IDisposób w metodach, które używają zwrotu plonów lub innych odraczających metod wykonania. –

+0

Używanie z 'zwrotem plonów' jest bezpieczne i wywoływanie w strumieniu [IFF] (https: // www. google.com/search?q=define%20iff) kolekcja jest w pełni wyliczona. http://blogs.msdn.com/b/dancre/archive/2008/03/14/yield-and-usings-your-dispose-may-not-be-called.aspx – yzorg

10

Powodem tego jest to, że metoda GetAllAnimals nie zwraca kolekcjonowania zwierząt. Zwraca moduł wyliczający, który może zwrócić zwierzę w tym samym czasie.

Gdy zwrócisz wynik z wywołania GetAllAnimals wewnątrz bloku używając, po prostu zwróć moduł wyliczający. Blok używający udostępnia kontekst danych przed zakończeniem metody, a wtedy moduł wyliczający jeszcze nie odczytał żadnych zwierząt. Kiedy spróbujesz użyć modułu wyliczającego, nie można uzyskać żadnych zwierząt z kontekstu danych.

Obejście problemu polega na tym, że metoda GetAllAnimals powinna również utworzyć moduł wyliczający. W ten sposób za pomocą bloku nie będzie zamknięta aż do zaprzestania używania tego wyliczający:

public static IEnumerable<Animal> GetAllAnimals() { 
    using(AnimalDataContext dataContext = new AnimalDataContext()) { 
     foreach (Animal animal in dataContext.GetAllAnimals()) { 
     yield return animal; 
     } 
    } 
} 
Powiązane problemy