Domanda

Ho un Data Access Layer, un livello di servizio, e un livello di presentazione. Il livello di presentazione è ASP.NET MVC2 RTM (web), e il livello di servizio è WCF (servizi). E 'tutto .NET 3.5 SP1.

Il problema è che nei servizi, gli oggetti da restituire sono contrassegnati con l'attributo [DataContract]. Il web sta usando l'AppFabric cache (a.k.a Velocity) SessionStateProvider allo stato negozio di sessione. A causa di questo, niente negozio ho nella sessione deve essere serializzabile.

Ecco il problema: i DataContracts non sono contrassegnati con [Serializable] e, per quanto posso ricordare, con l'introduzione di esso su una classe già segnato con [DataContract] sorgono alcuni problemi, e quindi non credo che questa è una soluzione.

I è stato inizialmente pensando di usare i DataContracts proprio nello strato web, utilizzando come modelli di opinioni relative al rendendo i DataContracts (probabilmente nidificato all'interno di un livello di classe ViewModel superiore). Ma a causa del fornitore di stato della sessione che richiede tutti gli oggetti memorizzati al suo interno per essere serializzabile, sto iniziando a ripensare questa strategia. Sarebbe bello avere anche se, in quanto contengono la logica di convalida utilizzando l'interfaccia IDataErrorInfo, e la stessa logica di convalida potrebbe essere riutilizzato in MVC come parte del modello vincolante.

Quale ritiene sia il modo migliore per permettere a me di ridurre il lavoro necessario?

Al momento ho pensato dei seguenti modi:

A. Creare una parte 'ServiceIntegration' nel progetto web.

Si tratterebbe di un uomo di mezza tra le mie controllori e il mio livello di servizio WCF. La parte ServiceIntegration avrebbe parlato con il livello di servizio utilizzando DataContracts, e allo strato Web utilizzando ViewModels, ma avrebbe dovuto trasformare tra le DataContracts e ViewModels con un doppio senso trasformatore.

Inoltre, dal momento che la convalida IDataErrorInfo non sarebbe riutilizzabile, sarebbe necessario creare un validatore per DataContract troppo, che utilizza il trasformatore per convertire da ViewModel a DataContract, eseguire la convalida utilizzando IDataErrorInfo e restituire i suoi risultati. Questo sarebbe quindi essere utilizzato nei metodi di azione dei controllori (ad es if (!MyValidator.IsValid(viewModel)) return View();)

Diverse classi richiesto: xDataContract, xViewModel, xTransformer, xValidator

B. Creare una parte 'SessionIntegration' nel progetto web

Questa sarebbe una middle-man tra i controller (o qualcosa di accesso a sessione) e la sessione stessa. Tutto ciò che richiede l'accesso alla sessione sarebbe passare attraverso questa classe. DataContracts sarebbero stati utilizzati in tutta l'applicazione, a meno che non vengono memorizzate nella sessione. La parte SessionIntegration prenderebbe la responsabilità di trasformare il DataContract ad una qualche forma ISerializable, e ritorno. Nessuna ulteriore Validator è necessaria a causa dell'uso di dell'interfaccia IDataErrorInfo sul DataContract.

Diverse classi richiesto: xDataContract, xTransformer, xSerializableForm


. Nota: ci sarebbe ancora ViewModels giro in entrambi gli scenari, ma con (B) sarei in grado di ViewModels Compose dal DataContracts

(B) ha il vantaggio di non aver bisogno di un validatore in più.


Prima di andare fuori e implementare (A) / (B) completamente, mi piacerebbe un feedback. Al momento, sto iniziando a inclinarsi verso (B), invece, (A) potrebbe essere più flessibile. In entrambi i casi, sembra che modo troppo lavoro per quello che vale. Qualcun altro ha incontrato questo problema, sei d'accordo / disaccordo con me, e / o avete un altro modo per risolvere il problema?

Grazie,

James

È stato utile?

Soluzione

Senza andare via soffiato pieno di A o B, si potrebbe solo fare un'ISerializable generica involucro oggetto e mettere quelli nella tua 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);   
                }
            }
        }
    }

Altri suggerimenti

Come estensione della risposta fornita, ho aggiunto questi due metodi per facilitare la memorizzazione / recupero dei dati.

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

Questo lo rende un po 'più facile da impostare / ottenere i valori dalla sessione:

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

Per rendere ancora più semplice, aggiungere metodi di estensione:

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 l'uso come di seguito:

    MyDataContract c = ...;
    Session.SetWrapped("mykey", c);
    c = Session.GetWrapped<MyDataContract>("mykey");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top