2015-06-10 8 views
5

Ricevo l'errore seguente e questo errore si verifica solo quando più utenti stanno premendo lo stesso pulsante. Qualsiasi aiuto/idea sarà molto apprezzato:La raccolta degli errori di automapper è stata modificata quando più utenti creano un utente

System.InvalidOperationException: Collection è stato modificato; l'operazione di enumerazione potrebbe non essere eseguita. Generated: Wed, 10 Giugno 2015 07:29:06 GMT

AutoMapper.AutoMapperMappingException:

tipi Mapping: utente -> Utente ApplicationSecurityManager.Service.User -> ApplicationSecurityManager.Models.User

Destinazione percorso: Utente

Valore sorgente: ApplicationSecurityManager.Service.User ---> System.InvalidOperationException: Collection è stato modificato; l'operazione di enumerazione potrebbe non essere eseguita. a 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 opta) a ApplicationSecurityManager.UserManager.LoadUser (String username) a ApplicationSecurityManager.UserManager.get_AuthenticatedUser() a ApplicationSecurityManager.UserManager.IsAuthenticated() a ApplicationSecurityManager.Infrastructure.ApplicationSecurityAttribute. OnAuthorization (AuthorizationContext filterContext) a 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 callback, oggetto di stato, Int32 timeout) a System.Web.Mvc.Async.AsyncResultWrapper. Inizia [TResult] (AsyncCallback callback, stato oggetto, 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, tag Object) a System.Web.Mvc.Controller. <> c__DisplayClass1d.b__17 (AsyncCallback asyncCallback, oggetto asyncState) a 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, tag Object, Int32 timeout) a System.Web.Mvc.Controller.BeginExecuteCore (AsyncCallback callback , oggetto di stato) a 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, tag Object, Int32 timeout) a System.Web.Mvc.Async.AsyncResultWrapper.Begin (AsyncCallback callback, stato oggetto, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, tag Object) a System.Web.Mvc.Controller.BeginExecute (RequestContext requestContext, Chiamata asincrona richiamata, stato oggetto) a System.Web.Mvc.MvcHandler. <> c__DisplayClass8.b__2 (AsyncCallback asyncCallback, oggetto asyncState) a 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, tag Object, Int32 timeout) a System.Web.Mvc.Async.AsyncResultWrapper.Begin (AsyncCallback callback, stato oggetto, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, tag Object) al System.Web.Mvc.MvcHandler.BeginProcessRequest (HttpContextBase HttpContext, AsyncCallback callback, oggetto di stato) a System.Web.HttpApplication.CallHandlerExecutionStep.System .Web.HttpApplication.IExecutionStep.Execute() a System.Web.HttpApplication.ExecuteStep (IExecutionStep passo, booleano & completedSynchronously)

Questo è il costruttore in cui penso che l'aftermap è il problema, ma in fase di debug non ricevo l'errore.

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 mappatura dell'iniezione:

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

Nel saveUserResponse, l'errore è sempre gettato.

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 

risposta

1

Ciò è causato quando più utenti modificano lo stesso collezione user.Groups nel metodo AfterMap() della tua mappatura. Per evitare ciò, bloccare il codice che sta elaborando la raccolta. Ad esempio:

'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

ho eseguito questo passaggio ma un po 'diverso da quello che hai detto: synclock _mylock Mapper.CreateMap (Of Service.User, Models.User)() _ .ForMember (Funzione (dest As Models.User) dest.ENumber, Sub (opt) opt.MapFrom (Funzione (src As Service.User) src.INumber)) _ .AfterMap (Sub (src As Service.User, dest As Models.User) ..code End Sub) endynclock. quindi ho messo tutto in synclock invece del solo codice di gruppo. Pensi che sia qui il problema? – Baahubali

+1

Due cose. Ricordare che il metodo AfterMap() non viene chiamato quando si chiama CreateMap(), ma dopo aver chiamato Mapper.Map() e si richiama il mapping che è stato configurato. Quindi SyncLock non sarà efficace a modo tuo. In secondo luogo, dovresti sempre bloccare il meno codice possibile. Si tratta di una raccolta sull'oggetto utente che si sta lamentando durante AfterMap, in base alla traccia dello stack, che punta a .Groups.Pertanto, bloccare l'elaborazione solo su quella raccolta. –

+0

Grazie per averlo spiegato. – Baahubali

1

Questo è un errore piuttosto comune che modifica una raccolta mentre la itera. È possibile che lasci qualcosa qui fuori che non possiamo vedere? Comunque è nel codice che hai fornito non c'è uno scenario del genere da quello che posso vedere. Ciò significa che potresti chiamarlo in un ambiente multi-thread e la raccolta verrà modificata in qualche altro thread.

Cosa puoi provare?

Cerca di bloccare il tuo enumeration in modo che sia possibile accedere a un solo thread alla volta. È possibile che si acceda a più ore quando gli utenti fanno clic sul pulsante.

Ecco un buon collegamento per aiutarvi: http://weblogs.asp.net/leftslipper/mvc-locking-the-routecollection

+0

ho pensato perché le funzioni Automapper.CreateMap e AfterMap vengono richiamate in un costruttore della classe. se qualche utente sta tentando di salvare l'oggetto chiamando la funzione create come elencato sopra e qualche altro utente crea un nuovo oggetto che chiamerebbe Automapper per modificare la collezione chiamando la funzione Aftermap che getterebbe la raccolta è stato modificato errore? Ho persino provato a inserire il codice in synclock ma l'errore non cambia. Stavo pensando di refactoring del codice, ma volevo capire completamente come funziona la funzione Aftermap. – Baahubali

+0

@ user1490835 'Aftermap' viene eseguito una sola volta per mappatura. Se si desidera eseguirlo solo una volta, è necessario collegarlo a una configurazione di mappatura da elenco a elenco e non a una configurazione da articolo a elemento; questo sicuramente getterebbe un errore 'collection is been modified ... ' – Codexer

+0

scusa cosa intendi per lista per elencare la mappatura e non una voce per la configurazione della voce? – Baahubali

Problemi correlati