ABA项目技术总结:IOC框架Autofac- 以及碰到的Bugs, sync同步数据为例

Autofac

 

数据库设计的坑:

1.因为把公用的属性Id,修改时间,修改人 等5个字段,放到base里面了,但是数据中每张表的主键叫sessionId,xxxId, 需要额外map,都叫id就省事了。

2.数据库到EF,数据中某些表名是复数形式.

而Domin映射的类中,复数代表一个集合。每次更新EF from DB之后,单个属性的映射会重命名为默认的表名字 也就是 ProgramTarget ProgramTargets, ProgramTarget 是domain对象,定义为单数形式了,ProgramTargets在我们C#代码中是用来表示集合的。

 

BUG1:

在保存不公开其关系的外键属性的实体时出错。EntityEntries 属性将返回 null,因为单个实体无法标识为异常的源。通过在实体类型中公开外键属性,可以更加轻松地在保存时处理异常。有关详细信息,请参阅 InnerException。
InnerException:{"“FK_BTPProgramTargets_BTPProgram”AssociationSet 中的关系处于“Deleted”状态。如果有多重性约束,则相应的“BTPProgramTargets”也必须处于“Deleted”状态。"}

 foreach (BTP btp in client.BTPs)
                    {
                        foreach (BTPProgram program in btp.BTPPrograms)
                        {

                            //Reset each BTPProgramTarget from requestModel
                            foreach (var btpSync in clientSync.Btps)
                            {
                                foreach (var programSync in btpSync.BTPPrograms)
                                {
                                    if (program.Id == programSync.ProgramId)
                                    {
                                        //Mapper.Map<ICollection<BTPProgramTargetSyncRequestModel>, ICollection<BTPProgramTarget>>(programSync.BTPProgramTargets,program.BTPProgramTargets);
                                        //SelfMapTargets(programSync.BTPProgramTargets, program.BTPProgramTargets);
                                        foreach (var targetSrc in programSync.BTPProgramTargets)
                                        {
                                            var targetDest = program.BTPProgramTargets.FirstOrDefault(t => !Guid.Equals(Guid.Empty, targetSrc.TargetId) && t.Id == targetSrc.TargetId);
                                            if (targetDest != null)
                                            {
                                                Mapper.Map<BTPProgramTargetSyncRequestModel, BTPProgramTarget>(targetSrc, targetDest);
                                                EntityLogger.UpdateEntity(targetDest);
                                            }
                                        }

                                    }
                                }
 
                            }

                        }
                    }

解决步骤:

1.刚开始,看到Deleted状态,猜想应该是domain中导航属性,集合之前可能有10条记录,但是传过来的requestModel只有5条,这样EF会去删掉5条记录,会涉及到delete。对比数据库的记录和请求的记录发现并不是这样。

2.排除其他因素,注释掉Mapper.Map<ICollection<BTPProgramTargetSyncRequestModel>, ICollection<BTPProgramTarget>>(programSync.BTPProgramTargets,program.BTPProgramTargets);只一行把requestModel ICollection转换为Domain ICollection代码后,正常工作,问题定位到map上。

3.自己写一个方法,手动map,成功更新数据库。

        private void SelfMapTargets(ICollection<BTPProgramTargetSyncRequestModel> syncBTPProgramTargets, ICollection<BTPProgramTarget> btpProgramTargets)
        {
            foreach (var btpProgramTarget in btpProgramTargets)
            {
                foreach (var syncBTPProgramTarget in syncBTPProgramTargets)
                {
                    if(Guid.Equals(btpProgramTarget.Id,syncBTPProgramTarget.TargetId))
                    {
                        btpProgramTarget.PercentofCorrectResponse = syncBTPProgramTarget.PercentofCorrectResponse;
                    }
                }
            }
        }

4.Debug,watch的发现,直接Maping List之后的结果,BTPProgramTarget对象的父对象BTPProgram被重置了null了,很奇怪,requestModel中并没有BTPProgram的属性,按道理不应该覆盖才对。 client的BTP模块有类似功能,查看相关代码,发现那儿是foreach给每个对象map的,不是map list,也改为foreach每个对象,map,问题解决,成功更新到数据库。

ABA项目技术总结:IOC框架Autofac- 以及碰到的Bugs, sync同步数据为例_第1张图片

foreach (var targetSrc in programSrc.ProgramTargets)
                        {
                            var targetDest = programDest.BTPProgramTargets.FirstOrDefault(t => !Guid.Equals(Guid.Empty, targetSrc.TargetId) && t.Id == targetSrc.TargetId);
                            if (targetDest != null)
                            {
                                Mapper.Map<BTPProgramTargetRequestModel, BTPProgramTarget>(targetSrc, targetDest);
                                EntityLogger.UpdateEntity(targetDest);
                            }
                            else
                            {
                                targetDest = Mapper.Map<BTPProgramTargetRequestModel, BTPProgramTarget>(targetSrc);
                                EntityLogger.CreateEntity(targetDest);
                                programDest.BTPProgramTargets.Add(targetDest);
                            }

                            foreach (var taskSrc in targetSrc.TATargetTasks)
                            {
                                var taskDest = targetDest.BTPTATargetTasks.FirstOrDefault(t => !Guid.Equals(Guid.Empty, taskSrc.TaskId) && t.Id == taskSrc.TaskId);
                                if (taskDest != null)
                                {
                                    Mapper.Map<BTPTATargetTaskRequestModel, BTPTATargetTask>(taskSrc, taskDest);
                                    EntityLogger.UpdateEntity(taskDest);
                                }
                                else
                                {
                                    taskDest = Mapper.Map<BTPTATargetTaskRequestModel, BTPTATargetTask>(taskSrc);
                                    EntityLogger.CreateEntity(taskDest);
                                    targetDest.BTPTATargetTasks.Add(taskDest);
                                }
                            }
                            
                        }

PS:btp那里遍历是因为这个是后台模块,可能会添加新的targets,所以要foreach,如果数据库不存在,就添加到EF的domain集合,而我这里,前台传递过来的数据<=数据库数据 数据库中软删除,所以假如数据库有10条记录,其中可能有4条是delete状态,前台不会得到,但是还需要保留在数据库中,我们在保存的时候,最方便的做法应该是 update方法执行前,获取client下子对象的所有数据,包含删除的,map对象的时候,只map客户端传过来的id,也就是delete=false的,然后把map后的domain保存,这样就不涉及EF删掉被软删除的数据。

 

2016/1/4

sync post的时候,还是报错, {"Violation of PRIMARY KEY constraint 'PK__SessionT__3214EC07C3324B04'. Cannot insert duplicate key in object

 

'dbo.SessionTargetDTTRecord'. The duplicate key value is (347ae60e-efb3-43eb-ada9-1dd2efa59b3f).\r\nThe statement has been

 

terminated."}

 

 

事实上,是get的数据原封不动的post过来的,却生成了insert语句,应该是update语句才对,后台的_SessionTargetDTTRecordRepository.add(entity)方法调试的时候,也没有走到,saveChanges方法调用后,却生成了insert语句好奇怪。

 

  1. 我尝试去删掉所有的,在重新添加,报下面的错误:

 ABA项目技术总结:IOC框架Autofac- 以及碰到的Bugs, sync同步数据为例_第2张图片

可能也是因为导航属性的值丢失导致的,然而,删掉重新添加毕竟很low的方式,而且,还报错了。都打算用EF的context.Database.ExecuteSqlCommand

方式,先生成SQL语句,然后执行了。中午Kewei吃完饭,我请他过来帮看看为什么会生成insert语句,不行的话,就生成sql语句去做。

 

有2个bugs导致了这个问题:

  1. Kewei调试时候发现,SessionTargetDTTRecord的domain对象id属性为0000-0000,f12过去发现,基类中有id,这个类中又重复定义了,所以给子类的id赋值了,而base.id却没有赋值.
  2. 我debug发现,autoMap SessionTarget对象的时候,没有把SessionTargetDTTRecords和SessionTargetTARecords给ignore,导致进行不必要map了,把导航属性的值便成null了,比如下图的sessionTarget.

 ABA项目技术总结:IOC框架Autofac- 以及碰到的Bugs, sync同步数据为例_第3张图片

-           

 

你可能感兴趣的:(ABA项目技术总结:IOC框架Autofac- 以及碰到的Bugs, sync同步数据为例)