You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:02:47