Access the current InstanceContext in a WCF UsernamePasswordValidator

。_饼干妹妹 提交于 2019-12-05 18:50:46

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;

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.

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

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

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

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");
                }
       }
   }
} 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!