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.

     

    -           

  • 相关阅读:
    Trajectory Estimation for Geo-Fencing Applications on Small-Size Fixed-Wing UAVs
    A RUGD Dataset for Autonomous Navigation and Visual Perception in Unstructured Outdoor Environments
    DISCOMAN: Dataset of Indoor SCenes for Odometry, Mapping and Navigation
    On Data Sharing Strategy for Decentralized Collaborative Visual-Inertial Simultaneous Localization and Mapping
    Robust High Accuracy Visual-Inertial-Laser SLAM System
    On Data Sharing Strategy for Decentralized Collaborative Visual-Inertial Simultaneous Localization and Mapping
    FLAME: Feature-Likelihood Based Mapping and Localization for Autonomous Vehicles
    Visual-Based Autonomous Driving Deployment from a Stochastic and Uncertainty-Aware Perspective
    Data Flow ORB-SLAM for Real-Time Performance on Embedded GPU Boards
    maven安装本地jar
  • 原文地址:https://www.cnblogs.com/sen068/p/5073718.html
Copyright © 2011-2022 走看看