2015-06-10 13 views
5

Otrzymuję następujący błąd i ten błąd pojawia się tylko wtedy, gdy wielu użytkowników uderza w ten sam przycisk. Każda pomoc/pomysł będzie doceniana:Kolekcja błędów automappera została zmodyfikowana, gdy wielu użytkowników tworzy użytkownika.

System.InvalidOperationException: Kolekcja została zmodyfikowana; wyliczenie operacja nie może zostać wykonana. Generated: Wed, 10 czerwca 2015 07:29:06 GMT

AutoMapper.AutoMapperMappingException:

rodzaje mapowania: Użytkownik -> Użytkownik ApplicationSecurityManager.Service.User -> ApplicationSecurityManager.Models.User

Destination ścieżka: Użytkownik

Wartość źródła: ApplicationSecurityManager.Service.User ---> System.InvalidOperationException: Kolekcja została zmodyfikowana; wyliczenie operacja nie może zostać wykonana. w System.Collections.Generic.List 1.Enumerator.MoveNextRare() at AutoMapper.TypeMap.<get_AfterMap>b__1(Object src, Object dest) at AutoMapper.Mappers.TypeMapObjectMapperRegistry.PropertyMapMappingStrategy.Map(ResolutionContext context, IMappingEngineRunner mapper) at AutoMapper.Mappers.TypeMapMapper.Map(ResolutionContext context, IMappingEngineRunner mapper) at AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.Map(ResolutionContext context) --- End of inner exception stack trace --- at AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.Map(ResolutionContext context) at AutoMapper.MappingEngine.Map[TDestination](Object source, Action 1 opts) w ApplicationSecurityManager.UserManager.LoadUser nazwę użytkownika (ciąg) w ApplicationSecurityManager.UserManager.get_AuthenticatedUser() w ApplicationSecurityManager.UserManager.IsAuthenticated() w ApplicationSecurityManager.Infrastructure.ApplicationSecurityAttribute. OnAuthorization (AuthorizationContext filterContext) w System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters (controllerContext controllerContext, IList 1 filters, ActionDescriptor actionDescriptor) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.<BeginInvokeAction>b__1e(AsyncCallback asyncCallback, Object asyncState) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult 1.Begin (AsyncCallback zwrotna, stan obiektu, Int32 oczekiwania) w System.Web.Mvc.Async.AsyncResultWrapper. Rozpocznij [TResult] (AsyncCallback oddzwanianie, stan obiektu, BeginInvokeDelegate beginDelegate, EndInvokeDelegate 1 endDelegate, Object tag, Int32 timeout) at System.Web.Mvc.Async.AsyncResultWrapper.Begin[TResult](AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate 1 endDelegate, obiekt tag) w System.Web.Mvc.Controller. <> c__DisplayClass1d.b__17 (AsyncCallback AsyncCallback, obiekt asyncState) w System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult 1.Begin(AsyncCallback callback, Object state, Int32 timeout) at System.Web.Mvc.Async.AsyncResultWrapper.Begin[TResult](AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate 1 endDelegate, znacznika obiektu, czas oczekiwania Int32) w System.Web.Mvc.Controller.BeginExecuteCore (AsyncCallback zwrotna , stan Object) przy System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult 1.Begin(AsyncCallback callback, Object state, Int32 timeout) at System.Web.Mvc.Async.AsyncResultWrapper.Begin[TResult](AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate 1 endDelegate, znacznika obiektu, czas oczekiwania Int32) w System.Web.Mvc.Async.AsyncResultWrapper.Begin (AsyncCallback zwrotnego, stanu obiektu, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, Object tag) at System.Web.Mvc.Controller.BeginExecute (RequestContext requestContext, wywołanie zwrotne AsyncCallback, stan obiektu) na System.Web.Mvc.MvcHandler. <> c__DisplayClass8.b__2 (AsyncCallback AsyncCallback, obiekt asyncState) w System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult 1.Begin(AsyncCallback callback, Object state, Int32 timeout) at System.Web.Mvc.Async.AsyncResultWrapper.Begin[TResult](AsyncCallback callback, Object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate 1 endDelegate, znacznika obiektu, czas oczekiwania Int32) w System.Web.Mvc.Async.AsyncResultWrapper.Begin (AsyncCallback zwrotna, stanu obiektu, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, znacznik Object) przy System.Web.Mvc.MvcHandler.BeginProcessRequest (HttpContextBase HTTPContext, AsyncCallback zwrotnego, obiekt stan) w System.Web.HttpApplication.CallHandlerExecutionStep.System .Web.HttpApplication.IExecutionStep.Execute() w System.Web.HttpApplication.ExecuteStep (krok IExecutionStep, Boolean & completedSynchronously)

Jest to konstruktor, gdzie myślę, że aftermap jest problemem, ale podczas debugowania i nie pojawi się błąd.

Public Sub New(environmentCode As String, applicationCode As String) 
    MyBase.New(environmentCode, applicationCode) 

    SOBaseUrl = System.Configuration.ConfigurationManager.AppSettings(Enums.AppSettingKeys.SOBaseUrl.ToString()) 
    If Not String.IsNullOrEmpty(SOBaseUrl) Then 
     SOBaseUrl = SOBaseUrl.TrimEnd("/") 
    End If 

    'Setup mapping. 
    Mapper.CreateMap(Of Service.User, Models.User)() _ 
     .ForMember(Function(dest As Models.User) dest.ENumber, Sub(opt) opt.MapFrom(Function(src As Service.User) src.INumber)) _ 
     .AfterMap(Sub(src As Service.User, dest As Models.User) 

      dest.Groups = New List(Of String) 

      Using service = ApplicationSecurityManager.Service.Factory.GetService() 

       Dim applicationPermissions = service.LoadPermissionsForUser(dest.Username, MyBase.EnvironmentCode) 

       If (Not applicationPermissions Is Nothing AndAlso applicationPermissions.Any(Function(x) x.Code = MyBase.ApplicationCode)) Then 

        dest.Groups = applicationPermissions.Single(Function(x) x.Code = MyBase.ApplicationCode).GroupNames.ToList() 

       End If 

      End Using 

     End Sub) 

Depenendency wtryskowa Mapowanie:

container.RegisterType(Of IUserManager, UserManager)(New PerThreadLifetimeManager(), 
    New InjectionConstructor(
     ConfigurationManager.AppSettings(Common.Enums.AppSettingKeys.Environment.ToString()), 
     ConfigurationManager.AppSettings(Common.Enums.AppSettingKeys.ApplicationCode.ToString())) 
    ) 

W saveUserResponse, błąd jest wyrzucenie.

Public Function Create(user As Common.Models.User, approve As Boolean) As SO.Common.Models.User Implements IUserProvider.Save 

    Dim saveUserResponse = UserManager.SaveUser(Mapper.Map(Of ApplicationSecurityManager.Service.User)(user)) 

    If Not String.IsNullOrEmpty(saveUserResponse.ErrorMessage) Then 

     'The Security system returned an error. 

     Throw New Exception("Security Service returned error: " & saveUserResponse.ErrorMessage) 

    End If 

    'Return the username. 
    Return Mapper.Map(Of Common.Models.User)(saveUserResponse.User) 

End Function 

Odpowiedz

1

Jest to spowodowane, gdy wielu użytkowników modyfikować tej samej kolekcji user.Groups w metodzie AfterMap() swojego odwzorowania. Aby tego uniknąć, zablokuj kod, który przetwarza kolekcję. Na przykład:

'class level object 
private object _myLock = New object() 

'Setup mapping. 
Mapper.CreateMap(Of Service.User, Models.User)() _ 
    .ForMember(Function(dest As Models.User) dest.ENumber, Sub(opt) opt.MapFrom(Function(src As Service.User) src.INumber)) _ 
    .AfterMap(Sub(src As Service.User, dest As Models.User) 
        SyncLock _myLock 
         dest.Groups = New List(Of String) 

         Using service = ApplicationSecurityManager.Service.Factory.GetService() 

          Dim applicationPermissions = service.LoadPermissionsForUser(dest.Username, MyBase.EnvironmentCode) 

          If (Not applicationPermissions Is Nothing AndAlso applicationPermissions.Any(Function(x) x.Code = MyBase.ApplicationCode)) Then 

           dest.Groups = applicationPermissions.Single(Function(x) x.Code = MyBase.ApplicationCode).GroupNames.ToList() 

          End If 

         End Using 
        End SyncLock 

       End Sub) 
+0

Wykonałem ten krok, ale nieco inny od tego, co powiedziałeś: synclock _mylock Mapper.CreateMap (Of Service.User, Models.User)() _ .ForMember (Function (dest As Models.User) dest.ENumber, Sub (opt) opt.MapFrom (Funkcja (src As Service.User) src.INumber)) _ .Anapachap (Sub (src jako Service.User, dest As Models.User) ..code End Sub) endynclock. więc umieszczam wszystko w synchronizacji zamiast tylko kodu grupowego. Myślisz, że to właśnie tam tkwił problem? – Baahubali

+1

Dwie rzeczy. Pamiętaj, że metoda AfterMap() nie jest wywoływana podczas wywoływania CreateMap(), ale po wywołaniu Mapper.Map() i wywołuje skonfigurowane odwzorowanie. Więc SyncLock nie będzie skuteczny na swój sposób. Po drugie, należy zawsze blokować tak mało kodu jak to tylko możliwe. Jest to kolekcja obiektu użytkownika, która narzeka w czasie AfterMap, zgodnie ze stosem śledzenia - który wskazuje na .Groups.Dlatego zablokuj przetwarzanie tylko wokół tej kolekcji. –

+0

Dziękuję za wyjaśnienie tego. – Baahubali

1

Jest to dość powszechny błąd podczas modyfikowania kolekcji podczas iteracji. Czy to możliwe, że coś tu pozostawiasz, czego nie widzimy? W każdym razie w kodzie, który podałeś, nie ma takiego scenariusza z tego, co widzę. Oznacza to, że możesz wywoływać to w środowisku wielowątkowym, a kolekcja jest modyfikowana w innym wątku.

Co możesz spróbować?

Sprawdź, jak zablokować numer enumeration, aby uzyskać dostęp do tylko jednego wątku na raz. Możliwe, że dostęp do niego jest uzyskiwany wielokrotnie, gdy użytkownik kliknie przycisk.

Oto dobry link, aby pomóc: http://weblogs.asp.net/leftslipper/mvc-locking-the-routecollection

+0

Myślałem, ponieważ funkcje Automapper.CreateMap i AfterMap są wywoływane w konstruktorze klasy. jeśli jakiś użytkownik próbuje zapisać obiekt, wywołując funkcję utwórz jak pokazano powyżej, a inny użytkownik tworzy nowy obiekt, który wywoła Automappera w celu zmodyfikowania kolekcji, wywołując funkcję Aftermap, która spowodowałaby błąd kolekcji? Próbowałem nawet umieścić kod w synchronizacji, ale błąd się nie zmienia. myślałem o refaktoryzacji kodu, ale chciałem całkowicie zrozumieć, jak działa funkcja Aftermap. – Baahubali

+0

@ user1490835 'Aftermap' działa tylko raz na mapowanie. Jeśli chcesz uruchomić go tylko raz, powinieneś dołączyć go do konfiguracji odwzorowania List do listy, a nie do konfiguracji elementu do pozycji; to z pewnością spowodowałoby, że 'kolekcja został zmodyfikowany błąd' ... – Codexer

+0

przykro mi, co masz na myśli, mówiąc o mapowaniu listy do listy, a nie konfiguracji elementu do pozycji? – Baahubali