2010-05-26 10 views
8

OK, quiero asegurarme de cubrir mi situación y todo lo que he intentado exhaustivamente. Estoy bastante seguro de que lo que necesito/quiero se puede hacer, pero no he encontrado la combinación perfecta para el éxito.EF4 POCO WCF Problemas de serialización (sin carga lenta, proxy/sin proxy, referencias circulares, etc.)

estoy utilizando Entity Framework 4 RTM y su POCO apoyo. Estoy buscando para buscar una entidad (Config) que contiene una relación muchos a muchos con otra entidad (Aplicación). Giro de carga lenta y deshabilito la creación del proxy para el contexto y cargo explícitamente la propiedad de navegación (a través de .Include() o .LoadProperty()). Sin embargo, cuando se carga la propiedad de navegación (es decir, las aplicaciones se cargan para una configuración dada), los objetos de la aplicación que se cargaron ya contienen referencias a las configuraciones que se han llevado a la memoria. Esto crea una referencia circular .

Ahora sé que el DataContractSerializer que usa WCF puede manejar referencias circulares, estableciendo el parámetro preserveObjectReferences en true. Lo intenté con un par de implementaciones de atributos diferentes que encontré en línea. Es necesario para evitar el error "el gráfico del objeto contiene referencias circulares y no se puede serializar". Sin embargo, no impide la serialización de todo el gráfico, ida y vuelta entre Config y App.

Si lo invoco a través de WcfTestClient.exe, recibo una excepción de stackoverflow (ha!) Del cliente y me manchan. Obtengo resultados diferentes de diferentes entornos de invocación (la prueba de unidad C# con una referencia local al servicio web parece funcionar bien, aunque todavía puedo explorar sin parar entre las configuraciones y las aplicaciones, pero llamar desde un entorno de fusión fría solo devuelve la primera configuración en la lista y errores en los demás.) Mi objetivo principal es tener una representación serializada del gráfico que cargo explícitamente de EF (es decir, lista de configuraciones, cada una con sus aplicaciones, pero ninguna aplicación vuelve a la navegación de configuración)

NOTA: También he intentado utilizar la técnica ProxyDataContractResolver y mantener habilitada la creación del proxy desde mi contexto. Esto explota quejándose de tipos desconocidos encontrados. Leí que ProxyDataContractResolver no funcionaba completamente en Beta2, pero debería funcionar en RTM.

Por alguna referencia, aquí es más o menos la forma en que estoy consultar los datos en el servicio:

var repo = BootStrapper.AppCtx["AppMeta.ConfigRepository"] as IRepository<Config>; 
repo.DisableLazyLoading(); 
repo.DisableProxyCreation(); 

//var temp2 = repo.Include(cfg => cfg.Apps).Where(cfg => cfg.Environment.Equals(environment)).ToArray(); 
var temp2 = repo.FindAll(cfg => cfg.Environment.Equals(environment)).ToArray(); 
foreach (var cfg in temp2) 
{ 
    repo.LoadProperty(cfg, c => c.Apps); 
} 

return temp2; 

creo que el quid de mi problema es cuando se carga hasta propiedades de navegación para objetos POCO de Entity Framework 4, prepobla propiedades de navegación para objetos que ya están en la memoria. Esto, a su vez, ensucia la serialización de WCF, a pesar de todos los esfuerzos realizados para manejar adecuadamente las referencias circulares.

Sé que es mucha información, pero realmente está en mi camino de seguir adelante con EF4/POCO en nuestro sistema. He encontrado varios artículos y blogs tocando sobre estos temas, pero por mi vida, no puedo resolver este problema. Siéntase libre de hacer preguntas y ayudarme a hacer una lluvia de ideas sobre esta situación.

PS: Por el bien de ser exhaustiva, yo estoy inyectando los servicios WCF mediante la acumulación HEAD de Spring.NET para la solución de Spring.ServiceModel.Activation.ServiceHostFactory. Sin embargo, no creo que este sea el origen del problema.

EDIT: La clase ProxyDataContractResolver funciona correctamente si no tengo las referencias circulares. (es decir: hago que el setter de App.Configs sea privado, lo que impide la serialización de la propiedad.) Parece que explota cuando entra en Configs a través del objeto de la aplicación; no parecen ser reconocidos como el mismo tipo que las configuraciones de nivel superior.

EDIT2: Parece que EF o WCF no reconocen que las entidades son realmente iguales. es decir: 'Config' es lo mismo que un 'Config.Apps [x] .Configs [y]' en particular. Las claves de entidad están configuradas correctamente en el CSDL para cada modelo y he reemplazado la función Equals() para comparar entidades en función de su propiedad 'Id'. Esto se ajusta a los síntomas ya que no se genera ningún error de referencia circular, sin embargo, es de hecho una referencia circular (y explota WcfTestClient.exe) Y el ProxyDataContractResolver explota cuando alcanza el nivel 'Config.Apps [x] .Configs [y]' de las configuraciones. (No sabe cómo asignar un proxy de configuración. ProxyDataContractResolver funciona de otro modo. Es como si supiera cómo manejar la ronda inicial de entidades, pero el segundo nivel lo considera como entidades diferentes.)

Wow, puedo sé prolijo ¡Lo siento amigos!

+0

tengo la misma exacta pregunta/problema ahora. Entity Framework 5.0. El modelo de entidad funciona, pero la serialización parece no darse cuenta de que no debería volver atrás cuando se usa la relación de muchos a muchos –

+0

Tenía el mismo problema, me lo resolví. Aquí 'la solución: http://stackoverflow.com/a/17063364/1386781 –

+0

WCFTestClient no puede manejar referencias circulares. Ver este [SO hilo] [1] [1]: http://stackoverflow.com/questions/8686960/wcftestclient-exe-not-able-to-handle-circular-reference – MickyD

Respuesta

0

Intenta configurar myContext.ContextOptions.ProxyCreationEnabled = false;

Si el problema se resuelve (como la mía), entonces no ha seguido los pasos mencionados en: http://msdn.microsoft.com/en-us/library/ee705457.aspx

Esto resolvió el problema para mí.

+0

Lo he hecho de ambas maneras: "Apago la carga diferida y deshabilito la creación del proxy para el contexto y cargo explícitamente la propiedad de navegación (a través de .Include() o .LoadProperty()). Sin embargo, cuando se carga la propiedad de navegación (es decir, las aplicaciones se cargan para una configuración dada), los objetos de la aplicación que se cargaron ya contienen referencias a las configuraciones que se han llevado a la memoria. Esto crea una referencia circular ". – kdawg

1

hrmm, puede que no haya entendido completamente el problema, pero cada vez que encuentro referencias circulares con WCF, la respuesta es cambiar [DataContract] en las clases ofensivas a [DataContract (IsReference = true)].

Esto es una gran ayuda en comparación con todos los juegos de resolución de contratos que se necesitaban antes de WCF 3.5 SP1.

Espero que esto ayude.

2

Es posible que desee comprobar mi blog post on this specific scenario - por favor envíeme un correo electrónico si no ayuda a solucionar su situación actual! También incluí una solución de muestra.

Envíenos sus comentarios de cualquier manera, realmente me gustaría saber de más personas sobre este tema en particular, especialmente con los problemas de implementación en el cliente final.

+0

Hola Rob, probé la solución de MSDN y pude devolver una entidad, pero no sin sus objetos a otra tabla. Say Books -> Autores, solo obtengo libros pero no tengo los autores. ¿Es porque no fueron serializados? –

+0

@LeoLuis ¿Podría tener habilitada la carga diferida? – RobS

1

Se enfrentó al mismo problema hoy y usó Value Injecter para resolverlo. Es tan simple como:

var dynamicProxyMember = _repository.FindOne<Member>(m=>m.Id = 1); 
var member = new Member().InjectFrom(dynamicProxyMember) as Member; 

Nos podía permitirse la desactivación ProxyCreation

+1

No tienes idea de cuánto tiempo me salvaste hoy. – CoachNono

+0

contento de haber ayudado a un compañero desarrollador @ CoachNono – Korayem

0

Puede utilizar el ApplyDataContractResolverAttribute y una ProxyDataContractResolver junto con el CyclicReferencesAwareAttribute. Al principio esto produce error como éste - como si no se especifica en absoluto DataContractResolver:

Tipo 'System.Data.Entity.DynamicProxies.Whatever_E6 ...... A9' con el nombre de contrato de datos 'Whatever_E6 ...... A9: http: //schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies 'no se espera. Considere el uso de un DataContractResolver o agregue cualquier tipo no conocido de forma estática a la lista de tipos conocidos, por ejemplo, utilizando el atributo KnownTypeAttribute o agregándolos a la lista de tipos conocidos pasados ​​a DataContractSerializer.

Funciona con un simple cambio.

En ApplyCyclicDataContractSerializerOperationBehavior, los constructores para el DataContractSerializer también deben pasar en DataContractResolver. Esto queda fuera de todas las versiones que he visto en línea.

Ejemplo:

public class ApplyCyclicDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior 
{ 
    private readonly Int32 _maxItemsInObjectGraph; 
    private readonly bool _ignoreExtensionDataObject; 

    public ApplyCyclicDataContractSerializerOperationBehavior(OperationDescription operationDescription, Int32 maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences) 
     : base(operationDescription) 
    { 
     _maxItemsInObjectGraph = maxItemsInObjectGraph; 
     _ignoreExtensionDataObject = ignoreExtensionDataObject; 
    } 

    public override XmlObjectSerializer CreateSerializer(Type type, String name, String ns, IList<Type> knownTypes) 
    { 
     return new DataContractSerializer(type, name, ns, knownTypes, 
      _maxItemsInObjectGraph, 
      _ignoreExtensionDataObject, 
      true, 
      null /*dataContractSurrogate*/, 
      DataContractResolver); // <----------------------------- 
    } 

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes) 
    { 
     return new DataContractSerializer(type, name, ns, knownTypes, 
      _maxItemsInObjectGraph, 
      _ignoreExtensionDataObject, 
      true, 
      null /*dataContractSurrogate*/, 
      DataContractResolver); // <----------------------------- 
    } 
} 
Cuestiones relacionadas