使用EntityFramework Core的提示

你好。



我最近发现,并不是每个与EF合作的人都知道如何烹饪。而且,他们并不渴望了解。在早期阶段就面临问题-建立。

即使成功配置后,数据请求也存在问题。并不是因为人们不了解LINQ,而是因为并非所有事物都可以从对象映射到关系模型。因为当使用链接时,人们会在表中思考。绘制SQL查询并尝试将其转换为LINQ。



这以及我想在文章中讨论的其他内容。



配置



一个人来到了这个项目,第一次看到EF,惊叹于这样的奇迹,学会了使用它,决定将其用于个人目的。创建了一个新项目,...然后该怎么办?



您开始谷歌搜索,试用,错误。为了简单地将EF连接到项目,开发人员面临着各种无法理解的问题。



1.以及如何配置它以使其与所需的DBMS一起使用?

2.以及如何配置迁移工作?



我敢肯定还有更多问题,但这可能是最常见的问题。让我们依次讨论所有内容。



1.好吧,这是给谷歌的。 :)您的任务是为您的DBMS查找EntityFramework Core提供程序。



同样在提供者的描述中,您会找到设置说明。



例如,对于PostgreSQL,您需要安装Nuget包Npgsql.EntityFrameworkCore.PostgreSQL

创建接受DbContextOptions的自己的上下文,并像这样创建整个内容



var opts = new DbContextOptionsBuilder<MyDbContext>()
        .UseNpgsql(constring);

var ctx = new MyDbContext(opts.Options);




如果您有ASP .NET Core应用程序,则上下文在容器中的注册方式有所不同。但是您已经可以在工作项目中看到这一点。或谷歌。



2.如果您的公司没有保姆,那么您很可能会知道如何安装必要的工具来进行迁移。是用于程序包管理器还是NET Core CLI。



但是,您可能不知道的是,在进行迁移时,所选的启动项目(--startup-project)已启动。这意味着,如果迁移的起始项目与启动应用程序的项目相同,并且由于某种原因而启动时,则通过ctx.Database.Migrate()进行迁移,那么当您尝试创建删除先前创建的迁移或创建另一个迁移时,则最后创建的迁移将移至基础。惊喜!



但是,也许在尝试创建第一个迁移时,您会遇到类似这样的情况



没有为此DbContext配置数据库提供程序。可以通过重写DbContext.OnConfiguring方法或在应用程序服务提供程序上使用AddDbContext来配置提供程序。如果使用AddDbContext,则还要确保您的DbContext类型在其构造函数中接受DbContextOptions <TContext>对象,并将其传递给DbContext的基本构造函数。


这是因为用于迁移的工具会根据您的上下文创建迁移,但是为此需要一个实例。要提供它,您需要在启动程序项目中实现IDesignTimeDbContextFactory接口,这并不困难,只有一种方法可以返回上下文的实例。



设置模型



现在您已经创建了第一个迁移,但是由于某种原因它是空的。虽然您有模型。



关键是您还没有教过上下文将模型转换为数据库。



为了使EF创建迁移以在数据库中为模型创建表,您必须至少在上下文中创建DbSet <MyEntity>类型的属性。



例如,通过此属性

public DbSet <MyEntity> MyEntities {get;组; }



MyEntities表将使用与MyEntity实体的属性相对应的字段创建。



如果您不想拥有DbSet或不想影响实体的默认表创建规则,则需要重写

protected override void OnModelCreating(ModelBuilder modelBuilder)

上下文的method



您最可能知道该怎么做。此外,您可能熟悉允许您控制映射规则的属性。但是,这就是事情。属性不能完全控制映射,这意味着您将在OnModelCreating中进行完善。也就是说,您将具有以注释形式和流利api形式映射实体的规则。然后去查找为什么该字段具有错误的名称,您期望的名称或错误的限制。



-那么,一切都很简单,-您说-我将通过OnModelCreating配置所有内容



,然后在10个字段中设置了第20个实体后,您的眼睛开始起伏。您正在尝试查找某个实体的字段设置,但是所有东西都从一个200-500行长的统一块中浮出水面。



是的,您可以将此块划分为20种方法,它将变得更加容易。但是知道存在一个IEntityTypeConfiguration <TEntity>接口是很有用的,通过为特定实体实现该接口,您可以在此实现中描述特定实体的映射规则。好吧,为了使上下文能够被使用,在OnModelCreating中,您需要编写

modelBuilder.ApplyConfigurationsFromAssembly(assemblyWithConfigurations);



当然,也可以将一般行为转移到相同的OnModelCreating。例如,如果您对所有或许多实体的标识符都有通用规则,则可以像这样配置它



foreach (var idEntity in modelBuilder.Model.GetEntityTypes()
    .Where(x => typeof(BaseIdEntity).IsAssignableFrom(x.ClrType))
    .Select(x => modelBuilder.Entity(x.ClrType)))
{
    idEntity.HasKey(nameof(BaseIdEntity.Id));
}


建立查询



好吧,我们把事情整理了一下,变得更加愉快了。



现在仍然需要重做我们的家庭项目的查询,从SQL到Linq。我已经在工作中编写了请求,就像给

梨ctx.MyEntities.Where(条件).Select(地图).GroupBy(表达式).OrderBy(表达式)…轻巧一样。



那么,我们的要求是什么?



SELECT bla bla bla FROM table
RIGHT JOIN....... 


是的



ctx.LeftEntities.RightJoin(.... f@#$


在了解EF之前,我一直在使用Linq及其扩展方法。我从来没有问过一个问题,但是RIGHT JOIN,LEFT JOIN在哪里……



从对象的角度来说,LEFT JOIN是什么?








class LeftEntity 
{
    public List<RightEntity> RightEntities { get; set; }
}


这甚至都不是魔术。我们只有一个实体,它引用其他实体的列表,但可能没有任何实体。



就是这个



ctx.LeftEntities.Include(x => x.RightEntities)


什么是正确的加入?这是一个反向的LEFT JOIN,这意味着我们只是从另一个实体开始。



但是,任何事情都可能发生,有时您需要将每个捆绑软件作为一个单独的实体进行控制,即使左实体没有与任何右实体关联(右NULL)。因此,可以像这样显式地进行LEFT JOIN



ctx.LeftEntities.SelectMany(x => x.RightEntities.DefaultIfEmpty(), (l, r) => new { Left = l, Right = r })


就像在关系模型中一样,这使我们能够控制绑定。您为什么需要它?假设客户需要以表格形式显示数据,并且需要排序和/或分页。



我也不喜欢这个主意,但是每个人在Excel中都有自己的怪癖和无尽的爱。因此,我们将用咳嗽盖住垫子,诅咒成拳头,然后继续工作。我们接下来要做什么?



FULL JOIN


所以,嗯,我已经知道,我们不考虑使用SQL,我们不考虑对象,像这样,现在却像这样……该死。如何描绘呢?



没有元组,无处。



总的来说,当我们必须使用元组时,我们对连接的类型(1-1、1-n,nn)失去了了解,并且一般而言,从理论上讲,我们可以越过苍蝇和大象。这是黑魔法。

我们开始做吧!



让我们以多对多为基础。



因此,我们有3种类型的实体



LeftEntity

RightEntity

LeftRight(用于组织连接)



LeftRight我将使用复合键创建。我不需要直接访问此实体,不需要外部引用,因此不会产生不必要的字段和索引。



作为查询的结果,我想获得一组元组,其中将包含左实体和右实体。此外,如果原始实体与正确的实体无关,那么正确的对象将为null。右侧实体也是如此。



事实证明,LeftRight不适合我们作为输出,它的局限性不允许我们在没有实体之一的情况下创建捆绑商品。如果您练习DDD,则会发现不一致之处。

即使您不练习,也不应练习。让我们为

LeftRightFull输出创建一个新类型,该类型仍然包含对左和右实体的引用。



因此,我们有





L1

L2





R1

R2



左右改变

L2 R2



我们要输出

L1ñ

L2 R2

R1



让我们从左边的连接开始



var query = ctx.LeftEntities
    .SelectMany(x => x.RightLinks.DefaultIfEmpty(), (l, lr) => new LeftRightFull
    {
        LeftEntity = l,
        RightEntity = lr.RightEntity
    })




现在,我们已经有了

L1 n

L2 R2



接下来是什么?通过SelectMany坚持RightEntity吗?好了,这样我们就得到

L1ñR1ñ

L1ňR2 L2

L2 R2 R1ň

L2 R2 R2 L2



我们可以通过条件过滤掉不必要的(L2 R2 R1 n和L1ñR2 L2),但是,如何将L1ñR1ñ仍不清楚在

L1 n

n R1中



也许发掘使我们进入了错误的草原。让我们通过Union将这些相似的查询与左和右实体的左联接连接起来。

L1 n

L2 R2

UNION

L2 R2

n R1



在这里您可能对性能有疑问。我不会回答它们,因为一切都将取决于特定的DBMS及其优化器的细致性。



或者,您可以使用所需查询创建视图。Union在EF Core 2上不起作用,因此我必须创建一个View。但是,我建议不要忘记它。突然,您决定通过IsDeleted标志实现对实体的软删除。您需要在视图中对此进行支持。如果您忘记了,则何时会收到错误报告尚不清楚。



顺便说一句,如何在不重写整个代码的情况下实现软删除?我下次再说。也许别的东西。



大家再见



All Articles