如何使用Entity Framework实现Gmail联系人的去重存储与更新
实现Gmail联系人与数据库的去重同步方案
这个需求是典型的数据增量同步+去重场景,核心在于先找到Gmail联系人与数据库记录的唯一匹配依据,再针对性做增删改操作。我给你一步步拆解实现方案:
第一步:给POCO类添加唯一匹配标识
姓名存在重复风险,不能作为判断重复的依据,必须依赖Gmail返回的全局唯一联系人ID(每个Gmail联系人都有专属ID,你可以从contact.Id或对应API字段获取)。修改你的实体类:
public class CONTACTS_INFO { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int CI_ROWID { get; set; } public int CI_CONTACT_REFNO { get; set; } // 建议拆分存储号码和标签,方便后续比对修改 public string CI_PHONE_VALUE { get; set; } public string CI_PHONE_LABEL { get; set; } // 若需保留原拼接字段可继续保留,不影响核心逻辑 // public string CI_PHONE_NUMBERS { get; set; } } public class GMAIL_CONTACTS { public GMAIL_CONTACTS() { CONTACTS_INFO = new List<CONTACTS_INFO>(); } [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int GC_ROWID { get; set; } // 新增:Gmail联系人唯一ID,作为去重和匹配的核心依据 [Index(IsUnique = true)] // 数据库层面加唯一索引,双重防重复 public string GC_GMAIL_CONTACT_ID { get; set; } public string GC_CONTACT_NAME { get; set; } [ForeignKey("CI_CONTACT_REFNO")] public List<CONTACTS_INFO> CONTACTS_INFO { get; set; } }
拆分电话号码的原因是:如果只存拼接后的字符串,修改标签(比如从"手机"改成"工作手机")时,无法精准判断是修改还是新增,拆分后比对逻辑更清晰。
第二步:实现同步逻辑(去重+更新)
核心思路是先查后处理:对每个Gmail联系人,先通过唯一ID查询数据库是否存在,存在则更新,不存在则新增;同时处理电话号码的增删改。
修改你的方法代码:
using (var context = new MPContext()) { foreach (var contact in cr.GetContacts().Entries) { if (string.IsNullOrEmpty(contact.Name.FullName)) continue; // 1. 获取Gmail联系人的唯一ID(根据你的API实际字段调整,这里假设是contact.Id) string gmailContactId = contact.Id; if (string.IsNullOrEmpty(gmailContactId)) continue; // 确保有唯一标识 // 2. 查询数据库中是否存在该联系人,同时加载关联的电话号码 var existingContact = context.GMAIL_CONTACTS .Include(c => c.CONTACTS_INFO) // 必须Include,否则EF不会加载关联的电话号码 .FirstOrDefault(c => c.GC_GMAIL_CONTACT_ID == gmailContactId); if (existingContact == null) { // 3. 数据库无该联系人,执行新增 var newContact = new GMAIL_CONTACTS { GC_GMAIL_CONTACT_ID = gmailContactId, GC_CONTACT_NAME = contact.Name.FullName }; // 添加所有有效电话号码 foreach (var phonenumber in contact.Phonenumbers) { if (string.IsNullOrEmpty(phonenumber.Value)) continue; newContact.CONTACTS_INFO.Add(new CONTACTS_INFO { CI_PHONE_VALUE = phonenumber.Value, CI_PHONE_LABEL = phonenumber.Label ?? "默认" // 处理空标签场景 // 若需保留拼接字段:CI_PHONE_NUMBERS = $"{phonenumber.Value}{phonenumber.Label}" }); } context.GMAIL_CONTACTS.Add(newContact); } else { // 4. 数据库已有该联系人,先更新姓名(如果有变化) if (existingContact.GC_CONTACT_NAME != contact.Name.FullName) { existingContact.GC_CONTACT_NAME = contact.Name.FullName; context.Entry(existingContact).State = EntityState.Modified; } // 5. 同步电话号码:处理增删改 // 先把Gmail的号码转成便于比对的集合 var gmailPhones = contact.Phonenumbers .Where(p => !string.IsNullOrEmpty(p.Value)) .Select(p => new { Value = p.Value, Label = p.Label ?? "默认" }) .ToList(); // 数据库中已有的电话号码 var dbPhones = existingContact.CONTACTS_INFO.ToList(); // a. 删除数据库中存在但Gmail里已删除的号码 foreach (var dbPhone in dbPhones) { bool existsInGmail = gmailPhones.Any(gp => gp.Value == dbPhone.CI_PHONE_VALUE && gp.Label == dbPhone.CI_PHONE_LABEL); if (!existsInGmail) { context.CONTACTS_INFO.Remove(dbPhone); } } // b. 添加Gmail里新增但数据库没有的号码 foreach (var gmailPhone in gmailPhones) { bool existsInDb = dbPhones.Any(dbPhone => dbPhone.CI_PHONE_VALUE == gmailPhone.Value && dbPhone.CI_PHONE_LABEL == gmailPhone.Label); if (!existsInDb) { existingContact.CONTACTS_INFO.Add(new CONTACTS_INFO { CI_PHONE_VALUE = gmailPhone.Value, CI_PHONE_LABEL = gmailPhone.Label // CI_PHONE_NUMBERS = $"{gmailPhone.Value}{gmailPhone.Label}" }); } } // c. 可选:处理电话号码的修改(比如号码不变,标签更新) // foreach (var gmailPhone in gmailPhones) { // var existingPhone = dbPhones.FirstOrDefault(p => p.CI_PHONE_VALUE == gmailPhone.Value); // if (existingPhone != null && existingPhone.CI_PHONE_LABEL != gmailPhone.Label) { // existingPhone.CI_PHONE_LABEL = gmailPhone.Label; // context.Entry(existingPhone).State = EntityState.Modified; // } // } } } context.SaveChanges(); }
关键细节说明
- 唯一标识的必要性:必须用Gmail提供的唯一ID,避免因重名导致重复插入或错误更新。
- Include关联数据:查询现有联系人时一定要用
Include(c => c.CONTACTS_INFO),否则EF不会加载关联的电话号码,后续比对会失效。 - 双重防重复:代码层面判断+数据库唯一索引,即使代码逻辑有疏漏,数据库也会报错拦截重复数据。
- 电话号码同步策略:这里采用全量比对的方式,确保数据库与Gmail数据完全一致;如果只需要增量更新,也可以结合Gmail的更新时间戳优化,但复杂度会提升。
内容的提问来源于stack exchange,提问作者Asım Gündüz
相关产品推荐
相关产品推荐

