unity una tiempo tamaño tag script posicion otro obtener objetos objeto llamar instanciar generar funcion español deltatime contador cambiar aleatorios c# unity-container enterprise-library

c# - una - ¿Se puede hacer que Unity no arroje SynchronizationLockException todo el tiempo?



time.deltatime unity español (8)

El contenedor de inyección de dependencias de Unity tiene lo que parece ser un problema ampliamente conocido en el que SynchronizedLifetimeManager a menudo provocará que el método Monitor.Exit arroje una SynchronizationLockException que luego se detecta e ignora. Esto es un problema para mí porque me gusta depurar con el conjunto de Visual Studio para romper cualquier excepción lanzada, por lo que cada vez que se inicia mi aplicación estoy rompiendo esta excepción varias veces sin ninguna razón.

¿Cómo puedo evitar que esta excepción sea lanzada?

Dondequiera que se mencione este problema en otra parte de la web, el consejo generalmente implica cambiar la configuración del depurador para ignorarlo. Esto es similar a ir al médico y decir: "Doctor, doctor, me duele el brazo cuando lo levanto", para que le digan: "Bueno, deje de levantarlo". Estoy buscando una solución que detenga la excepción lanzada en primer lugar.

La excepción se produce en el método SetValue porque supone que GetValue se habrá llamado primero, donde se llama a Monitor.Enter. Sin embargo, las clases LifetimeStrategy y UnityDefaultBehaviorExtension regularmente llaman a SetValue sin llamar a GetValue.

Prefiero no tener que cambiar el código fuente y mantener mi propia versión de Unity, así que espero una solución donde pueda agregar una combinación de extensiones, políticas o estrategias al contenedor que garantice que, si el el administrador de por vida es un SynchronizedLifetimeManager, GetValue siempre se llama antes que cualquier otra cosa.


Esto podría ayudarte:

  • Vaya a Depurar -> Excepciones ...
  • Encuentra las excepciones que realmente te molestan como SynchronizationLockException

Voila.


Estoy seguro de que hay muchas formas en que el código podría llamar a SynchronizedLifetimeManager, o a un descendiente como ContainerControlledLifetimeManager, pero había dos escenarios en particular que me causaban problemas.

El primero fue mi propia falla: estaba usando inyección de constructor para proporcionar una referencia al contenedor, y en ese constructor también estaba agregando la nueva instancia de la clase al contenedor para uso futuro. Este enfoque hacia atrás tuvo el efecto de cambiar el administrador de por vida de Transitorio a ContainerControlled para que el objeto Unity llamado GetValue no fuera el mismo objeto al que llamó SetValue. La lección aprendida es no hacer nada durante la compilación que podría cambiar el administrador de por vida de un objeto.

El segundo escenario fue que cada vez que se llama a RegisterInstance, UnityDefaultBehaviorExtension llama a SetValue sin llamar primero a GetValue. Afortunadamente, Unity es lo suficientemente extensible como para que, con suficiente mente sanguinaria, puedas evitar el problema.

Comience con una nueva extensión de comportamiento como esta:

/// <summary> /// Replaces <see cref="UnityDefaultBehaviorExtension"/> to eliminate /// <see cref="SynchronizationLockException"/> exceptions that would otherwise occur /// when using <c>RegisterInstance</c>. /// </summary> public class UnitySafeBehaviorExtension : UnityDefaultBehaviorExtension { /// <summary> /// Adds this extension''s behavior to the container. /// </summary> protected override void Initialize() { Context.RegisteringInstance += PreRegisteringInstance; base.Initialize(); } /// <summary> /// Handles the <see cref="ExtensionContext.RegisteringInstance"/> event by /// ensuring that, if the lifetime manager is a /// <see cref="SynchronizedLifetimeManager"/> that its /// <see cref="SynchronizedLifetimeManager.GetValue"/> method has been called. /// </summary> /// <param name="sender">The object responsible for raising the event.</param> /// <param name="e">A <see cref="RegisterInstanceEventArgs"/> containing the /// event''s data.</param> private void PreRegisteringInstance(object sender, RegisterInstanceEventArgs e) { if (e.LifetimeManager is SynchronizedLifetimeManager) { e.LifetimeManager.GetValue(); } } }

Entonces necesitas una forma de reemplazar el comportamiento predeterminado. Unity no tiene un método para eliminar una extensión específica, por lo que debe eliminar todo y volver a colocar las otras extensiones:

public static IUnityContainer InstallCoreExtensions(this IUnityContainer container) { container.RemoveAllExtensions(); container.AddExtension(new UnityClearBuildPlanStrategies()); container.AddExtension(new UnitySafeBehaviorExtension()); #pragma warning disable 612,618 // Marked as obsolete, but Unity still uses it internally. container.AddExtension(new InjectedMembers()); #pragma warning restore 612,618 container.AddExtension(new UnityDefaultStrategiesExtension()); return container; }

¿Tenga en cuenta que UnityClearBuildPlanStrategies ? RemoveAllExtensions borra todas las listas internas de políticas y estrategias del contenedor, excepto una, así que tuve que usar otra extensión para evitar insertar duplicados cuando restauré las extensiones predeterminadas:

/// <summary> /// Implements a <see cref="UnityContainerExtension"/> that clears the list of /// build plan strategies held by the container. /// </summary> public class UnityClearBuildPlanStrategies : UnityContainerExtension { protected override void Initialize() { Context.BuildPlanStrategies.Clear(); } }

Ahora puede usar RegisterInstance sin temor a ser llevado al borde de la locura. Solo para estar seguro, aquí hay algunas pruebas:

[TestClass] public class UnitySafeBehaviorExtensionTests : ITest { private IUnityContainer Container; private List<Exception> FirstChanceExceptions; [TestInitialize] public void TestInitialize() { Container = new UnityContainer(); FirstChanceExceptions = new List<Exception>(); AppDomain.CurrentDomain.FirstChanceException += FirstChanceExceptionRaised; } [TestCleanup] public void TestCleanup() { AppDomain.CurrentDomain.FirstChanceException -= FirstChanceExceptionRaised; } private void FirstChanceExceptionRaised(object sender, FirstChanceExceptionEventArgs e) { FirstChanceExceptions.Add(e.Exception); } /// <summary> /// Tests that the default behavior of <c>UnityContainer</c> leads to a <c>SynchronizationLockException</c> /// being throw on <c>RegisterInstance</c>. /// </summary> [TestMethod] public void UnityDefaultBehaviorRaisesExceptionOnRegisterInstance() { Container.RegisterInstance<ITest>(this); Assert.AreEqual(1, FirstChanceExceptions.Count); Assert.IsInstanceOfType(FirstChanceExceptions[0], typeof(SynchronizationLockException)); } /// <summary> /// Tests that <c>UnitySafeBehaviorExtension</c> protects against <c>SynchronizationLockException</c>s being /// thrown during calls to <c>RegisterInstance</c>. /// </summary> [TestMethod] public void SafeBehaviorPreventsExceptionOnRegisterInstance() { Container.RemoveAllExtensions(); Container.AddExtension(new UnitySafeBehaviorExtension()); Container.AddExtension(new InjectedMembers()); Container.AddExtension(new UnityDefaultStrategiesExtension()); Container.RegisterInstance<ITest>(this); Assert.AreEqual(0, FirstChanceExceptions.Count); } } public interface ITest { }


La respuesta a su pregunta es lamentablemente no. Seguí este tema con el equipo de desarrollo aquí en el grupo de patrones y prácticas de Microsoft (yo era el líder de desarrollo hasta hace poco) y tuvimos esto como un error a considerar para EntLib 5.0. Hicimos una investigación y llegamos a la conclusión de que esto fue causado por algunas interacciones inesperadas entre nuestro código y el depurador. Consideramos una solución, pero resultó ser más compleja que el código existente. Al final, esto se priorizó por debajo de otras cosas y no llegó a ser de 5.

Lo siento, no tengo una mejor respuesta para ti. Si te sirve de consuelo, también me resulta irritante.


La solución de Rory es genial, gracias. ¡Resolvió un problema que me molesta todos los días! Hice algunos ajustes menores a la solución de Rory para que maneje las extensiones que estén registradas (en mi caso tenía una extensión WPF Prism / Composite).

public static void ReplaceBehaviourExtensionsWithSafeExtension(IUnityContainer container) { var extensionsField = container.GetType().GetField("extensions", BindingFlags.Instance | BindingFlags.NonPublic); var extensionsList = (List<UnityContainerExtension>)extensionsField.GetValue(container); var existingExtensions = extensionsList.ToArray(); container.RemoveAllExtensions(); container.AddExtension(new UnitySafeBehaviorExtension()); foreach (var extension in existingExtensions) { if (!(extension is UnityDefaultBehaviorExtension)) { container.AddExtension(extension); } } }


Tenga cuidado con un error en la respuesta de Zubin Appoo: faltan UnityClearBuildPlanStrategies en su código.

El fragmento de código correcto es:

FieldInfo extensionsField = container.GetType().GetField("extensions", BindingFlags.Instance | BindingFlags.NonPublic); List<UnityContainerExtension> extensionsList = (List<UnityContainerExtension>)extensionsField.GetValue(container); UnityContainerExtension[] existingExtensions = extensionsList.ToArray(); container.RemoveAllExtensions(); container.AddExtension(new UnityClearBuildPlanStrategiesExtension()); container.AddExtension(new UnitySafeBehaviorExtension()); foreach (UnityContainerExtension extension in existingExtensions) { if (!(extension is UnityDefaultBehaviorExtension)) { container.AddExtension(extension); } }


Yo uso esta breve solución:

/// <summary> /// KVV 20110502 /// Fix for bug in Unity throwing a synchronizedlockexception at each register /// </summary> class LifeTimeManager : ContainerControlledLifetimeManager { protected override void SynchronizedSetValue(object newValue) { base.SynchronizedGetValue(); base.SynchronizedSetValue(newValue); } }

y úsalo así:

private UnityContainer _container; ... _container.RegisterInstance(instance, new LifeTimeManager());

el problema es que la clase base de ContainerControlledLifetimeManager espera que SynchronizedSetValue haga un monitor.Introduzca () a través de la base.GetValue, pero la clase ContainerControlledLifetimeManager no puede hacer esto (aparentemente sus desarrolladores no tienen activada la función ''break at exception''?) .

Saludos, Koen


Fixed en la última versión de Unity (2.1.505.2). Obténgalo vía NuGet.


Fixed

  1. Abordar un problema de seguridad de subprocesos: http://unity.codeplex.com/discussions/328841

  2. Mejorando la experiencia de depuración en System.Threading.SynchronizationLockException: https://entlib.uservoice.com/forums/89245-general/suggestions/2377307-fix-the-system-threading-synchronizationlockexcep

  3. Mejora de la experiencia de depuración a través de mejores mensajes de error cuando no se puede cargar un tipo: http://unity.codeplex.com/workitem/9223

  4. Admitir un escenario de ejecución de BuildUp () en una instancia existente de una clase que no tiene un constructor público: http://unity.codeplex.com/workitem/9460

Para que la experiencia de actualización sea lo más simple posible para los usuarios y para evitar la necesidad de redireccionamientos vinculantes de ensamblaje, optamos por incrementar solo la versión del archivo ensamblador, no la versión del ensamblado .NET.