Todo el mundo sabe que es un contexto de entity framework… ok
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.aspx
Todo el mundo sabe que son los interceptors de Unity… ¿ok? (Es un mecanismo para poder lanzar código antes y después de que un método se ejecute)
http://msdn.microsoft.com/en-us/library/ff647107.aspx
Si interceptamos un contexto con Unity tal que así
1: container.RegisterType<MyEntities, MyEntities>(
2: "auditable",
3: new PerThreadLifetimeManager(),
4: new InjectionConstructor(),
5: new Interceptor<VirtualMethodInterceptor>(),
6: new InterceptionBehavior<AuditableContextBehavior>());
(MyEntities es algo que hereda de ObjectContext y tiene un método llamado SaveChanges que vamos a auditar.(
Y AuditableContextBehavior es algo tal que así:
1: public class AuditableContextBehavior : Microsoft.Practices.Unity.InterceptionExtension.IInterceptionBehavior
2: {
3: /// <summary>
4: /// Gets a value indicating whether this behavior will actually do anything when invoked.
5: /// </summary>
6: public bool WillExecute
7: {
8: get
9: {
10: return true;
11: }
12: }
13:
14: /// <summary>
15: /// Returns the interfaces required by the behavior for the objects it intercepts.
16: /// </summary>
17: /// <returns>
18: /// The required interfaces.
19: /// </returns>
20: public IEnumerable<Type> GetRequiredInterfaces()
21: {
22: return Type.EmptyTypes;
23: }
24:
25: /// <summary>
26: /// Implement this method to execute your behavior processing.
27: /// </summary>
28: /// <param name="input">Inputs to the current call to the target.</param>
29: /// <param name="getNext">Delegate to execute to get the next delegate in the behavior chain.</param>
30: /// <returns>
31: /// Return value from the target.
32: /// </returns>
33: public Microsoft.Practices.Unity.InterceptionExtension.IMethodReturn Invoke(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation input, Microsoft.Practices.Unity.InterceptionExtension.GetNextInterceptionBehaviorDelegate getNext)
34: {
35: if (input == null)
36: {
37: throw new ArgumentNullException("input");
38: }
39:
40: if (getNext == null)
41: {
42: throw new ArgumentNullException("getNext");
43: }
44:
45: // Audit the entities, the new entities will be delayed.
46: IEnumerable<ObjectStateEntry> entriesDelayed = this.PreSaveChanges(input);
47:
48: // get next handler
49: IMethodReturn message = getNext()(input, getNext);
50:
51: // Audit the delayed entities
52: this.PostSaveChanges(entriesDelayed);
53:
54: // return the result of the interceptors
55: return message;
56: }
57:
58: /// <summary>
59: /// Execute code after the method
60: /// </summary>
61: /// <param name="input">The input.</param>
62: /// <returns>Entities delayed to audit</returns>
63: [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We can never stop the execution for a error in this method")]
64: private IEnumerable<ObjectStateEntry> PreSaveChanges(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation input)
65: {
66: IEnumerable<ObjectStateEntry> entriesToAdd = null;
67:
68: try
69: {
70: if (input.MethodBase.Name == "SaveChanges")
71: {
72: ...
73: }
74: }
75:
76:
77: }
78: catch (Exception ex)
79: {
80: ...
81: }
82:
83: return entriesToAdd;
84: }
85:
86: /// <summary>
87: /// Execute code after the method SaveChanges.
88: /// </summary>
89: /// <param name="entriesToAdd">The entries to add.</param>
90: [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We can never stop the execution for a error in this method")]
91: private void PostSaveChanges(IEnumerable<ObjectStateEntry> entriesToAdd)
92: {
93: // the entries marked to added must be logged at the end of the method to log the state
94: if (entriesToAdd != null && entriesToAdd.Count() > 0)
95: {
96: ...
97: }
98: }
99: }
¿Que ocurre cuando el método interceptado lanza una excepción? ¿Se para todo?
Pues no, si el método getNext devuelve una excepción, PostSaveChanges tambien se ejecutará y en mi caso no tiene sentido. Para evitar eso hay que hacer una comprobación tal que así (en amarillo)
1: if (input == null)
2: {
3: throw new ArgumentNullException("input");
4: }
5:
6: if (getNext == null)
7: {
8: throw new ArgumentNullException("getNext");
9: }
10:
11: // Audit the entities, the new entities will be delayed.
12: IEnumerable<ObjectStateEntry> entriesDelayed = this.PreSaveChanges(input);
13:
14: // get next handler
15: IMethodReturn message = getNext()(input, getNext);
16:
17: // if an exception is produced in the SaveChanges method of the context, it doesn't make sense save the state of the entities
18: if (message.Exception != null)
19: {
20: // Audit the delayed entities
21: this.PostSaveChanges(entriesDelayed);
22: }
23:
24: // return the result of the interceptors
25: return message;
Tenerlo en cuenta porque la inercia es usar el result para logear o lo que sea pensando que siempre está disponible y no, en caso de excepción, la vida sigue en el interceptor y luego, dentro del método interceptado, es cuando saltará la excepción, nunca antes