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

时间:2014-12-20 20:30:08

标签: c# mysql entity-framework

我需要使用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。

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

1 个答案:

答案 0 :(得分:0)

我建议您覆盖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();
    }