嗨大家帮助我们在使用AR的WCF服务上进行延迟加载我为WCF创建了一个“Session Scope PerRequest”解决方案。
[编辑] 好的,所以我把问题放在最后:)所以请耐心等待,最后开始阅读。 :) [/编辑]
如果您想在网站或网络服务中使用ActiveRecord,您必须通过其在网络环境中运行的配置告诉它。
它怎么会假设它有一个HttpContext.Current,它不会在WCF中存在。
因此,我们告诉AR使用我们自己的AbstractThreadScopeInfo实现,它也实现了IWebThreadScopeInfo,告诉它它与每个请求的会话模式兼容,该模式今天成为每会话会话模式。
为我从here
遇到的延迟加载异常添加了一些修复public class WCFThreadScopeInfo : AbstractThreadScopeInfo, IWebThreadScopeInfo
{
public static readonly ILog Logger = LogManager.GetLogger (typeof (WCFThreadScopeInfo));
private readonly object _syncLock;
public WCFThreadScopeInfo()
{
_syncLock = new object ();
}
public new void RegisterScope (ISessionScope scope)
{
CurrentStack.Push (scope);
}
public new ISessionScope GetRegisteredScope ()
{
if (CurrentStack.Count == 0)
{
//Instead of returning a "null" stack as is in the original ActiveRecord code,
//instantiate a new one which adds itself to the stack immediately
lock (_syncLock)
{
if (CurrentStack.Count == 0)
{
new SessionScope ();
}
}
}
return CurrentStack.Peek () as ISessionScope;
}
public new void UnRegisterScope (ISessionScope scope)
{
if (GetRegisteredScope () != scope)
{
throw new ScopeMachineryException ("Tried to unregister a scope that is not the active one");
}
CurrentStack.Pop ();
}
public new bool HasInitializedScope
{
get { return GetRegisteredScope () != null; }
}
#region Overrides of AbstractThreadScopeInfo
public override Stack CurrentStack
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
//Lets use the OperationContext instead of the HttpContext
OperationContext current = OperationContext.Current;
//Which offcourse can't be null
if (current == null)
throw new ScopeMachineryException ("Could not access OperationContext.Current");
//Get the first WCF StackContainer from the OperationContext or null
WCFStackContainer stackContainer = (WCFStackContainer)current.Extensions.FirstOrDefault (ex => ex is WCFStackContainer);
//If the previous statement didn't find any add a new one to the OperationContext
if (stackContainer == null)
{
Logger.Debug ("Creating new WCFStackContainer");
stackContainer = new WCFStackContainer ();
current.Extensions.Add (stackContainer);
}
//In the end return the stack in the container
return stackContainer.Stack;
}
}
#endregion
}
如上所示,我们需要一个可以添加到当前OperationContext.Extensions的WCFStackContainer。为了促进这一点,它需要实现IExtension。见这里:
public class WCFStackContainer : IExtension<OperationContext>
{
private Stack _stack;
public Stack Stack
{
get { return _stack; }
set { _stack = value; }
}
#region Implementation of IExtension<OperationContext>
public void Attach (OperationContext owner)
{
//On Attachment to the OperationContext create a new stack.
_stack = new Stack();
}
public void Detach (OperationContext owner)
{
_stack = null;
}
#endregion
}
所以现在我们为具有HttpContext的Web应用程序替换了IsWebApp功能。现在我们需要确定会话。
和我们一起玩吧!我很快就会答应这一点:)
因此,如果我们有一个HttpContext,我们会做这样的事情
.ctor
{
HttpContext.Current.Items.Add ("ar.sessionscope", new SessionScope());
}
public void Dispose ()
{
try
{
SessionScope scope = HttpContext.Current.Items["ar.sessionscope"] as SessionScope;
if (scope != null)
{
scope.Dispose ();
}
}
catch (Exception ex)
{
CTMLogger.Fatal("Error " + "EndRequest: " + ex.Message, ex);
}
}
这不能在WCF中完成所以你需要这样的东西:
public class ARSessionWCFExtension : IExtension<OperationContext>
{
private static readonly ILog Logger = LogManager.GetLogger (typeof(ARSessionWCFExtention));
private SessionScope _session;
#region IExtension<OperationContext> Members
public void Attach (OperationContext owner)
{
Logger.Debug ("Attachig ARSessionScope to WCFSession");
_session = new SessionScope();
}
public void Detach (OperationContext owner)
{
try
{
Logger.Debug ("Detaching ARSessionScope from WCFSession");
if (_session != null)
_session.Dispose ();
}
catch(Exception ex)
{
Logger.Fatal ("Exception: " + ex.Message + " Stacktrace: " + ex.StackTrace);
}
}
#endregion
}
我已经明白了这一点!还有一点:)
所以我们在WCFService中做到了:
.ctor
{
OperationContext.Current.Extensions.Add(new ARSessionWCFExtension());
}
在这里我们进入IDisposable实现我们添加它。我们很高兴:)
public void Dispose()
{
try
{
foreach (var extension in OperationContext.Current.Extensions.Where (ex => ex is ARSessionWCFExtention).ToList ())
OperationContext.Current.Extensions.Remove (extension);
Logger.Debug ("Session disposed ClinicID: " + _currentClinic.ClinicID);
}
catch (Exception ex)
{
Logger.Fatal ("Exception message: " + ex.Message + " StackTrace: " + ex.StackTrace);
}
}
但是它不起作用OperationContext.Current是null!。我在MSDN上发现了几个小时无意义的搜索后发现没有OperationContext,因为“只有在客户端启动代码时才能使用OperationContext”
我现在通过在构造函数中存储当前的OperationContext和SessionID并在Deconsturctor中对它们进行比较然后使用它们来处理会话来解决这个问题。
所以我现在有:
.ctor
{
_current = OperationContext.Current;
_sessionID = OperationContext.Current.SessionId;
OperationContext.Current.Extensions.Add (new ARSessionWCFExtention ());
}
public void Dispose()
{
try
{
OperationContext.Current = _current;
if (OperationContext.Current.SessionId != _sessionID)
throw new Exception("v weird!");
foreach (var extension in OperationContext.Current.Extensions.Where (ex => ex is ARSessionWCFExtention).ToList ())
OperationContext.Current.Extensions.Remove (extension);
Logger.Debug ("Session disposed ClinicID: " + _currentClinic.ClinicID);
}
catch (Exception ex)
{
Logger.Fatal ("Exception message: " + ex.Message + " StackTrace: " + ex.StackTrace);
}
}
有没有人知道我怎么能解决这个问题?有点强盗/更好/更好?
我尝试处理OperationContext.Channel.Close事件,但不会仅在客户端触发。每次完成调用后,事件OperationContext.OperationComplete都会触发。这不是我们想要的,我们希望AR Session能够持续WCFSession的长度。
谢谢:)