我最近发现,并不是每个与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标志实现对实体的软删除。您需要在视图中对此进行支持。如果您忘记了,则何时会收到错误报告尚不清楚。
顺便说一句,如何在不重写整个代码的情况下实现软删除?我下次再说。也许别的东西。
大家再见