详解EFCore中的导航属性

  使用了这么久的EntityFrameworkCore框架,今天想来就其中的一个部分来做一个知识的梳理,从而使自己对于整个知识有一个更加深入的理解,如果你对EFCore中的实体关系不熟悉你需要有一个知识的预热,这样你才能够更好的去理解整个知识,在建立好了这些实体之间的关系以后,我们可以通过使用InClude、ThenInclude这些方法来进行快速获得对应关联实体数据,用起来确实十分的方便,这里我们将通过一系列的例子来进行说明。

    1 单独使用Include

  在介绍这个方法之前,我来先贴出实体之间的关联关系,假设这里有三个相互关联的实体VehicleWarranty、WarrantyWarningLevel、VehicleWarrantyRepairHistory这三个实体后面两个都是第一个的子级并且并且VehicleWarranty、WarrantyWarningLevel之间的关系是1对1的关系,VehicleWarranty和VehicleWarrantyRepairHistory之间是1:N的关系,即1对多的关系,我们这里贴出具体的Model,从而方便后面分析具体的代码。

    /// <summary>
    /// 车辆三包信息(DCSService)
    /// </summary>
    public class VehicleWarranty : Entity<Guid> {
        public VehicleWarranty() {
            Details = new List<VehicleWarrantyRepairHistory>();
        }
 
        //车辆售后档案
        public Guid VehicleSoldId { get; set; }
 
        //VIN
        [Required]
        [MaxLength(EntityDefault.FieldLength_50)]
        public string Vin { get; set; }
 
        //产品分类
        public Guid? ProductCategoryId { get; set; }
 
        //产品分类编号
        [MaxLength(EntityDefault.FieldLength_50)]
        public string ProductCategoryCode { get; set; }
 
        //产品分类名称
        [MaxLength(EntityDefault.FieldLength_100)]
        public string ProductCategoryName { get; set; }
 
        //车牌号
        [MaxLength(EntityDefault.FieldLength_50)]
        public string LicensePlate { get; set; }
 
        //发动机号
        [MaxLength(EntityDefault.FieldLength_50)]
        public string EngineCode { get; set; }
 
        //变速箱号
        [MaxLength(EntityDefault.FieldLength_50)]
        public string TransmissionSn { get; set; }
 
        //开发票日期
        public DateTime InvoiceDate { get; set; }
 
        //行驶里程
        public int Mileage { get; set; }
 
        //是否三包期内
        public bool? IsInWarranty { get; set; }
 
        //预警等级
        public Guid? WarningLevelId { get; set; }
 
        public WarrantyWarningLevel WarningLevel { get; set; }
 
        //等级编号
        [MaxLength(EntityDefault.FieldLength_50)]
        public string LevelCode { get; set; }
 
        //等级名称
        [MaxLength(EntityDefault.FieldLength_100)]
        public string LevelName { get; set; }
 
        //预警内容
        [MaxLength(EntityDefault.FieldLength_800)]
        public string WarningComment { get; set; }
 
        //累计维修天数
        public int TotoalRepairDays { get; set; }
 
        //售出后60天/3000KM内严重故障次数
        public int? FNum { get; set; }
 
        //严重安全性能故障累计次数
        public int? GNum { get; set; }
 
        //发动机总成累计更换次数
        public int? HNum { get; set; }
 
        //变速箱总成累计更换次数
        public int? INum { get; set; }
 
        //发动机主要零件最大更换次数
        public int? JNum { get; set; }
 
        //变速箱主要零件最大更换次数
        public int? KNum { get; set; }
 
        //同一主要零件最大更换次数
        public int? LNum { get; set; }
 
        //同一产品质量问题最大累计次数(部件+故障+方位)
        public int? MNum { get; set; }
 
        //同一产品质量问题最大累计次数
        public int? NNum { get; set; }
 
        public List<VehicleWarrantyRepairHistory> Details { get; set; }
    }
 
/// <summary>
    /// 三包预警等级(DCS)
    /// </summary>
    public class WarrantyWarningLevel : Entity<Guid> {
        //等级编号
        [Required]
        [MaxLength(EntityDefault.FieldLength_50)]
        public string Code { get; set; }
 
        //等级名称
        [Required]
        [MaxLength(EntityDefault.FieldLength_100)]
        public string Name { get; set; }
 
        //颜色
        [Required]
        [MaxLength(EntityDefault.FieldLength_50)]
        public string Color { get; set; }
 
        //备注
        [MaxLength(EntityDefault.FieldLength_200)]
        public string Remark { get; set; }
    }
 
 
    /// <summary>
    /// 车辆三包信息维修履历(DCSService)
    /// </summary>
    public class VehicleWarrantyRepairHistory : Entity<Guid> {
        //车辆三包信息
        [Required]
        public Guid VehicleWarrantyId { get; set; }
 
        public VehicleWarranty VehicleWarranty { get; set; }
 
        //VIN
        [Required]
        [MaxLength(EntityDefault.FieldLength_50)]
        public string Vin { get; set; }
 
        //维修合同
        public Guid RepairContractId { get; set; }
 
        //维修合同编号
        [Required]
        [MaxLength(EntityDefault.FieldLength_50)]
        public string RepairContractCode { get; set; }
 
        //处理时间
        public DateTime? DealTime { get; set; }
 
        //经销商
        public Guid DealerId { get; set; }
 
        //经销商编号
        [Required]
        [MaxLength(EntityDefault.FieldLength_50)]
        public string DealerCode { get; set; }
 
        //经销商名称
        [Required]
        [MaxLength(EntityDefault.FieldLength_100)]
        public string DealerName { get; set; }
 
        //履历来源
        public VehicleWarrantyRepairHistorySource Source { get; set; }
 
        //累计维修天数
        public int? TotoalRepairDays { get; set; }
 
        //售出后60天/3000KM内严重故障次数
        public int? FNum { get; set; }
 
        //严重安全性能故障累计次数
        public int? GNum { get; set; }
 
        //发动机总成累计更换次数
        public int? HNum { get; set; }
 
        //变速箱总成累计更换次数
        public int? INum { get; set; }
 
        //发动机主要零件最大更换次数
        public int? JNum { get; set; }
 
        //变速箱主要零件最大更换次数
        public int? KNum { get; set; }
 
        //同一主要零件最大更换次数
        public int? LNum { get; set; }
 
        //同一产品质量问题最大累计次数(部件+故障+方位)
        public int? MNum { get; set; }
 
        //同一产品质量问题最大累计次数
        public int? NNum { get; set; }
    }

  这里我们贴出第一个简单的查询示例,通过Include方法来一下子查询出关联的三包预警等级这个实体,在我们的例子中我们返回的结果是带分页的,而且会根据前端传递的Dto来进行过滤,这里我们来看这段代码怎么实体。

/// <summary>
        /// 查询车辆三包信息
        /// </summary>
        /// <param name="input">查询输入</param>
        /// <param name="pageRequest">分页请求</param>
        /// <returns>带分页的三包预警车辆信息</returns>
        public async Task<Page<GetVehicleWarrantiesOutput>> GetVehicleWarrantiesAsync(GetVehicleWarrantiesInput input, PageRequest pageRequest) {           
            var queryResults = _vehicleWarrantyRepository.GetAll()
                .Include(v => v.WarningLevel)
                .Where(v => _vehicleSoldRepository.GetAll().Any(vs => vs.Status == VehicleStatus.实销完成 && v.Vin == vs.Vin));
            var totalCount = await queryResults.CountAsync();
            var pagedResults = await queryResults.ProjectTo<GetVehicleWarrantiesOutput>(_autoMapper.ConfigurationProvider).PageAndOrderBy(pageRequest).ToListAsync();
            return new Page<GetVehicleWarrantiesOutput>(pageRequest, totalCount, pagedResults);
        }

  在这里我们看到了通过一个Include就能够查询出关联的实体,为什么能够实现,那是因为在VehicleWarranty实体中存在WarrantyWarningLevel实体的外键,并且这里还增加了外键关联的实体,这样才能够正确使用InClude方法,并且这个InClude方法只能够以实体作为参数,不能以外键作为参数,到了这里我想提出一个问题,这里最终生成的SQL(SqlServer数据库)是left join 还是inner join呢?在看完后面的分析之前需要思考一下。

select top (20)
  [v].[EngineCode],
  [v].[GNum],
  [v].[Id],
  [v.WarningLevel].[Color] as [LevelColor],
  [v].[LevelName],
  [v].[LicensePlate],
  [v].[ProductCategoryName],
  [v].[TotoalRepairDays],
  [v].[Vin]
from [VehicleWarranty] as [v]
  left join [WarrantyWarningLevel] as [v.WarningLevel] on [v].[WarningLevelId] = [v.WarningLevel].[Id]
where EXISTS(
    select 1
    from [VehicleSold] as [vs]
    where ([vs].[Status] = 7) and ([v].[Vin] = [vs].[Vin]))
order by [v].[Vin]

  这里我们看到生成的SQL语句是left join ,那么这里为什么不是inner join呢?这里先给你看具体的答案吧?这里你看懂了吗?问题就处在我这里建立的外键是可为空的 public Guid? WarningLevelId { get; set; }、如果是不可为空的外键那么生成的SQL就是inner join这个你可以亲自尝试。另外有一个需要提醒的就是,如果你像上面的实体中建立了VehicleWarranty、WarrantyWarningLeve之间的关系的话,迁移到数据库会默认生成外键约束,这个在使用的时候需要特别注意,但是如果你只是添加了外键而没有添加对应的外键同名的实体是不会生成外键约束关系的,这个暂时不理解里面的实现机制。

  2 主清单使用Include

  刚才介绍的是1对1的关联关系,那么像VehicleWarranty、VehicleWarrantyRepairHistory之间有明显的主清单关系,即一个VehicleWarranty对应多个VehicleWarrantyRepairHistory的时候使用InClude方法会生成什么样的SQL语句呢?这里我也贴出代码,然后再来分析生成的SQL语句。

/// <summary>
        /// 查询特定的三包预警车辆信息
        /// </summary>
        /// <param name="id">特定Id</param>
        /// <returns>特定的三包预警车辆信息</returns>
        public async Task<GetVehicleWarrantyWithDetailsOutput> GetVehicleWarrantyWithDetailsAsync(Guid id) {
            var query = await _vehicleWarrantyRepository.GetAll()
                .Include(v => v.WarningLevel)
                .Include(v => v.Details)
                .SingleOrDefaultAsync(v => v.Id == id);
            if (null == query) {
                throw new ValidationException("找不到当前特定的三包预警车辆信息");
            }
 
            var retResult = ObjectMapper.Map<GetVehicleWarrantyWithDetailsOutput>(query);
            return retResult;
        }

  这里使用了两个InClude方法,那么EFCore会怎么生成这个SQL呢?通过查询最终的SQL我们发现EFCore在处理这类问题的时候是分开进行查询,然后再合并到查询的实体中去的,所以在这个查询的过程中生成的SQL如下:

select top (2)
  [v].[Id],
  [v].[EngineCode],
  [v].[FNum],
  [v].[GNum],
  [v].[HNum],
  [v].[INum],
  [v].[InvoiceDate],
  [v].[IsInWarranty],
  [v].[JNum],
  [v].[KNum],
  [v].[LNum],
  [v].[LevelCode],
  [v].[LevelName],
  [v].[LicensePlate],
  [v].[MNum],
  [v].[Mileage],
  [v].[NNum],
  [v].[ProductCategoryCode],
  [v].[ProductCategoryId],
  [v].[ProductCategoryName],
  [v].[TotoalRepairDays],
  [v].[TransmissionSn],
  [v].[VehicleSoldId],
  [v].[Vin],
  [v].[WarningComment],
  [v].[WarningLevelId],
  [v.WarningLevel].[Id],
  [v.WarningLevel].[Code],
  [v.WarningLevel].[Color],
  [v.WarningLevel].[Name],
  [v.WarningLevel].[Remark]
from [VehicleWarranty] as [v]
  left join [WarrantyWarningLevel] as [v.WarningLevel] on [v].[WarningLevelId] = [v.WarningLevel].[Id]
where [v].[Id] = @__id_0
order by [v].[Id]
 
select
  [v.Details].[Id],
  [v.Details].[DealTime],
  [v.Details].[DealerCode],
  [v.Details].[DealerId],
  [v.Details].[DealerName],
  [v.Details].[FNum],
  [v.Details].[GNum],
  [v.Details].[HNum],
  [v.Details].[INum],
  [v.Details].[JNum],
  [v.Details].[KNum],
  [v.Details].[LNum],
  [v.Details].[MNum],
  [v.Details].[NNum],
  [v.Details].[RepairContractCode],
  [v.Details].[RepairContractId],
  [v.Details].[Source],
  [v.Details].[TotoalRepairDays],
  [v.Details].[VehicleWarrantyId],
  [v.Details].[Vin]
from [VehicleWarrantyRepairHistory] as [v.Details]
  inner join (
               select distinct [t].*
               from (
                      select top (1) [v0].[Id]
                      from [VehicleWarranty] as [v0]
                        left join [WarrantyWarningLevel] as [v.WarningLevel0]
                          on [v0].[WarningLevelId] = [v.WarningLevel0].[Id]
                      where [v0].[Id] = @__id_0
                      order by [v0].[Id]
                    ) as [t]
             ) as [t0] on [v.Details].[VehicleWarrantyId] = [t0].[Id]
order by [t0].[Id]

  这个在查询的过程中会分作几个SQL查询并且会将前面查询的结果作为后面查询的部分条件来进行了,待整个查询完毕后再在内存中将这些结果组合到一个query对象中。

  3 ThenInclude用法

  上面的介绍完了之后,你应该能够明白这个Include的具体含义和用法了,接着上面的例子,如果WarrantyWarningLevel里面还有通过外键Id去关联别的实体,这个时候ThenInclude就派上了用场了,理论上只要彼此之间建立了这种外键关系就可以一直ThenInClude下去,但是一般情况下不会用到这么复杂的情况,当然这里面每一个Include也都是作为一个单独的查询来进行的,这个也可以找具体的例子进行试验,这里也贴出一个具体的例子吧。

public async Task<GetRepairContractDetailForSettlementOutput> GetById(Guid id) {
            var repairContract = await _repairContractRepository.GetAll()
                .Include(d => d.RepairContractWorkItems)
                    .ThenInclude(w => w.Materials)
                .FirstOrDefaultAsync(r => r.Id == id);
 
            if (repairContract == null)
                throw new ValidationException(_localizer["当前维修合同不存在"]);
            var vehicleSold = _vehicleSoldRepository.Get(repairContract.VehicleId);
            var isTrafficSubsidy = _repairContractManager.IsTrafficSubsidy(repairContract.Id);
            var (nextMaintenanceMileage, nextMaintenanceTime) = _repairContractManager.GetNextMaintainInfo(repairContract, vehicleSold);
            var result = new GetRepairContractDetailForSettlementOutput() {
                Id = repairContract.Id,
                Code = repairContract.Code,
                CustomerName = repairContract.CustomerName,
                CellPhoneNumber = repairContract.CellPhoneNumber,
                Vin = repairContract.Vin,
                LicensePlate = repairContract.LicensePlate,
                NextMaintenanceTime = nextMaintenanceTime,
                NextMaintenanceMileage = nextMaintenanceMileage,
                LaborFee = repairContract.LaborFee,
                LaborFeeAfter = repairContract.LaborFeeAfter,
                MaterialFee = repairContract.MaterialFee,
                MaterialFeeAfter = repairContract.MaterialFeeAfter,
                OutFee = repairContract.OutFee,
                OtherFee = repairContract.OtherFee,
                TotalFeeAfter = repairContract.TotalFeeAfter,
                ShowIsTrafficSubsidy = isTrafficSubsidy,
                LastMaintenanceTime = vehicleSold.LastMaintenanceTime,
                LastMaintenanceMileage = vehicleSold.LastMaintenanceMileage,
                WorkItems = _mapper.Map<IList<GetRepairContractWorkItemForSettlementOutput>>(repairContract.RepairContractWorkItems)
            };
            return result;
        }

  最后还要介绍一种极特殊的情况,由于ThenInclude方法只能一层层向下进行,如果我想对同一个实体里面的两个关联实体做ThenInclude操作这个怎么处理,这里就直接给出代码吧。

/// <summary>
        ///维修合同完成的事件
        /// </summary>
        /// <param name="repairContractId"></param>
        public void Finished(Guid repairContractId) {
            var repairContract = _repairContractRepository.GetAll()
                .Include(c => c.RepairContractWorkItems).ThenInclude(wi => wi.Materials)
                .Include(c => c.RepairContractWorkItems).ThenInclude(wi => wi.Fault)
                .SingleOrDefault(c => c.Id == repairContractId);
            var repairContractAdjusts = _repairContractAdjustRepository.GetAll()
                .Include(a => a.WorkItems).ThenInclude(w => w.Materials)
                .Where(a => a.RepairContractId == repairContractId).ToList();
 
            var @event = new AddRepairContractEvent {
                Key = repairContract?.Code,
                RepairContract = repairContract,
                RepairContractAdjusts = repairContractAdjusts
            };
            _producer.Produce(@event);
        }

  这里需要Include同一个实体两次,然后分别调用ThenInclude方法,这个属于比较特殊的情况,在使用的时候需要注意。

  温馨提示:

  这里读者在看代码的时候可能不太理解类似这种 _repairContractRepository的具体由来,这里贴出一份完整的代码。

internal class AddRepairContractEventManager : DomainService, IAddRepairContractEventManager {
        private readonly KafkaProducer _producer;
        private readonly IRepository<RepairContract, Guid> _repairContractRepository;
        private readonly IRepository<RepairContractAdjust, Guid> _repairContractAdjustRepository;
 
        public AddRepairContractEventManager(KafkaProducer producer,
                                          IRepository<RepairContract, Guid> repairContractRepository,
                                          IRepository<RepairContractAdjust, Guid> repairContractAdjustRepository) {
            _producer = producer;
            _repairContractRepository = repairContractRepository;
            _repairContractAdjustRepository = repairContractAdjustRepository;
        }
 
        /// <summary>
        ///维修合同完成的事件
        /// </summary>
        /// <param name="repairContractId"></param>
        public void Finished(Guid repairContractId) {
            var repairContract = _repairContractRepository.GetAll()
                .Include(c => c.RepairContractWorkItems).ThenInclude(wi => wi.Materials)
                .Include(c => c.RepairContractWorkItems).ThenInclude(wi => wi.Fault)
                .SingleOrDefault(c => c.Id == repairContractId);
            var repairContractAdjusts = _repairContractAdjustRepository.GetAll()
                .Include(a => a.WorkItems).ThenInclude(w => w.Materials)
                .Where(a => a.RepairContractId == repairContractId).ToList();
 
            var @event = new AddRepairContractEvent {
                Key = repairContract?.Code,
                RepairContract = repairContract,
                RepairContractAdjusts = repairContractAdjusts
            };
            _producer.Produce(@event);
        }
    }

  4 IncludeFilter用法

  在有些场景中我们可能需要带出清单的时候并且过滤清单,这个功能算是对Include方法的一个提升,可以将两个操作合并到一起来进行,这个在使用的时候需要注意 这个并不是Asp.Net Core自带的功能,这个需要通过引入包 Z.EntityFramework.Plus.EFCore.dll的包来实现的,如果你们的系统中使用的是ABP作为项目主框架,那么你只需要引用 Abp.EntityFrameworkCore.EFPlus这个包就可以了,因为这个包中就包含和Z.EntityFramework相关的子包,这个在使用的时候需要注意。

  下面我们来看一看我们的代码中是怎么使用的。

private (Company company, IEnumerable<PartSaleOrderType> partSaleOrderTypes) GetCompanyDetailForFactory(Guid id) {
            var currentPartSaleOrderTypes = GetCurrentPartSaleOrderTypes();
            var currentPartSaleOrderTypeIds = currentPartSaleOrderTypes.Select(t => t.Id);
            var company = _companyRepository.GetAll()
                .IncludeFilter(c => c.CustomerPartInformations.Where(i => i.BranchId == SdtSession.TenantId.GetValueOrDefault()))
                .IncludeFilter(c => c.CustomerOrderWarehouses.Where(w => currentPartSaleOrderTypeIds.Contains(w.PartSaleOrderTypeId)))
                .IncludeFilter(c => c.CustomerMarkupRates.Where(w => currentPartSaleOrderTypeIds.Contains(w.PartSaleOrderTypeId)))
                .IncludeFilter(c => c.OrderShippingSequences.Where(w => currentPartSaleOrderTypeIds.Contains(w.PartSaleOrderTypeId)))
                .IncludeFilter(c => c.OrderingCalendars.Where(w => currentPartSaleOrderTypeIds.Contains(w.PartSaleOrderTypeId)))
                .FirstOrDefault(d => d.Id == id && d.Status == BaseDataStatus.生效);
            if (company == null) {
                throw new EntityNotFoundException(SharedLocalizer["未能找到对应的企业"]);
            }
            return (company, currentPartSaleOrderTypes);
        }

  这个提供了一种新的清单过滤方式,不仅提高了效率而且使代码更加优化简练。

  5 特殊情况

  这里还介绍一种不通过Include方法来获取清单中的方式,就像下面这种写法,Company对象和OrderingCalendars之间建立了一对多的导航属性,我们在使用 company.OrderingCalendars之前先将内部的清查查询出来(不需要定义变量接收,使用_即可),这样也是能通过导航属性自动映射到company的OrderingCalendars中去的,这个在使用的时候需要特别注意,查询的时候要提前ToList,将数据查询到内存里面

_ = _orderingCalendarRepository.GetAll().Where(t => t.OrderingCompanyId == company.Id).ToList();           
           var tempCalendars = company.OrderingCalendars.OrderBy(d => d.PartSaleOrderType.Level).Select(d => new {
               d.PartSaleOrderType.BrandId,
               d.PartSaleOrderType.BrandCode,
               d.PartSaleOrderType.BrandName,
               d.PartSaleOrderTypeId,
               d.PartSaleOrderTypeCode,
               d.PartSaleOrderTypeName,
               d.Year,
               d.Month,
               d.Day
           });

以上就是详解EFCore中的导航属性的详细内容,更多关于EFCore 导航属性的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解.Net Core 权限验证与授权(AuthorizeFilter、ActionFilterAttribute)

    在.Net Core 中使用AuthorizeFilter或者ActionFilterAttribute来实现登录权限验证和授权 一.AuthorizeFilter 新建授权类AllowAnonymous继承AuthorizeFilter,IAllowAnonymousFilter public class AllowAnonymous : AuthorizeFilter, IAllowAnonymousFilter { } 新建拦截类继承AuthorizeFilter public class

  • 如何在Asp.Net Core中集成Refit

    在很多时候我们在不同的服务之间需要通过HttpClient进行及时通讯,在我们的代码中我们会创建自己的HttpClient对象然后去跨领域额进行数据的交互,但是往往由于一个项目有多个人开发所以在开发中没有人经常会因为不同的业务请求去写不同的代码,然后就会造成各种风格的HttpClient的跨域请求,最重要的是由于每个人对HttpClient的理解程度不同所以写出来的代码可能质量上会有参差不齐,即使代码能够达到要求往往也显得非常臃肿,重复高我们在正式介绍Refit这个项目之前,我们来看看我们在项目

  • 从EFCore上下文的使用到深入剖析DI的生命周期最后实现自动属性注入

    故事背景 最近在把自己的一个老项目从Framework迁移到.Net Core 3.0,数据访问这块选择的是EFCore+Mysql.使用EF的话不可避免要和DbContext打交道,在Core中的常规用法一般是:创建一个XXXContext类继承自DbContext,实现一个拥有DbContextOptions参数的构造器,在启动类StartUp中的ConfigureServices方法里调用IServiceCollection的扩展方法AddDbContext,把上下文注入到DI容器中,然后

  • 在.NET Core类库中使用EF Core迁移数据库到SQL Server的方法

    前言 如果大家刚使用EntityFramework Core作为ORM框架的话,想必都会遇到数据库迁移的一些问题. 起初我是在ASP.NET Core的Web项目中进行的,但后来发现放在此处并不是很合理,一些关于数据库的迁移,比如新增表,字段,修改字段类型等等,不应该和最上层的Web项目所关联,数据的迁移文件放到这里也感觉有点多余,有点乱乱的感觉,所以才想着单独出来由专门的项目进行管理会比较好,也比较清晰! 注意目标框架选择的是.NET Core 2.0而不是.NET Standard 2.0.

  • CodeFirst从零开始搭建Asp.Net Core2.0网站

    一步步教大家如何搭建Asp.Net Core2.0网站,以下所有都是建立在.NETCore2.0环境已经搭建好 右键解决方案>新建项目> 选择Web>ASP.NETCoreWeb应用程序(.NET Core) 选择Web应用程序,暂时不选择启用Docker,身份验证选择个人用户账户(会自动生成一系列和用户认证的代码) 随后生代码层次目录如下: 其中会包含身份信息的相关实现,比如相关实体信息(user)之类的,如果想对扩展微软自动的生成的用户实体类,可在Models中的Applicatio

  • .net core实用技巧——将EF Core生成的SQL语句显示在控制台中

    前言 笔者最近在开发和维护一个.NET Core项目,其中使用几个非常有意思的.NET Core相关的扩展,在此总结整理一下. EF Core性能调优 如果你的项目中使用了EF Core, 且正在处于性能调优阶段,那么了解EF Core生成的SQL语句是非常关键的.那么除了使用第三方工具,如何查看EF Core生成的SQL语句呢?这里笔者将给出一个基于.NET Core内置日志组件的实现方式. 创建一个实例项目 我们首先建一个控制台程序,在主程序中我们编写了一个最简单的EF查询. class P

  • .net EF Core专题:EF Core 读取数据时发生了什么?

    原文:https://bit.ly/2UMiDLb 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能会去除一些本人实在不知道如何组织但又不影响理解的句子. 本文将为你详细描绘 EF Core 从数据库中读取数据的"幕后"视图.我将揭开两种数据库读取方式的面纱:一个是普通的查询,另一个是使用 AsNoTracking 方法的非跟踪查询.我还将通过一个实验来演示我是如何解决我的一个客户遇到的性能问题. 我假设你对 EF C

  • .net core EF Core调用存储过程的方式

    前言 在这里,我们将尝试去学习一下 .net core EF Core 中调用存储过程. 我们知道,EF Core 是不支持直接调用存储过程的,那它又提供了什么样的方式去执行存储过程呢?有如下方法: 1.FromSql,官方文档 DbSet<TEntity>.FromSql() 2.执行SQl命令 DbContext.Database.ExecuteSqlCommand() 但是,这两种方式都有局限性: 1.FromSql方式的结果一定要是实体类型,就是数据库表映射的模型.这意味着,执行存储过

  • EFCore 通过实体Model生成创建SQL Server数据库表脚本

    在我们的项目中经常采用Model First这种方式先来设计数据库Model,然后通过Migration来生成数据库表结构,有些时候我们需要动态通过实体Model来创建数据库的表结构,特别是在创建像临时表这一类型的时候,我们直接通过代码来进行创建就可以了不用通过创建实体然后迁移这种方式来进行,其实原理也很简单就是通过遍历当前Model然后获取每一个属性并以此来生成部分创建脚本,然后将这些创建的脚本拼接成一个完整的脚本到数据库中去执行就可以了,只不过这里有一些需要注意的地方,下面我们来通过代码来一

  • 记一次EFCore类型转换错误及解决方案

    一  背景 今天在使用EntityFrameworkCore 查询的时候在调试的时候总是提示如下错误:Unable to cast object of type 'System.Data.SqlTypes.SqlString' to type 'System.Data.SqlTypes.SqlGuid' 第一次看这个报错肯定是数据库实体和EFCore中定义的某种类型不匹配从而导致类型转换错误,但是业务涉及到这么多的实体Entity,那么到底是哪里类型无法匹配呢?所以第一步肯定是调试代码,然后看报

  • .Net Core中使用ref和Span<T>提高程序性能的实现代码

    一.前言 其实说到ref,很多同学对它已经有所了解,ref是C# 7.0的一个语言特性,它为开发人员提供了返回本地变量引用和值引用的机制. Span也是建立在ref语法基础上的一个复杂的数据类型,在文章的后半部分,我会有一个例子说明如何使用它. 二.ref关键字 不论是ref还是out关键,都是一种比较难以理解和操作的语言特性,如C语言中操作指针一样,这样的高级语法总是什么带来一些副作用,但是我不认为这有什么,而且不是每一个C#开发者都要对这些内部运行的机制有着深刻的理解,我觉得不论什么复杂的东

  • .NET Core类库System.Reflection.DispatchProxy实现简易Aop的方法

    前言 aop即是面向切面编程,众多Aop框架里Castle是最为人所知的,另外还有死去的Spring.NET,当然,.NET Core社区新秀AspectCore在性能与功能上都非常优秀,已经逐渐被社区推崇和有越来越多的人使用.感谢柠檬同学的礼物! 如果大家出于自身需求或者学习,想实现一个Aop,是不是觉得一来就要使用Emit去做?最近我了解到了System.Reflection.DispatchProxy这个corefx类库,已经实现了动态代理功能. 1|1System.Reflection.

随机推荐