На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: maxim84_, juice
  
> Редактирование связанных объектов через EF в SQLite, C#, EF 6.2, SQLite, C# Winforms
    Здравствуйте.
    Суть: используя вышеперечисленные технологии, при чтении из базы данных связанных объектов после добавления, функция SingleOfDefault падает эксепшеном запрос содержит более одной записи.

    Подробности:
    1. Описание сущностей:
    Есть 3 связанных сущности - клиенты, телефоны и комментарии:
    ExpandedWrap disabled
      class Clients
          {
              public long Id { get; set; }
              public string Name { get; set; }
              public long RegionId { get; set; }
              public string Address { get; set; }
              public long Status { get; set; }
              public System.DateTime FirstCallDate { get; set; }
              public System.DateTime RecallDate { get; set; }
              public long Mark { get; set; }
          
              public virtual Regions Regions { get; set; }
              [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
              public virtual ICollection<Comments> Comments { get; set; }
              [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
              public virtual ICollection<Orders> Orders { get; set; }
              [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
              public virtual ICollection<Telephones> Telephones { get; set; }
          }
       
      class Comments
          {
              public long Id { get; set; }
              public System.DateTime CommentDate { get; set; }
              public string CommentText { get; set; }
              public long ClientId { get; set; }
          
              public virtual Clients Clients { get; set; }
          }
       
      class Telephones
          {
              public long Id { get; set; }
              public string Telephone { get; set; }
              public string Info { get; set; }
              public long ClientId { get; set; }
          
              public virtual Clients Clients { get; set; }
          }


    2. Используемый код
    Что именно происходит: добавляю клиента, к нему номер телефона и комментарий - все нормально.
    Редактирую этого клиента - добавляю к нему новый номер телефона - при вызове этого клиента функция SingleOfDefault падает эксепшеном запрос содержит более одной записи.
    Примечание: то же самое делаю с комментарием(т.е. к существующему клиенту добавляю комментарий) все нормально, не падает
    Код добавления(по сути одинаков для добавления комментария и телефона):
    ExpandedWrap disabled
      _t.Telephone = textBoxTelephone.Text;
      _t.Info = textBoxInfo.Text;
      _t.ClientId = Convert.ToInt32(comboBoxClients.SelectedValue);
       
      using (var db = new DatabaseContext())
                              {
                                      // наличие или отсутствие следующих двух строк ничего не меняет
                                      var cln = db.Clients.SingleOrDefault(c => c.Id == _t.ClientId);                                
                                      _t.Clients = cln;
       
                                  db.Telephones.Add(_t);
                                  db.SaveChanges();
                              }

    по комментам:
    ExpandedWrap disabled
      _com.ClientId = Convert.ToInt32(comboBoxClients.SelectedValue);
                  _com.CommentDate = dateTimePickerOtherDate.Value;
                  _com.CommentText = richTextBoxComment.Text;
       
      using (var db = new DatabaseContext())
                          {
                              db.Comments.Add(_com);
                              db.SaveChanges();
                          }

    После такого добавления вызывается функция:
    ExpandedWrap disabled
      public static ClientSample GetTodayUpdatedClient(long clientId)
              {
                  var dt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day + 1);
                  ClientSample _todayClient = null;
                  using (var db = new DatabaseContext())
                  {
                      var transactions = from c in db.Clients
                                         join r in db.Regions on c.RegionId equals r.Id /*join t in db.Telephones on c.Id equals t.ClientId*/                                  
                                         where c.RecallDate < dt && c.Id == clientId && c.Status == (int)ClientStatus.Actual
                                         select new ClientSample
                                         {
                                             ClientId =c.Id,
                                             ClientName = c.Name,
                                             RegionName = r.RegionName,
                                             Status = c.Status,
                                             FirstCallDate = c.FirstCallDate,
                                             RecallDate = c.RecallDate,
                                             Mark = c.Mark,
                                             //TelList = c.Telephones.ToList()
                                         };
                      if (transactions != null)
                          _todayClient = transactions.SingleOrDefault();
                  }
                  if(_todayClient != null)
                  {
                      using (var db = new DatabaseContext())
                      {
                          Clients tmpCl = db.Clients.Where(c => c.Id == _todayClient.ClientId).SingleOrDefault();
                          if(tmpCl != null)
                              _todayClient.TelList = tmpCl.Telephones.ToList();
                      }
                  }
                  
                  return _todayClient;
              }

    в таком варианте эксепшн не падает, но после перезагрузки проги появляется пару идентичных записей

    ExpandedWrap disabled
      public static ClientSample GetTodayUpdatedClient(long clientId)
              {
                  var dt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day + 1);
                  ClientSample _todayClient = null;
                  using (var db = new DatabaseContext())
                  {
                      var transactions = from c in db.Clients
                                         join r in db.Regions on c.RegionId equals r.Id
                                         join t in db.Telephones on c.Id equals t.ClientId                                  
                                         where c.RecallDate < dt && c.Id == clientId && c.Status == (int)ClientStatus.Actual
                                         select new ClientSample
                                         {
                                             ClientId =c.Id,
                                             ClientName = c.Name,
                                             RegionName = r.RegionName,
                                             Status = c.Status,
                                             FirstCallDate = c.FirstCallDate,
                                             RecallDate = c.RecallDate,
                                             Mark = c.Mark,
                                             TelList = c.Telephones.ToList()
                                         };
                      if (transactions != null)
                          _todayClient = transactions.SingleOrDefault();
                  }
                  
                  return _todayClient;
              }

    в этом варианте падает на SingleOrDefault.

    Также, появление дублирующихся записей наблюдается, если добавлять нового клиента с несколькими телефонами:
    ExpandedWrap disabled
       _c.Name = textBoxName.Text;
                  _c.RegionId = Convert.ToInt32(comboBoxRegion.SelectedValue);
                  _c.Address = textBoxAddress.Text;
                  _c.Mark = Convert.ToInt32(comboBoxMark.SelectedValue);
                  _c.FirstCallDate = dateTimePickerFirstCall.Value;
                  _c.RecallDate = dateTimePickerRecall.Value;
                  _c.Status = Convert.ToInt32(comboBoxStatus.SelectedValue);
       
      if (_isAdd)
                  {
                      using (var db = new DatabaseContext())
                      {
                          db.Clients.Add(_c);
                          db.SaveChanges();                    
                      }
                      using (var db = new DatabaseContext())
                      {
                          //_c = db.Clients.Last();
                          db.Clients.Attach(_c);
                      }
                  }
                  else
                  {
                      if (_wasClChanged)
                      {
                          using (var db = new DatabaseContext())
                          {
                              db.Clients.Attach(_c);
                              db.Entry(_c).State = EntityState.Modified;
                              db.SaveChanges();
                          }
                      }
                  }
                  bool wasTelUpdate = false;
                  foreach (Telephones tl in _telephonesOfCurClient)
                  {
                      if (tl.ClientId != _c.Id)
                      {
                          tl.ClientId = _c.Id;
                          wasTelUpdate = true;
                      }
                  }
                  if (wasTelUpdate)
                  {
                      using (var db = new DatabaseContext())
                      {
                          foreach (Telephones tl in _telephonesOfCurClient)
                          {
                              if (tl.Id == -1)
                              {
                                  db.Telephones.Add(tl);
                              }
                          }
                          db.SaveChanges();
                      }
                  }
                  bool wasCommUpdate = false;
                  foreach (Comments cm in _commentsOfCurrentClient)
                  {
                      if (cm.ClientId != _c.Id)
                      {
                          cm.ClientId = _c.Id;
                          wasCommUpdate = true;
                      }
                  }
                  if (wasCommUpdate)
                  {
                      using (var db = new DatabaseContext())
                      {
                          foreach (Comments cm in _commentsOfCurrentClient)
                          {
                              if (cm.Id == -1)
                              {
                                  db.Comments.Add(cm);
                              }
                          }
                          db.SaveChanges();
                      }
                  }

    Опять-же, с несколькими комментами если добавлять - все нормально.

    3. Итог:
    можно переписать функцию выбора, как в случае с не падающей singleordefault, но очень смущают эти дублирующие записи при кросстабличной выборке.
    Подскажите, пожалуйста, в чем может быть проблема ?

    За рекомендации по оптимизации кода тоже буду благодарен.


    Дополнительная информация к размышлению:
    - в таблицах базы данных повторяемых строк нет
    - проблема возникает при формировании промежуточного класса ClientSample после добавления 2 и более телефона к клиенту
    - повторяемых записей будет столько, сколько добавленных телефонов (к примеру, я добавляю 3 номера телефона к клиенту, тогда будет 3 идентичных на вид записи)
    - проверка методом Equals показала странную штуку: взял 2 идентичных на вид объекта, они не эквивалентны, взял их списки телефонов они не эквивалентны, по отдельности сравнил между собой соответствующие телефоны - они эквивалентны. Получается, разные ссылки на одни и те же данные, почему ?
    Сообщение отредактировано: bogdanchek -
      Разобрался.
      Проблема была в правильности составления запроса к базе данных, а именно:
      ExpandedWrap disabled
         var dt = new DateTime( DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day+1);
                    List<ClientSample> _todayClientsList = null;
                    using (var db = new DatabaseContext())
                    {
                        var transactions = from c in db.Clients
                                           join r in db.Regions on c.RegionId equals r.Id
                                           join t in db.Telephones on c.Id equals t.ClientId    // лишняя строка
                                           where c.RecallDate <  dt && c.Status == (int)ClientStatus.Actual
                                           select new ClientSample
                                           {
                                               ClientId = c.Id,
                                               ClientName = c.Name,
                                               RegionName = r.RegionName,
                                               Status = c.Status,
                                               FirstCallDate = c.FirstCallDate,
                                               RecallDate = c.RecallDate,
                                               Mark = c.Mark,
                                               TelList = c.Telephones.ToList()
                                           };
                        if (transactions != null)
                            _todayClientsList = transactions.ToList();
                        else
                            _todayClientsList = new List<ClientSample>();

      Этот джоин лишний.
      Как я предполагаю, этой лишней строкой я ставил в соответствие каждый номер телефона клиенту, т.е. делал связь 1 к 1(?), что и генерировало мне одного и того же клиента по количеству всех его телефонов.
      1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
      0 пользователей:


      Рейтинг@Mail.ru
      [ Script Execution time: 0,0935 ]   [ 15 queries used ]   [ Generated: 21.02.19, 07:47 GMT ]