4

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

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

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

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

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

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

我的服务托管在 IIS 中。

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

4

6 回答 6

2

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;
于 2010-07-23T10:55:04.417 回答
1

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

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

于 2010-07-23T09:48:18.750 回答
1

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];
        }
    }
于 2013-08-02T20:17:55.720 回答
0

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

[ServiceBehaviour(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService {
    ObjectContext context;
}
于 2010-07-23T09:46:07.127 回答
0

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

于 2012-12-12T15:54:29.767 回答
0

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");
                }
       }
   }
} 
于 2015-11-17T18:46:23.240 回答