Pergunta

Eu tenho um serviço WCF que está usando um UsernamePasswordValidator personalizado.O validador precisa acessar o contexto da estrutura da minha entidade.

Gostaria de criar um ObjectContext para toda a chamada de serviço e, em seguida, destruí-lo/descartá-lo no final da chamada.Então criei uma classe estática singleton que fornecia essa funcionalidade, porém, o que está acontecendo agora é que se duas chamadas de serviço acontecerem simultaneamente, uma das chamadas descarta o singleton.

Eu mantenho uma referência local para o ObjectContext, caso em que o segundo serviço a usá-lo o vê como descartado e gera um erro, ou coloco uma propriedade wrapper em torno da classe Singleton onde quer que eu precise e então todas as minhas alterações são lançadas afastado porque estou obtendo uma nova instância do objeto se outra chamada o tiver descartado.

Então, basicamente, minha pergunta é como instanciar um ObjectContext por chamada de serviço?

OBSERVAÇÃO:A instância precisa estar acessível no código de serviço E no código UsernamePasswordValidator personalizado.

Não posso simplesmente fazer isso no construtor ou usar uma instrução using porque então o custom UsernamePasswordValidator não tem acesso a ele.Existe uma maneira de ter uma classe estática por chamada?Parece impossível, mas qual é a maneira de contornar isso?Devo armazenar o objeto em cache em uma sessão?

Meu serviço está hospedado no IIS.

ATUALIZAR:
Então, resolvi isso para armazenar o estado no InstanceContext usando um objeto IExtension.Mas como faço para acessar o InstanceContext atual em um UsernamePasswordValidator?

Foi útil?

Solução

Ok, no final resolvi usando a seguinte classe estática e contando com o ASP.NET para armazenar em cache o contexto para mim.

Não tenho certeza se essa é a melhor maneira de fazer as coisas, mas isso me permite usar um ObjectContext por solicitação, para não gerar muitos e isso também significa que não preciso usar um bloqueio no objeto o que se tornaria um pesadelo se muitos usuários usassem o serviço.

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

Então, sempre que eu precisar de um ObjectContext no aplicativo, eu simplesmente chamo

var context = MyContextProvider.Context;

Outras dicas

Você tem uma instância por chamada, você também tem 1 chamada por instância.

Por isso, deve ser muito simples, use um bloco using () { } na Toplevel do seu método OperationContract.

Ok, aqui está a classe com método estático thread-safe que fornece um único objeto de modelo de entidade ObjectContext para qualquer chamada de serviço WCF e o descarta automaticamente no final da chamada:

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

Para o seu serviço, você pode especificar um comportamento de serviço que detalha o modo de instância do serviço:

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

Uma maneira mais limpa pode ser usar o ServiceAuthenticationManager, que está no .NET 4.

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

De Authenticate método (que você substituirá), você pode acessar o objeto Message e definir propriedades nele.Eu não usei isso com raiva, então YMMV :)

EDITAR o problema com esta abordagem é que você não tem o nome de usuário e a senha, então ainda precisará da autenticação personalizada.

Dê uma olhada no UsernameSecurityTokenAuthenticator...http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.usernamesecuritytokenauthenticator(v=vs.90).aspx


Leitura adicional da minha pesquisa:

As respostas a esta pergunta dão algumas dicas sobre como usá-lo:

Autenticação WCF personalizada com System.ServiceModel.ServiceAuthenticationManager?

Se você consegue ler (ou ignorar) o russo, encontrei dicas úteis em:

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

Este bom artigo do CodeProject vai além (criptografia e compactação, bem como autorização personalizada)

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

Por que não passar o contexto para o seu CustomValidator quando você atribui ao serviço - armazene o contexto do objeto no seu validador e, no método de validação substituído, atualize-o, se necessário.Então você ainda tem acesso ao objeto através dos Serviços CutomUserNameValidator..

Dependendo do que você está perguntando:Crie sua classe ObjectContext separada como um objeto dinâmico - adicione-a como uma propriedade ao seu CustomValidator.No seu validador personalizado - agora você pode verificar se o objeto foi descartado e criá-lo novamente, se necessário.Caso contrário, se não é isso que você procura - basta armazenar o Contexto no validador - você ainda terá acesso no lado do servidor.O código aqui é apenas uma ideia generalizada - estou apenas postando-o como um quadro de referência para que você possa ter uma ideia do que estou falando.

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");
                }
       }
   }
} 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top