zoukankan      html  css  js  c++  java
  • ABA项目技术总结:IOC框架Autofac- 以及碰到的Bugs, sync同步数据为例

    Autofac 一个依赖注入框架

    protected void Application_Start()
            {
                GlobalConfiguration.Configure(AutofacWebAPI.Initialize);
            }
    

      

    public class AutofacWebAPI
        {
            public static void Initialize(HttpConfiguration config)
            {
                Initialize(config, RegisterServices(new ContainerBuilder()));
            }
    
            public static void Initialize(HttpConfiguration config, IContainer container)
            {
    
                config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
            }
    
            private static IContainer RegisterServices(ContainerBuilder builder)
            {
                builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    
    
                // EF DbContext
                builder.RegisterType<ABATrackerAgencyDataContext>()
                       .As<DbContext>()
                       .InstancePerRequest();
    
                // Register repositories by using Autofac's OpenGenerics feature
                // More info: http://code.google.com/p/autofac/wiki/OpenGenerics
                builder.RegisterGeneric(typeof(EntityRepository<,>))
                       .As(typeof(IEntityRepository<,>))
                       .InstancePerRequest();
    
                // Services
                builder.RegisterType<DesCryptService>()
                    .As<ICryptoService>()
                    .InstancePerRequest();
    
                builder.RegisterType<MembershipService>()
                    .As<IMembershipService>()
                    .InstancePerRequest();
    
                builder.RegisterType<ProgramService>()
                    .As<IProgramService>()
                    .InstancePerRequest();
    
                builder.RegisterType<ClientService>()
                    .As<IClientService>()
                    .InstancePerRequest();
    
                builder.RegisterType<BTPService>()
                    .As<IBTPService>()
                    .InstancePerRequest();
    
                builder.RegisterType<TherapySessionService>()
                    .As<ITherapySessionService>()
                    .InstancePerRequest();
    
    
                return builder.Build();
            }
        }
    
    public class ProgramService : IProgramService
        {
            private readonly IEntityRepository<Program, Guid> _programRepository;
            private readonly IEntityRepository<ProgramGoal, Guid> _programGoalRepository;
            private readonly IEntityRepository<ProgramTarget, Guid> _programTargetRepository;
            private readonly IEntityRepository<TATargetTask, Guid> _taTargetTaskRepository;
    
            public ProgramService(
                IEntityRepository<Program, Guid> programRepository,
                IEntityRepository<ProgramGoal, Guid> programGoalRepository,
                IEntityRepository<ProgramTarget, Guid> programTargetRepository,
                IEntityRepository<TATargetTask, Guid> taTargetTaskRepository)
            {
                this._programRepository = programRepository;
                this._programGoalRepository = programGoalRepository;
                this._programTargetRepository = programTargetRepository;
                this._taTargetTaskRepository = taTargetTaskRepository;
            }
    
            public Program GetProgram(Guid Id)
            {
                var program = _programRepository.AllIncluding(
                    p => p.ProgramGoals, 
                    p => p.ProgramTargets, 
                    p => p.ProgramTargets.Select(t => t.TATargetTasks))
                    .Where(p => p.Id == Id && p.Deleted == false)
                    .FirstOrDefault();
    
                return program;
            }
    
            public bool CheckUniqueForProgramName(Guid id, string programName)
            {
                return !_programRepository.GetAll().Any(p => p.ProgramName == programName && p.Id != id);
            }
    
        }
        [Authorize(Roles="Admin,BCBA")]
        public class ProgramsController : ApiController
        {
            private readonly IProgramService _programService;
            private readonly IMembershipService _membershipService;
    
            public ProgramsController(IProgramService programService, IMembershipService membershipService)
            {
                this._programService = programService;
                this._membershipService = membershipService;
            }
    
            [HttpGet]
            public bool CheckProgramName(Guid id, string programName)
            {
                return _programService.CheckUniqueForProgramName(id, programName);
            }
    
            public ProgramDto GetProgram(Guid id)
            {
                var program = _programService.GetProgram(id);
                if (program == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
    
                program.ProgramGoals = program.ProgramGoals.Where(g => g.Deleted == false).ToList();
                program.ProgramTargets = program.ProgramTargets.Where(t => t.Deleted == false).OrderBy(t => t.StatusIndicator).ThenBy(t => t.TargetTypeCode).ThenBy(t => t.TargetName).ThenByDescending(t=>t.MasteredDate).ToList();
    
                foreach (var target in program.ProgramTargets)
                {
                    target.TATargetTasks = target.TATargetTasks.Where(t => t.Deleted == false).ToList();
                }
    
                return Mapper.Map<Program, ProgramDto>(program);
            }
    
            public IEnumerable<ProgramSelectionDto> GetProgramsByDomains(string domainIds)
            {
                var programs = _programService.GetProgramsByDomains(domainIds);
                return Mapper.Map<IEnumerable<Program>, IEnumerable<ProgramSelectionDto>>(programs);
            }
    
           
            [EmptyParameterFilter("requestModel")]
            public HttpResponseMessage PutProgram(Guid id, ProgramRequestModel requestModel)
            {
                var program = _programService.GetProgram(id);
                if (program == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
    
                var httpStatusCode = HttpStatusCode.OK;
                var updatedProgramResult = _programService.UpdateProgram(requestModel.ToProgram(program));
                if (!updatedProgramResult.IsSuccess)
                {
                    httpStatusCode = HttpStatusCode.Conflict;
                }
    
                var response = Request.CreateResponse(httpStatusCode,
                    Mapper.Map<Program, ProgramDto>(updatedProgramResult.Entity));
                return response;
            }
    
            public HttpResponseMessage DeleteProgram(Guid id)
            {
                var program = _programService.GetProgram(id);
                if (program == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
    
                var programRemoveResult = _programService.RemoveProgram(program);
                if (!programRemoveResult.IsSuccess)
                {
                    return new HttpResponseMessage(HttpStatusCode.Conflict);
                }
    
                return new HttpResponseMessage(HttpStatusCode.NoContent);
            }
        }

    数据库设计的坑:

    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,问题解决,成功更新到数据库。

    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). The statement has been

     

    terminated."}

     

     

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

     

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

     

    可能也是因为导航属性的值丢失导致的,然而,删掉重新添加毕竟很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.

     

    -           

  • 相关阅读:
    swift 第十四课 可视化view: @IBDesignable 、@IBInspectable
    swift 第十三课 GCD 的介绍和使用
    swift 第十二课 as 的使用方法
    swift 第十一课 结构体定义model类
    swift 第十课 cocopod 网络请求 Alamofire
    swift 第九课 用tableview 做一个下拉菜单Menu
    swift 第八课 CollectView的 添加 footerView 、headerView
    swift 第七课 xib 约束的优先级
    swift 第六课 scrollview xib 的使用
    swift 第五课 定义model类 和 导航栏隐藏返回标题
  • 原文地址:https://www.cnblogs.com/sen068/p/5073718.html
Copyright © 2011-2022 走看看