实体框架:为 MYSQL 表生成自定义 ID

本文关键字:自定义 ID MYSQL 框架 实体 | 更新日期: 2023-09-27 18:34:46

我需要使用 MYSQL 表的实体框架生成一个自定义 ID(以字符串为前缀的数字序列(。以下示例说明了所需的内容

Id            name
ACT-DT-1      Activity DT
ACT-Dx-1      Activity Dx
ACT-Dx-2      Activity Dx
ACT-DT-2      Activity DT
ACT-DT-3      Activity DT

因此,如果新记录是 ACT-Dx,则 ID 必须是 ACT-Dx-3,或者如果它是 ACT-DT,则 ID 必须是 ACT-DT-4,或者如果它有新的前缀,例如 ACT-Dg,则 ID 必须是 ACT-Dg-1。

我能采取的最佳方法是什么,

实体框架:为 MYSQL 表生成自定义 ID

我建议你覆盖DbContext SaveChanges方法。不确定这是否是最佳实践,但它在我的一个项目中有效。

原则是,当您向 dbContext 添加新实体时,您会分配值为 -1、-2、-3 等的假 ID。在执行调用方法时SaveChanges这些假密钥将替换为正确的密钥值ReplaceIDs

这样做,您当然必须更新另一个相关实体中的引用。如果您将 foregn 键与主键命名不同,则可能会很棘手。我总是尝试为外键和主键使用相同的名称,不仅使用ID,还使用ActivityID

这是一个快速修改的版本,希望它不包含错误。

public partial class MyDbContext 
{
    public override int SaveChanges()
    {
        // Other custom logic          
        ReplaceIDs();          
    }
    /// <summary>
    /// Replaces IDs with calculated values when saving
    /// </summary>
    private void ReplaceIDs()
    {
        Debug.Print("**************************************************************");
        this.ObjectContext.DetectChanges();
        foreach (var dbEntityEntry in ChangeTracker.Entries().Where(x => x.State == EntityState.Added))
        {
            Type t =  GetEntityType(dbEntityEntry.Entity);
            string baseTypeName = t.Name;
            // Get the entity key name
            var keyName = this.ObjectContext.MetadataWorkspace
                            .GetEntityContainer(this.ObjectContext.DefaultContainerName, DataSpace.CSpace)
                            .BaseEntitySets
                            .First(x => x.ElementType.Name.Equals(baseTypeName))
                            .ElementType
                            .KeyMembers
                            .Select(key => key.Name)
                            .FirstOrDefault();
            string keyValue = null;  
            if (keyName!=null)
            {
                keyValue = dbEntityEntry.CurrentValues[keyName]; 
            }
            string newKey = null;
            //Custom logic for ID replacement
            if (t.Name.Equals("MyTable") && keyValue!= null)
            {
                string addedName = dbEntityEntry.CurrentValues["Name"];
                int keyValueNumber;  
                if (keyValue.HasValue && Int32.TryParse(keyValue, out keyValueNumber) && keyValueNumber <= 0) 
                {
                    //Calculate shomehow the last value of your set of IDs
                    //I store last IDs for different sets in extra table, e.g. IDSets  
                    var q = from x in this.IDSets where x.Name.Equals(addedName) && x.TableName.Equals("MyTable")
                            select x.LastID;
                    var lastKey = q.FirstOrDefault();
                    if (lastKey == null) throw new Exception("..."); //Handle somehow missing ID set, create new etc.
                    int? newID = null;
                    if (lastKey.LastID  == null)
                       newID=1;
                    else
                       newID= lastKey.LastID;
                    newKey = string.Format("{0}-{1}",addedName,++newID);                        
                    lastKey.LastID = newID;  
                }
            }
            //Key replacement
            if (newKey!=null)
            {
                int originalKey = (int)dbEntityEntry.CurrentValues[keyName];
                dbEntityEntry.CurrentValues[keyName] = newKey;
                Debug.Print("{0},{1} = {2} : {3}", baseTypeName, addedKontext, newKey, newIDWithKontext);
                //Refresh references
                string keyNameHelper=String.Format("{0}_", keyName);
                foreach (var e in ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged))
                {
                    Type tfix = e.Entity.GetType().BaseType;
                    foreach (var pname in e.CurrentValues.PropertyNames)
                    {
                        if ((pname.Equals(keyName) || pname.StartsWith(keyNameHelper)))
                        {
                            object o = e.CurrentValues[pname];
                            if (o!=null && o.GetType() == typeof(Int32) && ((int)o) == originalKey)
                            {
                                e.CurrentValues[pname] = newKey;
                                Debug.Print("FIX: {0},{1} = {2} >> {3}",tfix.Name, pname, originalKey, newKey);
                            }
                        }
                    }
                }
            }
        }
        this.ObjectContext.DetectChanges();
        Debug.Print("**************************************************************");
    }

    public static Type GetEntityType(object entity)
    {
        if (entity == null)
            return null;
        if (IsProxy(entity))
            return entity.GetType().BaseType;
        else
            return entity.GetType();
    }