我有一个使用自定义UsernamePasswordValidator的WCF服务。验证器需要访问我的实体框架上下文。
我想为整个服务调用创建一个ObjectContext,然后在调用结束时销毁/处理它。所以我创建了一个提供此功能的单例静态类,但是,现在发生的事情是,如果两个服务调用同时发生,其中一个调用会处理单例。
我要么保留对ObjectContext的本地引用,在这种情况下,使用它的第二个服务将其视为处置并抛出和错误,或者,我在Singleton类的任何地方放置一个包装器属性,无论我需要它然后我的所有更改被丢弃,因为如果另一个调用已经处理它,我将获得该对象的新实例。
所以基本上我的问题是如何为每个服务调用实例化一个ObjectContext?
注意:实例需要在服务代码和自定义UsernamePasswordValidator代码中可访问。
我不能在构造函数中执行此操作或使用using语句,因为自定义UsernamePasswordValidator无法访问它。有没有办法每次调用一个静态类?这听起来不可能,但是这有什么办法呢?我应该在会话中缓存对象吗?
我的服务托管在IIS中。
更新
所以我已经把它钉在了使用IExtension对象在InstanceContext中存储状态。但是如何在UsernamePasswordValidator中访问当前的InstanceContext?
答案 0 :(得分:2)
好的,最后我通过使用以下静态类并依靠ASP.NET为我缓存上下文来解决它。
我不确定这是否是最好的处理方式,但是这允许我每个请求使用一个ObjectContext,所以我不会旋转太多,这也意味着我不必使用锁如果许多用户正在使用该服务,那么该对象将成为一场噩梦。
public static class MyContextProvider
{
public static MyModel Context
{
get
{
if (HttpContext.Current.Items["context"].IsNull())
{
HttpContext.Current.Items["context"] = new MyModel();
}
return HttpContext.Current.Items["context"] as MyModel;
}
}
}
然后,无论我在应用程序中需要ObjectContext,我只需要调用
var context = MyContextProvider.Context;
答案 1 :(得分:1)
每个呼叫都有一个实例,每个实例也有1个呼叫。
所以它应该非常简单,在OperationContract方法的顶层使用using () { }
块。
答案 2 :(得分:1)
好的,这是带有线程安全静态方法的类,它为任何WCF服务调用提供单个ObjectContext实体模型对象,并在调用结束时自动处理它:
public static class EntityModelProvider
{
private static readonly Dictionary<OperationContext, MyEntityModel> _entityModels = new Dictionary<OperationContext, MyEntityModel>();
public static MyEntityModel GetEntityModel()
{
if (OperationContext.Current == null)
throw new Exception("OperationContext is missing");
lock (_entityModels)
{
if (!_entityModels.ContainsKey(OperationContext.Current))
{
_entityModels[OperationContext.Current] = new MyEntityModel();
OperationContext.Current.OperationCompleted += delegate
{
lock (_entityModels)
{
_entityModels[OperationContext.Current].Dispose();
_entityModels.Remove(OperationContext.Current);
}
};
}
return _entityModels[OperationContext.Current];
}
}
答案 3 :(得分:0)
对于您的服务,您可以指定详细说明服务实例模式的服务行为:
[ServiceBehaviour(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService {
ObjectContext context;
}
答案 4 :(得分:0)
更简洁的方法可能是使用ServiceAuthenticationManager,它位于.NET 4中。
http://msdn.microsoft.com/en-us/library/system.servicemodel.serviceauthenticationmanager.aspx
从Authenticate
方法(您将覆盖它),您可以访问Message对象并在其上设置属性。我没有在愤怒中使用它,所以YMMV:)
编辑此方法的问题是您没有用户名和密码,因此仍需要自定义身份验证。
看一下UsernameSecurityTokenAuthenticator ...... http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.usernamesecuritytokenauthenticator(v=vs.90).aspx
进一步阅读我的研究:
这个问题的答案提供了一些关于如何使用它的提示:
Custom WCF authentication with System.ServiceModel.ServiceAuthenticationManager?
如果你能阅读(或忽略)俄语,我发现了有用的提示:
http://www.sql.ru/forum/actualthread.aspx?tid=799046
这个相当不错的CodeProject文章更进一步(加密和压缩以及自定义授权)
http://www.codeproject.com/Articles/165844/WCF-Client-Server-Application-with-Custom-Authenti
答案 5 :(得分:0)
为什么不在分配给服务时将上下文传递到CustomValidator中 - 将对象上下文存储在验证器中,如果需要,则在重写的验证方法中新建它。然后,您仍然可以通过Services CutomUserNameValidator ..
访问该对象取决于您的要求: 创建单独的ObjectContext类作为动态对象 - 将其作为属性添加到CustomValidator。 在您的自定义验证器中 - 您现在可以检查对象是否已处理并在需要时再次创建对象。 否则,如果这不是您想要的 - 只需将Context存储在验证器中 - 您仍然可以在服务器端访问。 这里的代码只是一般化的想法 - 我只是将它作为参考框架发布,这样你就可以了解我所说的内容。
public DynamicObjectContextObjectClass
{
ObjectContext internalObjectContext;
}
public class ServiceUserNamePasswordValidator : UserNamePasswordValidator
{
public DynamicObjectContextObjectClass dynamiccontext;
public override void Validate(string userName, string password)
{
if(dynamiccontext.internalObjectContext.isdisposed)
{
dynamiccontext.internalObjectContext = new Context;
}
try
{
if (string.IsNullOrEmpty(userName) || password == null)
{
//throw new ArgumentNullException();
throw new FaultException("Username cannot be null or empty; Password cannot be null and should not be empty");
}
}
}
}