Редактирование связанных объектов через EF в SQLite, C#
    , EF 6.2, SQLite, C# Winforms
  ![]()  | 
Наши проекты:
 Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту  | 
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS | 
| [216.73.216.5] | 
 
 | 
		
  | 
    Редактирование связанных объектов через EF в SQLite, C#
    , EF 6.2, SQLite, C# Winforms
  | 
         
         
         
          
           Сообщ.
           #1
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Здравствуйте.  
        
      Суть: используя вышеперечисленные технологии, при чтении из базы данных связанных объектов после добавления, функция SingleOfDefault падает эксепшеном запрос содержит более одной записи. Подробности: 1. Описание сущностей: Есть 3 связанных сущности - клиенты, телефоны и комментарии: ![]() ![]() 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 падает эксепшеном запрос содержит более одной записи. Примечание: то же самое делаю с комментарием(т.е. к существующему клиенту добавляю комментарий) все нормально, не падает Код добавления(по сути одинаков для добавления комментария и телефона): ![]() ![]() _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();                         } по комментам: ![]() ![]() _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();                     } После такого добавления вызывается функция: ![]() ![]() 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;         } в таком варианте эксепшн не падает, но после перезагрузки проги появляется пару идентичных записей ![]() ![]() 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. Также, появление дублирующихся записей наблюдается, если добавлять нового клиента с несколькими телефонами: ![]() ![]()  _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 идентичных на вид объекта, они не эквивалентны, взял их списки телефонов они не эквивалентны, по отдельности сравнил между собой соответствующие телефоны - они эквивалентны. Получается, разные ссылки на одни и те же данные, почему ?  | 
    
| 
         
         
         
          
           Сообщ.
           #2
          
          , 
          
         
         
        
       | 
    |
| 
         | 
      
          Разобрался. 
        
      Проблема была в правильности составления запроса к базе данных, а именно: ![]() ![]()  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(?), что и генерировало мне одного и того же клиента по количеству всех его телефонов.  |