前言

EF Core 8.0 推出了 Complex Types,这篇要来介绍一下。

由于它和 Owned Entity Types 傻傻分不清楚,加上我之前也没有写过 Owned Entity Types 的文章,所以这篇就一起介绍呗。

Owned Entity Types

Owned Entity Types 本质上任然属于一种 Entity Types,只是它有一些潜规则,所以变得和普通 Entity Type 有所区别。

Owned Entity Types 在 Domain-driven design (领域驱动设计) 里被视作为 Aggregate 的实现。很遗憾,我对 DDD 一窍不通,无法用 DDD 视角去解释它。

Compare with one-to-one relationship

我们拿 one-to-one relationship 来做对比,这样就可以看出 Owned Entity Types 的特色了。

首先,做两个 Entity -- Order 和 OrderCustomerInfo

public class Order
{
public int Id { get; set; }
public OrderCustomerInfo CustomerInfo { get; set; } = null!;
public decimal Amount { get; set; }
} public class OrderCustomerInfo
{
public int Id { get; set; }
public Order Order { get; set; } = null!;
public string Name { get; set; } = "";
public string Phone { get; set; } = "";
}

它们是一对一关系

public class ApplicationDbContext() : DbContext()
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<OrderCustomerInfo> OrderCustomerInfos => Set<OrderCustomerInfo>(); protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(
builder =>
{
builder.ToTable("Order");
builder.Property(e => e.Amount).HasPrecision(19, 2);
builder.HasOne(e => e.CustomerInfo).WithOne(e => e.Order).HasForeignKey<OrderCustomerInfo>(e => e.Id);
}); modelBuilder.Entity<OrderCustomerInfo>(
builder =>
{
builder.ToTable("OrderCustomerInfo");
builder.Property(e => e.Name).HasMaxLength(256);
builder.Property(e => e.Phone).HasMaxLength(256);
});
}
}

接着 insert 和 query

    using var db = new ApplicationDbContext();

// insert order with customer info
db.Orders.Add(new()
{
Amount = 100,
CustomerInfo = new()
{
Name = "Derrick",
Phone = "+60 16-773 7062",
},
});
db.SaveChanges(); // query
var order = db.Orders
.Include(e => e.CustomerInfo)
.First(); Console.WriteLine(order.CustomerInfo.Name); // "Derrick"

Owned Entity Types 的特性

对比 one-to-one,Owned Entity Types 有几个特性:

  1. Owned Entity Types 没有 DbSet

    OrderCustomerInfo 不是独立的 Entity,它属于 part of the Order Entity,所以它没有 DbSet。

    这也导致了它不能直接被创建,下面这句是不成立的

    db.OrderCustomerInfos.Add() // 'ApplicationDbContext' does not contain a definition for 'OrderCustomerInfos'

    OrderCustomerInfo 需要依附在 Order 上才能一起被创建,像这样

    db.Orders.Add(new()
    {
    Amount = 100,
    CustomerInfo = new()
    {
    Name = "Derrick",
    Phone = "+60 16-773 7062",
    },
    });
  2. 自动 Include

    没有 DbSet 自然也无法直接 query

    db.OrderCustomerInfos.ToList() // 'ApplicationDbContext' does not contain a definition for 'OrderCustomerInfos'

    要获取 OrderCustomerInfo 只能透过 Order。另外 Owned Entity Types 有一个特色 -- 它会自动被 Include 出来。

    即便我们没有写 Include,Owned Entity Types 依然会被 eager loading 出来。

  3. same Table

    默认情况下,Owned Entity Types (OrderCustomerInfo) 会和它依附的 Entity Types (Order) 存放在同一个数据库 Table,还有 column name 会加上 prefix,这就像使用了 Table Splitting 的那样。

  4. Id become shadow property

    Owned Entity Types 任然是 Entity Types,它依然有 primary key 的概念,只是它改成了 Shadow Property,在 class 会看不见 Id。

Config Owned Entity Types

替换成这样

modelBuilder.Entity<Order>(
builder =>
{
builder.ToTable("Order");
builder.Property(e => e.Amount).HasPrecision(19, 2);
builder.OwnsOne(e => e.CustomerInfo, builder =>
{
builder.Property(e => e.Name).HasMaxLength(256);
builder.Property(e => e.Phone).HasMaxLength(256);
});
});

效果

// query
var order = db.Orders.First(); Console.WriteLine(order.CustomerInfo.Name); // "Derrick"

不需要 Include,它会自动 Include。

数据库 Order Table

OrderCustomerInfo 的属性被映射到 Order Table,而且 column name 加了 prefix "CustomerInfo" 这名字来自 Order.CustomerInfo 属性。

我们调 Entity Model 出来证实一下,Owned Entity Types 也是一种 Entity Types 而且它其实是有 Key 的。

using var db = new ApplicationDbContext();
var customerEntityType = db.Model.FindEntityType(typeof(OrderCustomerInfo))!; // Owned Entity Types 也是一种 Entity Types
var isOwned = customerEntityType.IsOwned(); // true,OrderCustomerInfo 是 Owned Entity Types
var property = customerEntityType.GetProperty("OrderId")!;
Console.WriteLine(property.IsShadowProperty()); // true,"OrderId" 是 Shadow Property
Console.WriteLine(property.IsKey()); // true,"OrderId" 是 Primary Key
Console.WriteLine(property.IsForeignKey()); // true,"OrderId" 是 Foreign Key var orderEntityType = db.Model.FindEntityType(typeof(Order))!;
Console.WriteLine(orderEntityType.FindNavigation("CustomerInfo")!.ForeignKey); // "OrderId", CustomerInfo 属性不是普通的 Property 而是 Navigation

Two Table

默认情况下 Owned Entity Types (OrderCustomerInfo) 会和它的 Owner (Order) 共用一个 Table。

如果我们不希望这样,则可以使用 Entity Splitting 将它们分开成两个 Table。

column name prefix 会自动被拿掉。

效果

Sharing Owned Entity Types

Owned Entity Types 最好不要共用,不顺风水。

public class Address
{
public string Street1 { get; set; } = "";
public string Street2 { get; set; } = "";
public string PostalCode { get; set; } = "";
public string Country { get; set; } = "";
} public class Order
{
public int Id { get; set; }
public Address BillingAddress { get; set; } = null!;
public Address ShippingAddress { get; set; } = null!;
public decimal Amount { get; set; }
} public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public Address Address { get; set; } = null!;
}

Order 和 Customer 都使用了 Address。

试想,如果是一对一关系,上面这样做成立吗?

答案是不成立,因为一对一关系是靠 id 作为 foreign key 维持关系的,上面这样就乱了套了。

我们测试看强制设置 Owned Entity Types 出来的效果是怎样的。

public class ApplicationDbContext() : DbContext()
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<Customer> Customers => Set<Customer>(); protected override void OnModelCreating(ModelBuilder modelBuilder)
{
static void BuildAction<TEntity>(OwnedNavigationBuilder<TEntity, Address> builder) where TEntity : class
{
builder.Property(e => e.Street1).HasMaxLength(256);
builder.Property(e => e.Street2).HasMaxLength(256);
builder.Property(e => e.PostalCode).HasMaxLength(256);
builder.Property(e => e.Country).HasMaxLength(256);
} modelBuilder.Entity<Order>(
builder =>
{
builder.ToTable("Order");
builder.Property(e => e.Amount).HasPrecision(19, 2);
builder.OwnsOne(e => e.BillingAddress, BuildAction);
builder.OwnsOne(e => e.ShippingAddress, BuildAction);
}); modelBuilder.Entity<Customer>(
builder =>
{
builder.ToTable("Customer");
builder.Property(e => e.Name).HasMaxLength(256);
builder.OwnsOne(e => e.Address, BuildAction);
});
}
}

BuildAction 是共用的。

效果

如果做 Entity Splitting 的话,就会多出 3 个 Table -- CustomerAddress,OrderBillingAddress,OrderShippingAddress。

从这个结构可以看出,它底层依然是一对一的概念,只是在上层搞了许多映射。

我们进资料看看

using var db = new ApplicationDbContext();
var address = new Address
{
Street1 = "test",
Street2 = "test",
PostalCode = "81300",
Country = "Malaysia"
};
db.Orders.Add(new()
{
Amount = 100,
ShippingAddress = address,
BillingAddress = address
});
db.SaveChanges();

注意,shippingAddress 和 billingAddress 使用了同一个 address 对象。

运行结果是报错

因为官网声明了,Owned Entity Types 实例是不可以共享的,必须一个对一个。

我觉得 EF Core 如果硬硬要做映射是可以做到的,只是他们认为这种使用方式已经脱离了 Owned Entity Types 的本意,所以才不支持它。

但是,EF Core 8.0 推出的 Complex Types 支持这种使用方式,Complex Types 和 Owned Entity Types 有几分相似下一 part 会详细讲。

Collections of owned types

Owned Entity Types 不仅仅可以映射一对一关系,一对多关系也可以映射。

但是一对多就不可能共用同一个数据库 Table 了,一定是 2 个 Table。

其它特性,比如没有 DbSet,自动 Include,不能 share instance 这些则都一样。

来个简单的示范

Entity

public class Address
{
public string Street1 { get; set; } = "";
public string Street2 { get; set; } = "";
public string PostalCode { get; set; } = "";
public string Country { get; set; } = "";
} public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public List<Address> Addresses { get; set; } = [];
}

ModelBuilder

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(
builder =>
{
builder.ToTable("Customer");
builder.Property(e => e.Name).HasMaxLength(256);
builder.OwnsMany(e => e.Addresses, builder =>
{
builder.Property(e => e.Street1).HasMaxLength(256);
builder.Property(e => e.Street2).HasMaxLength(256);
builder.Property(e => e.PostalCode).HasMaxLength(256);
builder.Property(e => e.Country).HasMaxLength(256);
});
});
}

数据库

和普通一对多的表结构是一样的。

using var db = new ApplicationDbContext();
var addressEntityType = db.Model.FindEntityType(typeof(Address))!;
addressEntityType.GetProperty("Id").IsKey(); // true
addressEntityType.GetProperty("Id").IsShadowProperty(); // true
addressEntityType.GetProperty("CustomerId").IsForeignKey(); // true
addressEntityType.GetProperty("CustomerId").IsShadowProperty(); // true

CustomerId 是 Foreign Key,Id 是 Primary Key,它们都是 Shadow Property。

如果想修改 Primary Key 和 Foreign Key 可以这样配置

builder.OwnsMany(e => e.Addresses, builder =>
{
builder.WithOwner().HasForeignKey("CustomerId2"); // rename foreign key CustomerId to CustomerId2
builder.Property<int>("Id").HasColumnName("AddressId"); // rename primary key Id to AddressId
});

How to detech Owned Entity Types modified?

相关 Issue:

Detecting if an owned entity was changed

Return true for IsModified on navigations pointing to Added or Deleted entities

How can I get a deleted owned entity from the parent entity?

我们来看一个简单的例子

public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public Address Address { get; set; } = null!;
} public class Address
{
public string Line1 { get; set; } = null!;
}

Address 是 Owned Entity Type。

var product = db.Products.AsTracking().Single();
product.Address.Line1 = "new line";
var isProductChanged = db.Entry(product).State == EntityState.Modified; // false
var isAddressPropertyModified = db.Entry(product).Reference(e => e.Address).IsModified; // false
var isAddressChanged = db.Entry(product.Address).State == EntityState.Modified; // true
var isLine1PropertyModified = db.Entry(product.Address).Property(e => e.Line1).IsModified; // true

我们从 product 对象进入到 address 对象里,然后修改 line1 的值。

可以看到,从 product entry 是完全感知不到任何 modified,只有 product.address entry 才能感知到 modified。

我们换一个修改方式

await using var db = new ApplicationDbContext();

var product = db.Products.AsTracking().Single();
var oldAddress = product.Address;
var newAddress = new Address { Line1 = "new line" };
product.Address = newAddress; var isProductChanged = db.Entry(product).State == EntityState.Modified; // false
var isAddressPropertyModified = db.Entry(product).Reference(e => e.Address).IsModified; // true
var isAddressChanged = db.Entry(product.Address).State == EntityState.Modified; // false
var isLine1PropertyModified = db.Entry(product.Address).Property(e => e.Line1).IsModified; // false
var isOldAddressDeleted = db.Entry(oldAddress).State == EntityState.Deleted; // true
var isNewAddressAdded = db.Entry(oldAddress).State == EntityState.Added; // true

新旧 address 对象替换,这样操作的话属于 add and delete,同时 product entry 的 address reference 会感知到 modified (注:但是 product entry 依旧是 unchanged 哦)。

To JSON

Owned Entity Types 还有一个强项,它可以以 JSON 格式保存到数据库里,不管是 OwnsOne 还是 OwnsMany 都行。

builder.OwnsMany(e => e.Addresses, builder =>
{
builder.ToJson();
builder.Property(e => e.Street1).HasMaxLength(256);
builder.Property(e => e.Street2).HasMaxLength(256);
builder.Property(e => e.PostalCode).HasMaxLength(256);
builder.Property(e => e.Country).HasMaxLength(256);
});

加一句 ToJson() 就可以了。

测试

using var db = new ApplicationDbContext();
db.Customers.Add(new()
{
Name = "Derrick",
Addresses = [
new() { Street1 = "test1", Street2 = "test1", PostalCode = "81300", Country = "Malaysia" },
new() { Street1 = "test2", Street2 = "test2", PostalCode = "123456", Country = "Singapore" }
]
});
db.SaveChanges();

效果

厉害吧

Nested Owned Entity Types

Owned Entity Types 支持嵌套,配置的方式和上面一样,在 OwnsOne / Many 里面继续调用 OwnsOne / Many 就可以了。

这里我就不演示了。

另外,ToJson 的话,一定要在最上层调用。

目前不支持一半一半的状况,要 JSON 就得从上层到下层通通 JSON。

Limitation

目前不支持 inheritance hierarchies 继承结构,TPH、TPT、TPC 通通不支持,希望未来会支持。

Complex Types

参考:Docs – Value objects using Complex Types

EF Core 8.0 推出了 Complex Types。Complex Types 和 Owned Entity Types 外观有点像,但内在思想是不同的。

Complex Types 在 Domain-driven design (领域驱动设计) 里被视作为 Value Object 的实现。很遗憾,我对 DDD 一窍不通,无法用 DDD 视角去解释它。

Current limitations

目前 Complex Types 还不算完整,它有很多该有得功能都还没有实现,要使用它要小心哦。

  1. 不支持一对多关系。

  2. 不支持 nullable

    public OrderCustomerInfo? CustomerInfo { get; set; }

    像上面这样 OrderCustomerInfo 将无法设置成 Complex Types,因为它是 nullable。

  3. 不支持 ToJson。

这几个都挺需要的,既然都没有

和 Owned Entity Types 的共同点和区别

共同点:

  1. 没有 DbSet,不可以直接 Add 和 Query

  2. 自动 Include

区别:

  1. Complex Types 不支持一对一映射到 two table。

  2. Complex Types 不继承 Entity Types,因为它完全没有 Primary Key 概念。

  3. Complex Types 的实例是可以共用的。

  4. Complex Types 更倾向使用 record 而非 class。

Config Complex Types

和 config Owned Entity Types 大同小异。

Entity

public class Order
{
public int Id { get; set; }
public OrderCustomerInfo CustomerInfo { get; set; } = null!;
public decimal Amount { get; set; }
} public class OrderCustomerInfo
{
public string Name { get; set; } = "";
public string Phone { get; set; } = "";
}

ModelBuilder

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(
builder =>
{
builder.ToTable("Order");
builder.Property(e => e.Amount).HasPrecision(19, 2);
builder.ComplexProperty(e => e.CustomerInfo, builder =>
{
builder.Property(e => e.Name).HasMaxLength(256);
builder.Property(e => e.Phone).HasMaxLength(256);
});
});
}

效果

Entity Model

using var db = new ApplicationDbContext();
var customerInfoEntityType = db.Model.FindEntityType(typeof(OrderCustomerInfo)); // null
var orderEntityType = db.Model.FindEntityType(typeof(Order))!;
var customerInfoProperty = orderEntityType.FindProperty("CustomerInfo"); // null
var customerInfoComplexProperty = orderEntityType.FindComplexProperty("CustomerInfo");

OrderCustomerInfo 不是 Entity Types,所以 FindEntityType 是找不到的,这点和 Owned Entity Types 不同。

另外,CustomerInfo 也不是普通的 Property 也不是 Navigation,而是 ComplexProperty,要使用 FindComplexProperty 才能获取到,这点也和 Owned Entity Types 不同。

DbContext.Entry

Entity Model 不一样,那访问 Entry CurrentValue,IsModified 这些自然也不一样了。

Entity

public class Order
{
public int Id { get; set; }
public OrderCustomerInfo CustomerInfo { get; set; } = null!;
public Address ShippingAddress { get; set; } = null!;
public decimal Amount { get; set; }
}

假设 OrderCustomerInfo 是 Complex Types,ShippingAddress 是 Owned Entity Types

想访问 ShippingAddress  的 CurrentValue 通过 DbContext.Entry 就可以了。

var order = db.Orders.First();
var shippingAddressEntry = db.Entry(order.ShippingAddress);
var countryValue = shippingAddressEntry.Property(e => e.Country).CurrentValue;

想访问 CustomerInfo 的 CurrentValue 通过 DbContext.Entry 会报错。

var customerInfoEntry = db.Entry(order.CustomerInfo); // error

因为 Complex Types 不是 Entity Types,它没有 Key 的概念。

正确的方式是先进入 OrderEntry 然后使用 ComplexProperty 找到 CustomerInfo 在进入它的 Property 获取 CurrentValue

var orderEntry = db.Entry(order);
var nameValue = orderEntry.ComplexProperty(e => e.CustomerInfo).Property(e => e.Name).CurrentValue

Immutable

Value Object 虽然是对象,但它通常是值类型 (Immutable)。

所以一般都是使用 struct / record 而不是 class。(虽然 EF Core 支持使用 class 而且即使不是 Immutable 它也可以 tracking 到 changes)

Entity

public class Order
{
public int Id { get; set; }
public OrderCustomerInfo CustomerInfo { get; set; } = null!;
public decimal Amount { get; set; }
} public record OrderCustomerInfo(string Name, string Phone);

Add Order

using var db = new ApplicationDbContext();
db.Orders.Add(new()
{
Amount = 100,
CustomerInfo = new("Derrick", "+60 16-773 7062")
});
db.SaveChanges();

update CustomerInfo

var order = db.Orders.First();
order.CustomerInfo = order.CustomerInfo with { Name = "New Name" };
db.SaveChanges();

个人觉得使用 record 会比使用 class 更合适。不熟悉 record 的朋友可以看这篇 C# – Record, Class, Struct

总结

我们可以把 Owned Entity Types 视为变种的 one-to-one 和 one-to-many。

它最大的好处是自动 Include,自动 Table Splitting,而且可以 ToJson,底层思想继承自 one-to-one 和 one-to-many Entity Types 也算很好理解。

目前只有一个缺陷 -- 不支持继承结构。

Complex Types 不是 one-to-one 和 one-to-many 的概念,它没有 Key,它不是 Entity Types。

它适合用在 Object Value,就是说这些属性确确实实是 Property 只是它们有关系所以被 group 在一起。

比如说,地址本来是一个 string "11, Jalan Merak 22, Taman Mutiara Rini, 81300 Johor Malaysia",我想把它做的有结构,

于是拆分成 Street1, Street2, PostalCode, State, Country,这种情况就适合使用 Complex Types 来表达。

EF Core – Owned Entity Types & Complex Types的更多相关文章

  1. 【EF Core】Entity Framework Core 批处理语句

    在Entity Framework Core (EF Core)有许多新的功能,最令人期待的功能之一就是批处理语句.那么批处理语句是什么呢?批处理语句意味着它不会为每个插入/更新/删除语句发送单独的请 ...

  2. Entity Framework (EF) Core工具创建一对多和多对多的关系

     一. EntirtyFramework(EF)简介 EntirtyFramework框架是一个轻量级的可扩展版本的流行实体框架数据访问技术,微软官方提供的ORM工具让开发人员节省数据库访问的代码时间 ...

  3. 在EF Core里面如何使用以前EntityFramework的DbContext.Database.SqlQuery<SomeModel>自定义查询

    问: With Entity Framework Core removing dbData.Database.SqlQuery<SomeModel> I can't find a solu ...

  4. ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 Entity Fram ...

  5. .NET 5/.NET Core使用EF Core 5连接MySQL数据库写入/读取数据示例教程

    本文首发于<.NET 5/.NET Core使用EF Core 5(Entity Framework Core)连接MySQL数据库写入/读取数据示例教程> 前言 在.NET Core/. ...

  6. EF Core 新特性——Owned Entity Types

    Owned Entity Types 首先owned entity type是EF Core 2.0的新特性. 至于什么是owned entity types,可以先把他理解为EF Core官方支持的 ...

  7. EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之彩蛋篇

    EFCore Owned Entity Types的定义 EFCore Owned Entity Types的文档在这里:https://docs.microsoft.com/zh-cn/ef/cor ...

  8. EFCore Owned Entity Types,彩蛋乎?鸡肋乎?之鸡肋篇

    鸡肋 鸡肋(Chicken ribs),现代汉语词语,出自<三国志·魏书·武帝纪>裴松之注引<九州春秋>曰:"夫鸡肋,弃之如可惜,食之无所得,以比汉中,知王欲还也.& ...

  9. Lerning Entity Framework 6 ------ Complex types

    Complex types are classes that map to a subset of columns of a table.They don't contains key. They a ...

  10. 张高兴的 Entity Framework Core 即学即用:(一)创建第一个 EF Core 应用

    写在前面 Entity Framework Core (EF Core) 是 .NET 平台流行的对象关系映射(ORM)框架.虽然 .NET 平台中 ORM 框架有很多,比如 Dapper.NHibe ...

随机推荐

  1. 《探索Python Requests中的代理应用与实践》

    requests加代理 高匿API代理 此处使用的小象代理:1元100个,便宜,可以购买尝试加下代理 存活期1到2分钟 import time import requests from lxml im ...

  2. 轻松掌握useAsyncData获取异步数据

    title: 轻松掌握useAsyncData获取异步数据 date: 2024/7/12 updated: 2024/7/12 author: cmdragon excerpt: 摘要:本文详细介绍 ...

  3. 第二部分:关键技术领域的开源实践【内网穿透FRP】

    FRP简介 FRP(Fast Reverse Proxy)作为一种高性能的内网穿透工具,支持 TCP.UDP.HTTP.HTTPS 等多种协议.可以将内网服务以安全.便捷的方式通过具有公网IP节点(云 ...

  4. 解决方案 | Get-AppxPackage : 无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动

    前几天由于需要卸载一些win10自带应用,导致onenote2016无法启动(根本原因:当时可能remove-appxpackage导致某些微软原生应用出现问题),同时今天使用power shell出 ...

  5. LeViT:Facebook提出推理优化的混合ViT主干网络 | ICCV 2021

    论文提出了用于快速图像分类推理的混合神经网络LeVIT,在不同的硬件平台上进行不同的效率衡量标准的测试.总体而言,LeViT在速度/准确性权衡方面明显优于现有的卷积神经网络和ViT,比如在80%的Im ...

  6. 浅谈Git架构和如何避免代码覆盖的事故

    浅谈Git架构和如何避免代码覆盖的事故 Git 不同于 SVN 的地方在于, Git 是分布式的版本管理系统, 所有的客户端和服务器都保存了一份代码, 涉及到仓库仓之间的同步, 所以处理不当极易造成冲 ...

  7. Java--匿名类(学习笔记)

    匿名类的特点:(1) 匿名类是final类:(3) 在匿名类中可以定义实例变量和若干个实例初始化代码块和新的实例方法.Java虚拟机首先调用父类的构造方法,然后按照实例变量的和实例初始化代码块定义的先 ...

  8. Jmeter参数化2-读取文件

    如果你想要jmeter批量生成 指定 的参数值数据,可以使用导入文件参数化方法来实现 下面讲述两种jmeter读取文件参数值方法:"CSV 数据文件设置"."函数助手&q ...

  9. 【YAML】非标记语言的标记语言

    什么是YAML? YAML是"YAML Ain't a Markup Language"(YAML不是一种标记语言)的递归缩写. 在开发的这种语言时,YAML 的意思其实是:&qu ...

  10. Git安装与windows终端配置Git-bash

    Git概述 简介 Git是一个分布式版本控制工具,通常用来对软件开发过程中的源代码文件进行管理.通过Git仓库存储和管理这些文件,Git仓库分为两种: 本地仓库:开发人员自己电脑上的Git仓库 远程仓 ...