2016-02-24 28 views
15

Próbuję wysłać obiekt przy użyciu WCF. Obiekt jest pobierany z DB za pomocą EF.Próba aktualizacji jednostki przy użyciu EF i wysłanie go przy użyciu WCF - właściwość powoduje wyjątek w scenariuszu aktualizacji

Jest to wyjątek uzyskać:

enter image description here

Dzieje się tak tylko w scenariuszu aktualizacji. Wstawianie działa idealnie.
Śledząc błąd, stwierdziłem, że problem dotyczy kolekcji (zwanej Travelers), którą ostatnio dodałem.

Oto co się dzieje, gdy próbuję oglądać swoją wartość w czasie wykonywania, po aktualizacji, przed wysłaniem zaktualizowaną podmiot przez WCF:

enter image description here

Oto klasa wykraczająca deklaracja własność (próbowałem odkomentowanie DataMember atrybut, ale to nie działa):

[DataContract] 
public class Travel : InsuredObject, ISaleEntity, ICloneable 
{  
    //[DataMember] 
    public virtual ICollection<Traveler> Travelers { get; set; } 
    ... 

Czytałem, że this.Configuration.ProxyCreationEnabled = false; i/lub this.Configuration.LazyLoadingEnabled = false; może go naprawić, ale nie można zmienić tych względów poza mną, a nawet gdy próbowałem grać z nimi - Mam kilka innych wyjątków ...

dodatkowe Kod:
Metoda aktualizacji:

public virtual TEntity CreateAndUpdate(int saleId, TEntity entity) { 
    var context = ((IObjectContextAdapter)this.Context).ObjectContext; 

    var objBaseSet = context.CreateObjectSet<TBase>(); 

    var entityBaseKey = context.CreateEntityKey(objBaseSet.EntitySet.Name, entity); 
    Object foundBaseEntity; 
    var baseExists = context.TryGetObjectByKey(entityBaseKey, out foundBaseEntity); 

    entity.Id = saleId; 

    if (!baseExists) { 
     this.GetDbSet<TEntity>().Add(entity); 
    } 

    this.objectContext.SaveChanges(); 

    return entity; 
} 

Pobieranie obiekt zawierający przed aktualizacją:

public virtual IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> where, bool brutalRefresh = false) { 

    IQueryable<TEntity> retObj = this.GetDbSet<TEntity>(); 
    if (where != null) { 
     retObj = retObj.Where(where); 
    } 

    if (brutalRefresh) { 
     var context = ((IObjectContextAdapter)this.Context).ObjectContext; 
     context.Refresh(RefreshMode.StoreWins, retObj); 
    } 

    return retObj; 
} 

... Cały ten kod jest wspólnym kodem z innymi projektami, które wysyłają i otrzymują ten sam podmiot co ja, to tylko Travel. Tity I dodaje, że mnie problemy powoduje, więc rozwiązanie szukam powinna składać się z 0 zmianami wspólnego kodeksu ..

Traveler klasy (w pełni):

[DataContract] 
    public class Traveler : ISaleEntity, ICloneable 
    { 
     [DataMember] 
     public int Id { get; set; } 

     [DataMember] 
     public string FirstName { get; set; } 

     [DataMember] 
     public string LastName { get; set; } 

     [DataMember] 
     public string IDNumber { get; set; } 

     [DataMember] 
     public DateTime? BirthDate { get; set; } 

     [DataMember] 
     public virtual ICollection<SelectedCoverage> SelectedCoverages { get; set; } 

     [NotMapped] 
     public List<MedicalQuestionnaireAnswer> MedicalQuestionnaireAnswers 
     { 
      get 
      { 
       if (string.IsNullOrWhiteSpace(DBMedicalQuestionnaireAnswers)) 
        return new List<MedicalQuestionnaireAnswer>(); 

       return DBMedicalQuestionnaireAnswers.Split(',') 
        .Select(c => (MedicalQuestionnaireAnswer)int.Parse(c)).ToList(); 
      } 
      set { DBMedicalQuestionnaireAnswers = string.Join(",", value.Select(m => (int)m)); } 
     } 

     [NotMapped] 
     public Genders Gender 
     { 
      get { return (Genders)DBGender; } 
      set { DBGender = (int)value; } 
     } 

     /// <summary> 
     /// NOTE! Do not use this property directly! use MedicalQuestionnaireAnswers instead 
     /// </summary> 
     [DataMember] 
     public string DBMedicalQuestionnaireAnswers { get; set; } 

     /// <summary> 
     /// NOTE! Do not use this property directly! use Gender instead 
     /// </summary> 
     [DataMember] 
     public int DBGender { get; set; } 

     public object Clone() 
     { 
      Traveler traveler = new Traveler(); 
      traveler.FirstName = this.FirstName; 
      traveler.LastName = this.LastName; 
      traveler.IDNumber = this.IDNumber; 
      traveler.BirthDate = this.BirthDate; 
      traveler.DBMedicalQuestionnaireAnswers = this.DBMedicalQuestionnaireAnswers; 
      traveler.Gender = this.Gender; 
      if (this.SelectedCoverages != null) 
      { 
       traveler.SelectedCoverages = this.SelectedCoverages.Select(sc => (SelectedCoverage)sc.Clone()).ToList(); 
      } 

      return traveler; 
     } 
    } 

    public static class TravelerExtension 
    { 

     /// <summary> 
     /// copy all the property except from the id and src defualt values 
     /// </summary> 
     /// <param name="dbTraveler"></param> 
     /// <param name="src"></param> 
     public static void CopyTravelerProperties(this Traveler target, Traveler src) 
     { 
      target.FirstName = src.FirstName; 
      target.LastName = src.LastName; 
      target.IDNumber = src.IDNumber; 
      target.BirthDate = src.BirthDate; 
      target.DBMedicalQuestionnaireAnswers = src.DBMedicalQuestionnaireAnswers; 
      target.DBGender = src.DBGender; 
      target.SelectedCoverages.CopySelectedCoveragesProperties(src.SelectedCoverages); 
     } 
    } 

    public static class TravelersExtension 
    { 

     /// <summary> 
     /// copy all the property except from the id and src defualt values 
     /// </summary> 
     /// <param name="dbTravelers"></param> 
     /// <param name="src"></param> 
     public static void CopyTravelersProperties(this ICollection<Traveler> target, ICollection<Traveler> src) 
     { 

      List<int> allTravelersIdsSrc = src.Select(t => t.Id).ToList(); 

      // remove ids exist target and not in src 
      target.ToList().RemoveAll(t => allTravelersIdsSrc.Contains(t.Id)); 


      target = target ?? new List<Traveler>(); 
      foreach (Traveler srcTraveler in src) 
      { 
       var targetTraveler = target.FirstOrDefault(targetTrv => srcTraveler.Id != 0 && targetTrv.Id == srcTraveler.Id); 
       // if not exist traveler with target traveler id in db 
       if (targetTraveler == null) 
       { 
        // add srcTraveler to target 
        target.Add(srcTraveler); 
       } 
       else 
       { 
        targetTraveler.CopyTravelerProperties(srcTraveler); 
       } 

      } 
     } 
    } 

Dalsze informacje:
Wyjątek okna bezpośredniego nie występuje, jeśli wywołanie ToList() przed próbą uzyskania wartości w bezpośrednim oknie. Sam problem nadal istnieje.

Próbując skomentować atrybut [DataMember] na:

public virtual ICollection<SelectedCoverage> SelectedCoverages { get; set; } 

w Traveler klasie nie miał wpływu.

Wyjątek:

enter image description here

Więcej informacji 2:

Jest tylko jeden podmiot, który powoduje, że wyjątek:

public class Quote : ISaleEntity, ICloneable {  
    ... 
     [DataMember] 
     public virtual Travel Travel { get; set; } 
    ... 

Kiedy zmienić powyższą [DataMember] do [IgnoreDataMember] - bez wyjątku.

ustawić wszystkie właściwości tej klasy [IgnoreDataMember]

[DataContract] 
    public class Travel : InsuredObject, ISaleEntity, ICloneable 
    { 
     [IgnoreDataMember] 
     //[DataMember] 
     public bool? IsFromIsrael { get; set; } 

     [IgnoreDataMember] 
     //[DataMember] 
     public virtual ICollection<Traveler> Travelers { get; set; } 

     [IgnoreDataMember] 
     public virtual Quote Quote { get; set; } 

      [IgnoreDataMember] 
     //[DataMember] 
     [NotMapped] 
     public List<int> DestinationsCodes 
     { 
      get 
      { 
       if (string.IsNullOrWhiteSpace(DBDestinationsCodes)) 
        return new List<int>(); 

       return DBDestinationsCodes.Split(',').Select(c => int.Parse(c)).ToList(); 
      } 

      set { DBDestinationsCodes = string.Join(",", value); } 
     } 

     /// <summary> 
     /// NOTE! Do not use this property directly! use DestinationsCodes instead 
     /// </summary> 
      [IgnoreDataMember] 
     //[DataMember] 
     public string DBDestinationsCodes { get; set; } 
     ... 

jednak wyjątek nadal występuje. Prawdopodobnie ze względu na klasę tej klasy dziedziczy po:

[DataContract] 
    [KnownType(typeof(Vehicle))] 
    [KnownType(typeof(Apartment))] 
    [KnownType(typeof(Travel))] 
    public class InsuredObject : ISaleEntity, ICloneable { 
     [Key] 
     [DataMember] 
     public int Id { get; set; } 

     [DataMember] 
     public int? OwnerTypeId { get; set; } 


     //navigation property 

     [DataMember] 
     public bool? HasShiabud { get; set; } 

     [DataMember] 
     [IgnoreDataMember] 
     public virtual Shiabud Shiabud { get; set; } 

     //[NotMapped] 
     //public virtual Proposal Proposal { get; set; } 

     //[DataMember] 
     [IgnoreDataMember] 
     public virtual ICollection<Coverage> Coverages { get; set; } 
     ... 

Jak mogę wysłać ten obiekt za pośrednictwem WCF?

+0

Jeśli (tymczasowo) zadzwonisz 'ToList()' na 'Travellers' przed zapisaniem, czy błąd zniknie? – cokeman19

+0

nope .. ten sam błąd –

+0

To ciekawe. Ogólnie rzecz biorąc, podejście do rozwiązania tego problemu jest takie, na jakie odpowiada @WicherVisser (lub alternatywnie za pomocą 'Include (...)', [like here] (http://stackoverflow.com/a/19928165/1197605)), dla które nazywając 'ToList()' jest dobrym testem. Czy możesz również opublikować swoją klasę "Traveler"? Być może coś w tym jest przyczyną wyjątku. Możesz także spróbować ustawić opcję VS, aby złamać wyrzucone wyjątki (w przeciwieństwie do nieobsługiwanych) i sprawdzić, czy coś innego zostanie wcześniej zgłoszone. – cokeman19

Odpowiedz

0

Czy możesz udostępnić część kodu w kontekście EF, który aktualizuje bazę danych?

Z komunikatu o błędzie zbieram, że to nie jest WCF, który gra sztuczki. Wydaje się raczej, że EF ObjectContent została usunięta przed wprowadzeniem zmian (metoda SaveChanges()). Może się tak zdarzyć, jeśli utrzymasz go przy życiu między wywołaniami metod WCF, np. posiadanie go jako zmiennej statycznej w twoim zapleczu i posiadania go w klauzuli using.

Powinno to działać w celu zastosowania luźnego ładowania do kontekstu DB dla EF 6 i nowszych, chociaż działa podobnie w starszych wersjach. (I wdrożone odziedziczony klasę, by radzić sobie z konfiguracjach. To może nie być wymagane)

public class DomainDbContext : DbContext 
{ 
    Configuration.LazyLoadingEnabled = false; 
} 
+0

Wielkie dzięki, zaktualizowałem pytanie, a teraz próbuję zrozumieć, co napisałeś, znaczy :) :(Wydaje się, że SaveChanges ma miejsce przed wysłaniem wcf .. –

+0

Którą wersję EF używasz? –

+1

5. potrzeba więcej znaków , więc powiem, że użyłem odpowiedniego znacznika w pytaniu: –

0

Poprzez wasze własności wirtualnej, co robisz jest leniwy załadunku.

Zamiast tego można wprowadzać właściwości do ICollection i zamiast tego ładować je.