Pregunta

Tengo una capa de acceso a datos, una capa de servicio, y una capa de presentación. La capa de presentación es ASP.NET MVC2 RTM (web), y la capa de servicio es WCF (servicios). Es todo .NET 3.5 SP1.

El problema es que en los servicios, los objetos que se devuelven están marcados con el atributo [DataContract]. La web está utilizando la caché de AppFabric (a.k.a Velocidad) SessionStateProvider al estado de la sesión tienda. Debido a esto, cualquier cosa que tienda en la sesión debe ser serializable.

Aquí viene el problema: los DataContracts no están marcados con [Serializable] y por lo que puedo recordar, introduciéndolo en una clase ya marcado con [DataContract] surgen algunos problemas, así que no creo que esto es una solución.

Yo estaba planeando inicialmente en el uso de los DataContracts derecho en la capa web, utilizando como modelos a puntos de vista relacionados con la prestación de los DataContracts (probablemente anidado dentro de una clase de modelo de vista de nivel superior). Sin embargo, debido a que el proveedor de estado de sesión requiere que todos los objetos almacenados en su interior para ser serializable, estoy empezando a reconsiderar esta estrategia. Sería bueno tener, sin embargo, ya que contienen la lógica de validación utilizando la interfaz IDataErrorInfo, y la misma lógica de validación podría ser reutilizada en MVC como parte del modelo de unión.

¿Cuál cree que es la mejor manera que me permita reducir el trabajo necesario?

He momento pensó en las siguientes maneras:

A. Crear una pieza 'ServiceIntegration' en el proyecto web.

Esto sería un intermediario entre mis controladores y mi capa de servicio WCF. La parte ServiceIntegration hablaría con la capa de servicio utilizando DataContracts, ya la capa Web mediante ViewModels, pero tendría que transformar entre las DataContracts y ViewModels utilizando un transformador de dos vías.

Además, dado que la validación IDataErrorInfo no sería reutilizable, sería necesario crear un validador por DataContract también, que utiliza el transformador convertir de ViewModel a DataContract, realizar la validación usando IDataErrorInfo y devolver sus resultados. Esto entonces sería utilizado dentro de los métodos de acción de los controladores (por ejemplo if (!MyValidator.IsValid(viewModel)) return View();)

Las diferentes clases requerido: xDataContract, xViewModel, xTransformer, xValidator

B. Crear una pieza 'SessionIntegration' en el proyecto web

Esto sería un intermediario entre los controladores (o nada con el acceso a la sesión) y la propia sesión. Cualquier cosa que requiera acceso a la sesión iría a través de esta clase. DataContracts se utilizan en toda la aplicación, a menos que se almacenan en la sesión. La parte SessionIntegration tomaría la responsabilidad de transformar el DataContract a alguna forma ISerializable y la espalda. Sin Validador adicional es necesaria debido a la utilización de la interfaz de IDataErrorInfo en el DataContract.

Las diferentes clases requerido: xDataContract, xTransformer, xSerializableForm


Nota:. Todavía habría ViewModels vueltas en ambos escenarios, sin embargo con (B) Me gustaría ser capaz de componer ViewModels de DataContracts

(B) tiene la ventaja de no necesitar un validador adicional.


Antes de ir fuera y poner en práctica (A) / (B) totalmente, me gustaría alguna información. Por el momento, estoy empezando a inclinarse hacia (B), sin embargo, (A) podría ser más flexible. De cualquier manera, parece como demasiado trabajo para lo que vale la pena. Tiene cualquier otro encontrado con este problema, ¿estás de acuerdo / en desacuerdo conmigo, y / o tiene cualquier otra forma de resolver el problema?

Gracias,

James

¿Fue útil?

Solución

Sin ir la ruta completa soplado de A o B, ¿podrías hacer una ISerializable genérica objeto envoltorio y poner los de su 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);   
                }
            }
        }
    }

Otros consejos

Como una extensión de la respuesta proporcionada, he añadido estos dos métodos para aliviar almacenar / recuperar los datos.

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

Esto hace que sea un poco más fácil de instalar / obtener valores de la sesión:

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

Para hacer aún más fácil, añadir los métodos de extensión:

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

Y el uso de la siguiente manera:

    MyDataContract c = ...;
    Session.SetWrapped("mykey", c);
    c = Session.GetWrapped<MyDataContract>("mykey");
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top