访问WCF UsernamePasswordValidator中的当前InstanceContext

时间:2010-07-23 09:38:08

标签: c# wcf wcfserviceclient

我有一个使用自定义UsernamePasswordValidator的WCF服务。验证器需要访问我的实体框架上下文。

我想为整个服务调用创建一个ObjectContext,然后在调用结束时销毁/处理它。所以我创建了一个提供此功能的单例静态类,但是,现在发生的事情是,如果两个服务调用同时发生,其中一个调用会处理单例。

我要么保留对ObjectContext的本地引用,在这种情况下,使用它的第二个服务将其视为处置并抛出和错误,或者,我在Singleton类的任何地方放置一个包装器属性,无论我需要它然后我的所有更改被丢弃,因为如果另一个调用已经处理它,我将获得该对象的新实例。

所以基本上我的问题是如何为每个服务调用实例化一个ObjectContext?

注意:实例需要在服务代码和自定义UsernamePasswordValidator代码中可访问。

我不能在构造函数中执行此操作或使用using语句,因为自定义UsernamePasswordValidator无法访问它。有没有办法每次调用一个静态类?这听起来不可能,但是这有什么办法呢?我应该在会话中缓存对象吗?

我的服务托管在IIS中。

更新
所以我已经把它钉在了使用IExtension对象在InstanceContext中存储状态。但是如何在UsernamePasswordValidator中访问当前的InstanceContext?

6 个答案:

答案 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");
                }
       }
   }
}