访问 WCF UsernamePasswordValidator 中的当前 InstanceContext

发布于 2024-09-10 22:52:45 字数 759 浏览 4 评论 0原文

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

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

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

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

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

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

我的服务托管在 IIS 中。

更新:
因此,我将其确定为使用 IExtension 对象在 InstanceContext 中存储状态。但是如何访问 UsernamePasswordValidator 中的当前 InstanceContext 呢?

I have a WCF service that is using a custom UsernamePasswordValidator. The validator needs to access my entity framework context.

I would like to create one ObjectContext for the entire service call and then destroy/dispose it at the end of the call. So I created a singleton static class that provided this functionality, however, what's happening now is that if two service calls happen concurrently, one of the calls disposes the singleton.

I either keep a local reference to the ObjectContext, in which case the second service to use it sees it as disposed and throws and error, or, I put a wrapper property around the Singleton class wherever I need it and then all my changes get thrown away because I'm getting a new instance of the object if another call has disposed it.

So basically my question is how do I instantiate an ObjectContext per service call?

NOTE: The instance needs to be accesible in both the service code AND the custom UsernamePasswordValidator code.

I can't just do it in the constructor or use a using statement because then the custom UsernamePasswordValidator doesn't have access to it. Is there a way to have a static class per call? It does sound impossible, but what's the way around this? Should I be caching the object in a session?

My service is hosted in IIS.

UPDATE:
So I've nailed this down to storing state in the InstanceContext using an IExtension object. But How do I access the current InstanceContext in a UsernamePasswordValidator?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

橘亓 2024-09-17 22:52:45

好吧,最后我通过使用以下静态类并依靠 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;

Ok, so in the end I solved it by using the following static class and relying on ASP.NET to cache the context for me.

I'm not sure if this is the best way to do things, but this allows me to use one ObjectContext per request so I'm not spinning up too many and this also means I don't have to use a lock on the object which would become a nightmare if many users were using the service.

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;
            }
        }    
    }

Then wherever I need an ObjectContext in the app I just call

var context = MyContextProvider.Context;
不弃不离 2024-09-17 22:52:45

每个调用有一个实例,每个实例也有 1 个调用。

所以它应该非常简单,在你的OperationContract方法的顶层使用一个using () { }块。

You have one instance per call, you also have 1 call per instance.

So it should be very simple, use a using () { } block in the toplevel of your OperationContract method.

墨落成白 2024-09-17 22:52:45

好的,这里是具有线程安全静态方法的类,它为任何 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];
        }
    }

Ok, here is the class with thread-safe static method that provides single ObjectContext entity model object for any WCF service call and automatically dispose it at the end of call:

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];
        }
    }
夏了南城 2024-09-17 22:52:45

对于您的服务,您可以指定一个服务行为,其中详细说明了服务的实例模式:

[ServiceBehaviour(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService {
    ObjectContext context;
}

For your service, you can specify a service behaviour which details the instance mode of the service:

[ServiceBehaviour(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService {
    ObjectContext context;
}
仄言 2024-09-17 22:52:45

更简洁的方法可能是使用 .NET 4 中的 ServiceAuthenticationManager。

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


我的研究的进一步阅读:

此问题的答案给出了有关如何使用它的一些提示:

使用 System.ServiceModel.ServiceAuthenticationManager 自定义 WCF 身份验证?

如果您可以阅读(或忽略)俄语,我在以下位置找到了有用的提示:

http://www .sql.ru/forum/actualthread.aspx?tid=799046

这篇相当不错的 CodeProject 文章更进一步(加密和压缩以及自定义授权)

http://www.codeproject.com/Articles/165844/WCF-Client-Server-Application-with-Custom -正品

A cleaner way may be to use the ServiceAuthenticationManager, which is in .NET 4.

http://msdn.microsoft.com/en-us/library/system.servicemodel.serviceauthenticationmanager.aspx

From the Authenticate method (which you'll override) you can access the Message object and set properties on it. I've not used it in anger, so YMMV :)

EDIT the problem with this approach is that you don't have the Username and Password, so will still need the custom Authentication.

Take a look at the UsernameSecurityTokenAuthenticator...
http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.usernamesecuritytokenauthenticator(v=vs.90).aspx


Further reading from my research:

Answers to this question gives some hints about how to use it:

Custom WCF authentication with System.ServiceModel.ServiceAuthenticationManager?

If you can read (or ignore) the Russian, I found useful hints at:

http://www.sql.ru/forum/actualthread.aspx?tid=799046

This rather good CodeProject article goes further (encryption and compression as well as custom authorization)

http://www.codeproject.com/Articles/165844/WCF-Client-Server-Application-with-Custom-Authenti

埖埖迣鎅 2024-09-17 22:52:45

当您分配给服务时,为什么不将上下文传递到 CustomValidator 中 - 将对象上下文存储在验证器中,并在需要时在重写的验证方法中将其更新。然后您仍然可以通过 Services CutomUserNameValidator 访问该对象..

取决于您的要求:
创建单独的 ObjectContext 类作为动态对象 - 将其作为属性添加到 CustomValidator。
在您的自定义验证器中 - 您现在可以检查对象是否已处置,并在需要时再次创建对象。
否则,如果这不是您想要的 - 只需将上下文存储在验证器中 - 您仍然可以在服务器端访问。
这里的代码只是一般性的想法 - 我只是将其作为参考框架发布,以便您可以了解我在说什么。

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

Why not pass in the context into your CustomValidator when you assign to the service - store your object context in your validator, and in the overridden validation method new it up if need be. Then you still have access to the object through the Services CutomUserNameValidator ..

Depending on what you are asking :
Create your separate ObjectContext class as a dynamic object - add that as a property to you CustomValidator.
In your custom Validator - you can now check if the object is disposed and create the object again if need be.
Otherwise if this is not what you are after - just store the Context in the validator - you still have access on server side.
The code here is just generalized idea - I am just posting it as a frame of reference so you can have an idea of what I talking about.

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");
                }
       }
   }
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文