Pergunta

Eu tenho uma camada de acesso a dados, uma camada de serviço e uma camada de apresentação. A camada de apresentação é ASP.NET MVC2 RTM (Web) e a camada de serviço é WCF (Services). É tudo .NET 3.5 SP1.

O problema é que, nos serviços, os objetos que estão sendo retornados estão marcados com o [DataContract] atributo. A Web está usando o Cache Appfabric (também conhecido como Velocity) sessionStateProvider para armazenar o estado da sessão. Devido a isso, qualquer coisa que eu guardo na sessão deve ser serializável.

Aí vem o problema: os dados do datacontro não estão marcados com [Serializable] e até onde me lembro, apresentando -o em uma aula já marcada com [DataContract] Alguns problemas surgem e, portanto, não acredito que isso seja uma solução.

Inicialmente, eu estava planejando usar os dados do DataContrates na camada da Web, usando -os como modelos para visualizações relacionadas à renderização dos Datacontrates (provavelmente aninhados dentro de uma classe ViewModel de nível superior). Mas, devido ao provedor estadual de sessão que exige que todos os objetos armazenados nele sejam serializáveis, estou começando a repensar essa estratégia. Seria bom ter, pois eles contêm lógica de validação usando o IDataErrorInfo A interface e a mesma lógica de validação podem ser reutilizadas no MVC como parte da ligação do modelo.

Qual você acredita que é a melhor maneira de me permitir reduzir o trabalho necessário?

Atualmente, pensei nas seguintes maneiras diferentes:

A. Crie uma parte 'ServiceIntegration' no projeto da Web.

Este seria um intermediário entre meus controladores e minha camada de serviço WCF. A parte do ServiceIntegration falaria com a camada de serviço usando o DataCOntracts e a camada da Web usando o ViewModels, mas teria que transformar entre os DatacOntracts e o ViewModels usando um transformador de mão dupla.

Além disso, como a validação do IdataErrorinfo não seria reutilizável, seria necessário criar um validador por DatacOntract também, que usa o transformador para converter do ViewModel em Datacontract, execute a validação usando o idataerrorinfo e retorne seus resultados. Isso seria então usado dentro dos métodos de ação dos controladores (por exemplo if (!MyValidator.IsValid(viewModel)) return View();)

Diferentes classes necessárias: xdatacontract, xviewmodel, xtransformer, XValidator

B. Crie uma parte 'sessionIntegration' no projeto da web

Isso seria um intermediário entre os controladores (ou qualquer coisa que acesse a sessão) e a própria sessão. Qualquer coisa que exija acesso à sessão passaria por esta classe. O DataContracts seria usado em todo o aplicativo, a menos que estejam sendo armazenados na sessão. A parte da sessão de integração assumiria a responsabilidade de transformar o DataCOntract em algum formulário iserializável e voltar. Nenhum validador adicional é necessário devido ao uso da interface idataerrorinfo no DataContract.

Diferentes classes necessárias: xdatacontract, xtransformer, xSerializableForm


Nota: Ainda haveria viewmodels em ambos os cenários; no entanto, com (b) eu seria capaz de compor ViewModels a partir de DataContracts.

(B) tem o benefício de não precisar de um validador extra.


Antes de sair e implementar (a)/(b) totalmente, eu gostaria de algum feedback. No momento, estou começando a me inclinar para (b), no entanto, (a) pode ser mais flexível. De qualquer maneira, parece muito trabalho para o que vale a pena. Alguém mais se deparou com esse problema, você concorda/discorda de mim e/ou tem outra maneira de resolver o problema?

Obrigado,

James

Foi útil?

Solução

Sem seguir a rota completa de A ou B, você poderia apenas fazer um objeto de invólucro genérico iserializável e colocá -los no seu SessionState?

    [Serializable]
    public class Wrapper : ISerializable
    {
        public object Value { get; set; }

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (Value != null)
            {
                info.AddValue("IsNull", false);
                if (Value.GetType().GetCustomAttributes(typeof(DataContractAttribute), false).Length == 1)
                {
                    using (var ms = new MemoryStream())
                    {
                        var serializer = new DataContractSerializer(Value.GetType());
                        serializer.WriteObject(ms, Value);
                        info.AddValue("Bytes", ms.ToArray());
                        info.AddValue("IsDataContract", true);
                    }
                }
                else if (Value.GetType().IsSerializable)
                {
                    info.AddValue("Value", Value);
                    info.AddValue("IsDataContract", false);
                }
                info.AddValue("Type", Value.GetType());
            }
            else
            {
                info.AddValue("IsNull", true);
            }
        }

        public Wrapper(SerializationInfo info, StreamingContext context)
        {
            if (!info.GetBoolean("IsNull"))
            {
                var type = info.GetValue("Type", typeof(Type)) as Type;

                if (info.GetBoolean("IsDataContract"))
                {
                    using (var ms = new MemoryStream(info.GetValue("Bytes", typeof(byte[])) as byte[]))
                    {
                        var serializer = new DataContractSerializer(type);
                        Value = serializer.ReadObject(ms);
                    }
                }
                else
                {
                    Value = info.GetValue("Value", type);   
                }
            }
        }
    }

Outras dicas

Como uma extensão da resposta fornecida, adicionei esses dois métodos para facilitar o armazenamento/recuperação dos dados.

    public static void Set<T>(HttpSessionStateBase session, string key, T value)
    {
        session[key] = new Wrapper(value);
    }

    public static T Get<T>(HttpSessionStateBase session, string key)
    {
        object value = session[key];
        if (value != null && typeof(T) == value.GetType())
        {
            return (T) value;
        }
        Wrapper wrapper = value as Wrapper;
        return (T) ((wrapper == null) ? null : wrapper.Value);
    }

Isso torna um pouco mais fácil definir/obter valores da sessão:

    MyDataContract c = ...;
    Wrapper.Set(Session, "mykey", c);
    c = Wrapper.Get<MyDataContract>(Session, "mykey");

Para facilitar ainda mais, adicione métodos de extensão:

public static class SessionWrapperEx
{
    public static void SetWrapped<T>(this HttpSessionStateBase session, string key, T value)
    {
        Wrapper.Set<T>(session, key, value);
    }

    public static T GetWrapped<T>(this HttpSessionStateBase session, string key)
    {
        return Wrapper.Get<T>(session, key);
    }
}

E use como abaixo:

    MyDataContract c = ...;
    Session.SetWrapped("mykey", c);
    c = Session.GetWrapped<MyDataContract>("mykey");
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top