更新 : 2020-02-28

乐观锁

依据某些数据 -> 计算 -> 决定结果. 在这个过程中确保依赖数据不变,这个就是锁的作用.

比如一个客户跟我要了一个货,我得锁着给他,不可以给其它同事拿去卖. 一样的道理

更新 2020-01-01

当 nullable + unique 时, ef 会自动的帮我们添加 filter... 如果不希望它自作聪明的话,可以把它关了。

clustered 就没有 filter 哦

更新: 2019-11-17

ef core 没法 where COLLATE SQL_Latin1_General_CP1_CS_AS 区分大小写.

https://github.com/aspnet/EntityFrameworkCore/issues/8813

https://github.com/aspnet/EntityFrameworkCore/issues/1222

目前能做的就是 rawSql 或者在 column 上 set collate

  1. alter table Products alter column name nvarchar(64) collate SQL_Latin1_General_CP1_CS_AS;
  2. alter table Products alter column name nvarchar(64) collate SQL_Latin1_General_CP1_CI_AS;

更新: 2019-08-03

rename table 有时候会很麻烦, migration 会删除表和从创。里面有资料就不方便了。

可以手动写代码

https://stackoverflow.com/questions/13296996/entity-framework-migrations-renaming-tables-and-columns

  1. public override void Up()
  2. {
  3. RenameTable("ReportSections", "ReportPages");
  4. RenameTable("ReportSectionGroups", "ReportSections");
  5. RenameColumn("ReportPages", "Group_Id", "Section_Id");
  6. }

就如同我们跑 sql 语句类似了。

  1. use klc2;
  2. EXEC sp_rename 'TableSettings', 'UserSettings';
  3. EXEC sp_rename 'UserSettings.tableName', 'name', 'COLUMN';
  4. EXEC sp_rename 'UserSettings.settingJson', 'json', 'COLUMN'; -- INDEX 也是这个换法
  5. EXEC sp_rename 'dbo.PK_UserSettingsa', 'PK_UserSettings';
  6. EXEC sp_rename 'dbo.FK_UserSettings_AspNetUsers_userIa', 'FK_UserSettings_AspNetUsers_userId';
  7.  
  8. -- 检查名字
  9. SELECT name, SCHEMA_NAME(schema_id) AS schema_name, type_desc
  10. FROM sys.objects
  11. WHERE parent_object_id = (OBJECT_ID('UserSettings'))
  12. AND type IN ('C','F', 'PK');

直接跑

  1. migrationBuilder.Sql(
  2. @"
  3. EXEC sp_rename 'TableSettings', 'UserSettings';
  4. EXEC sp_rename 'UserSettings.tableName', 'name', 'COLUMN';
  5. EXEC sp_rename 'UserSettings.settingJson', 'json', 'COLUMN';
  6. EXEC sp_rename 'dbo.PK_TableSettings', 'PK_UserSettings';
  7. EXEC sp_rename 'dbo.FK_TableSettings_AspNetUsers_userId', 'FK_UserSettings_AspNetUsers_userId';
  8. ");

如果是要换 column length for 有 relation 的也是不可以用 ef core migrations, 去 sql management studio 换, 它会批量, 然后 migration 里面放 return 假假跑一下就可以了.

更新: 2019-06-12

不小心踩坑

  1. var adidas = new Supplier { name = "adidas" };
  2. Db.Suppliers.Add(adidas);
  3. Db.SaveChanges(); // 关键
  4. Db.Products.Add(new Product
  5. {
  6. supplier = adidas,
  7. code = "",
    // supplierId = adidas.Id
  8. });
  9. Db.SaveChanges();

这样会报错哦.

因为第一个 savechange 已经把 adidas 有了 id 那么 product.add 时我又传入了 adidas

那么 ef 会以为我要去创建, 但是它有 id 了啊, sql 就会报错了 (这里 id 是 auto increment 的情况)

所以按上面的写法其实第一个 save change 是多余的, 如果第一个 save change 是必须的话,下面就应该 update foreign key 就好了。

更新 : 2019-05-04

动态生成 fluent api

enum to string 这个方式我们可以通过 fluent api 来实现, 比如下面这样.

  1. modelBuilder.Entity<Order>().Property(p => p.status).IsRequired().HasMaxLength(128)
  2. .HasConversion(
  3. v => v.ToString(),
  4. v => (OrderStatus)Enum.Parse(typeof(OrderStatus), v)
  5. );

但是如果我们有非常多的 table 和 enum,每一个属性都写这么一行...累丫

那么我们可以通过反射来实现这个调用.

步骤如下,首先找出所有的 EntityType

  1. foreach (var entity in modelBuilder.Model.GetEntityTypes())

接着把所有 enum property 找出来

  1. var props = entity.ClrType.GetProperties()
  2. .Where(p => p.PropertyType.IsEnum)
  3. .ToList();

然后每一个 prop 都要绑定 HasConversion

  1. foreach (var prop in props)

接着就是反射调用

  1. // modelBuilder.Entity<Order>()
  2. var entityMethod = modelBuilder.GetType().GetMethod(nameof(ModelBuilder.Entity), 1, new Type[] { });
  3. entityMethod = entityMethod.MakeGenericMethod(new[] { entity.ClrType });
  4. var entityTypeBuilder = entityMethod.Invoke(modelBuilder, new object[] { });

接着

  1. // ...Property(entity => entity.status)
  2. var paramType = typeof(Expression<>).MakeGenericType(new[] { typeof(Func<,>).MakeGenericType(new[] { entity.ClrType, prop.PropertyType }) });
  3. var propertyMethod = entityTypeBuilder.GetType().GetMethods()
  4. .Where(m => m.Name == nameof(EntityTypeBuilder.Property) && m.GetGenericArguments().Count() == 1)
  5. .Select(m => m.MakeGenericMethod(prop.PropertyType))
  6. .Single(m =>
  7. {
  8. var parameters = m.GetParameters();
  9. if (parameters.Count() != 1) return false;
  10. return parameters[0].ParameterType == paramType;
  11. });
  12. var paramEntityExp = Expression.Parameter(entity.ClrType, "entity");
  13. var getPropExp = Expression.Property(paramEntityExp, prop);
  14. var lambdaExp = Expression.Lambda(getPropExp, paramEntityExp);
  15. var propertyBuilder = propertyMethod.Invoke(entityTypeBuilder, new[] { lambdaExp });

最后

  1. // ...HasConversion(propertyValue => propertyValue.ToString(), sqlValue => (OrderStatus)Enum.Parse(typeof(OrderStatus), propertyValue));
  2. var firstParamPropertyExp = Expression.Parameter(prop.PropertyType, "propertyValue");
  3. var firstParamCallMethod = typeof(Enum).GetMethod(nameof(Enum.ToString), new Type[] { });
  4. var firstParamCallMethodExp = Expression.Call(
  5. firstParamPropertyExp, firstParamCallMethod
  6. );
  7. var firstLambdaExp = Expression.Lambda(firstParamCallMethodExp, firstParamPropertyExp);
  8.  
  9. var secondParamPropertyExp = Expression.Parameter(typeof(string), "sqlValue");
  10. var secondParamCallMethod = typeof(Enum).GetMethod(nameof(Enum.Parse), new[] { typeof(Type), typeof(string) });
  11. var secondParamCallMethodExp = Expression.Call(
  12. secondParamCallMethod, new Expression[] { Expression.Constant(prop.PropertyType), secondParamPropertyExp }
  13. );
  14. var secondParamConvertExp = Expression.Convert(
  15. secondParamCallMethodExp,
  16. prop.PropertyType
  17. );
  18. var secondLambdaExp = Expression.Lambda(secondParamConvertExp, secondParamPropertyExp);
  19.  
  20. var firstParamType = typeof(Expression<>).MakeGenericType(new[] { typeof(Func<,>).MakeGenericType(new[] { prop.PropertyType, typeof(string) }) });
  21. var secondParamType = typeof(Expression<>).MakeGenericType(new[] { typeof(Func<,>).MakeGenericType(new[] { typeof(string), prop.PropertyType }) });
  22. var hasConversionMethod = propertyBuilder.GetType().GetMethods()
  23. .Where(m => m.Name == nameof(PropertyBuilder.HasConversion) && m.GetGenericArguments().Count() == )
  24. .Select(m => m.MakeGenericMethod(typeof(string)))
  25. .Single(m =>
  26. {
  27. var parameters = m.GetParameters();
  28. if (parameters.Count() != ) return false;
  29. return parameters[].ParameterType == firstParamType && parameters[].ParameterType == secondParamType;
  30. });
  31. hasConversionMethod.Invoke(propertyBuilder, new[] { firstLambdaExp, secondLambdaExp });

这样就可以了, 其实那些 data annotation 也是这样子做出来,通过反射获取 Attribute 的值,然后调用 fluent api. 有了上面这个概念就可以写自己的 data annotation 啦~~

反射和表达式树是 c# 常用到的功能,大家可以多学学.

更新 : 2018-11-26

这里记入一下关于 foreignKey cascade action

默认情况下如果我们使用 data annotation

required + foreginkey . ef 会帮我们设计成 cascade delete

如果 foreignkey + nullable 就会是 cascade restrict.

如果使用 fluent api 的话就由我们设计了.

ef 6.x 有一个功能可以把所有的 cascade delete 停掉, ef core 不一样了

https://github.com/aspnet/EntityFrameworkCore/issues/3815

我看了一下 identity 也是有用 cascade delete 的,所以停到完好像不是 best practices.

所以我的规范是, 要 cascade delete 的话,可以用 data annotations

如果要限制的话,用 fluent api

fluent api 可以双向设置.

  1. modelBuilder.Entity<QuestionVsSkill>().HasOne(e => e.skill).WithMany().HasForeignKey(e => e.skillId).OnDelete(DeleteBehavior.Restrict);
  2. modelBuilder.Entity<Skill>().HasMany<QuestionVsSkill>().WithOne(e => e.skill).HasForeignKey(e => e.skillId).OnDelete(DeleteBehavior.Restrict);

上面 2 个是一样的结果.

注意 : 如果我们的 entity 有 navigation property 就要放, 如果没有就空 e.g. WithMany() <--params is empty

有一个比较难搞的东西,就是 database 循环引用, 比如我们有继承 table, foreignkey 就不支持 sql cascade null or delete 了

这时需要设置成 restrict 然后在处理的时候自己写程序实现 cascade null or delete.

Entity Framework 已经很多年了.

ef core 和 从前的 ef 6.x 用起来是差不多的.

ef core 的有点事跨平台,更好的性能,持续发展中 ...

ef 6.x 是功能比较齐全, 但是逐渐被淘汰了..

基本用法和从前是一样的, 比如

创建 DbContext, 定义 Entity 还有关系等等

  1. namespace Project.Models
  2. {
  3. public class DB : DbContext
  4. {
  5. public DB(DbContextOptions<DB> options) : base(options)
  6. {
  7.  
  8. }
  9. protected override void OnModelCreating(ModelBuilder modelBuilder)
  10. {
  11. }
  12. public virtual DbSet<Product> Products { get; set; }
  13. }
  14. }

在 startup.cs 里的 ConfigureServices 加入

  1. services.AddDbContext<SchoolContext>(options =>
  2. options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

appsetting.json

  1. "ConnectionStrings": {
  2. "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  3. //"DefaultConnection": "Server=192.168.1.152;Database=databaseName;User Id=username;Password=password;"
  4. }

在 controller 依赖注入调用就可以了

  1. public class HomeController : Controller
  2. {
  3. public DB Db { get; set; }
  4. public HomeController(DB db) {
  5. Db = db;
  6. }
  7. public async Task<IActionResult> Index()
  8. {
  9. var products = await Db.Products.ToListAsync();
  10. return View();
  11. }
  12. }

要生产 database 或需要 update database 可以使用 ef core 的工具 migrations

这个有点像是 version control 一样, 创建 migrations -> 工具会生产一个 Up 的代码和一个 Down 的代码 用于对数据库添加内容和移除内容.

自动生产以后,我们可以手动修改这些代码,然后 update database, 这样当我们 commit 的时候 Up 代码被执行, rollback 的时候 Down 代码被执行, 大概就是这个过程.

它是用命令来操作的, 打开 Package Manager Console

输入 add-migration init (或者 dotnet ef migrations add init)

init 是一个名字,可以随意放.

输入 update-database (或者 dotnet ef database update)

这时 Up 代码执行, 数据库就被更新了.

如果做错了要删除

输入 remove-migration (或者 dotnet ef migrations remove)

如果要 rollback 某个 version

输入 update-database somename

somename 就是你第一步我们为那一次 migrations 取得名字.

这样 Down 代码被执行,数据库成功还原了.

migrations 仅适合用于数据库架构的更新, 而不是数据库资料的更新哦.

多个 context 的时候,我们需要表示名字

add-migration ids -context PersistedGrantDbContext

或者 dotnet ef migrations add init --context applicationdbcontext

Asp.net core 学习笔记 ( ef core )的更多相关文章

  1. Asp.net core 学习笔记 ( ef core transaction scope & change level )

    ef core 有 unit of work 的概念,当我们 save change 时会自动使用 transaction 确保更新的一致性. 隔离级别是默认的 read committed 不允许脏 ...

  2. Asp.net core 学习笔记 ef core Surrogate Key, Natural Key, Alternate Keys

    更新: 2019-12-23 foreignkey 并不一样要配上 alternate key,其实只要是 unique 就可以了. 和 sql server 是一样的, 经常有一种错觉 primar ...

  3. Asp.Net Core学习笔记:入门篇

    Asp.Net Core 学习 基于.Net Core 2.2版本的学习笔记. 常识 像Django那样自动检查代码更新,自动重载服务器(太方便了) dotnet watch run 托管设置 设置项 ...

  4. .NET CORE学习笔记系列(2)——依赖注入[6]: .NET Core DI框架[编程体验]

    原文https://www.cnblogs.com/artech/p/net-core-di-06.html 毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动 ...

  5. .NET CORE学习笔记系列(2)——依赖注入【3】依赖注入模式

    原文:https://www.cnblogs.com/artech/p/net-core-di-03.html IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架中以实现对流 ...

  6. .NET CORE学习笔记系列(2)——依赖注入【1】控制反转IOC

    原文:https://www.cnblogs.com/artech/p/net-core-di-01.html 一.流程控制的反转 IoC的全名Inverse of Control,翻译成中文就是“控 ...

  7. .NET CORE学习笔记系列(2)——依赖注入[7]: .NET Core DI框架[服务注册]

    原文https://www.cnblogs.com/artech/p/net-core-di-07.html 包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IS ...

  8. .NET CORE学习笔记系列(2)——依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在上篇中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的 ...

  9. .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]

    原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...

随机推荐

  1. host元素的属性autoDeploy和reloadable的区别

    web.xml文件的修改会触发AutoDeploy,受host节的autoDeploy配置值的影响. class类文件修改会触发Reload操作,受reloadable配置值的影响. 而autoDep ...

  2. Android之电话拨号和短信

    拨打电话号码,需要添加权限设置,在安装的应用程序信息中可以看到此权限信息Intent doSth=new Intent();//意图:你想做什么呢?doSth.setAction(Intent.ACT ...

  3. js通过 URL下载文件

    页面上一个button,点击之后触发一个function去请求数据,返回 pdf/epub 的URL,然后下载这个文件. 本来是直接用 a 写的,href里放资源地址,target设为'_blank' ...

  4. Activity之Serializable

    Student.java package cn.itcast.wh08.multiactivity.domain; import java.io.Serializable; public class ...

  5. 【Python48--魔法方法:迭代器&生成器】

    一.迭代器 1.iter() __iter__() 2.next() __next__() 二.用while语句实现for语句相同的功能 for each in range(5): print(eac ...

  6. 【Python047-魔法方法:定制序列】

    一.协议是什么 1.协议(protocols)与其他编程语言中的接口很相似,它规定你那些方法必须要定义.然而在Python中协议就显的不那么正式,事实上,在Python中,协议更像是一种指南 2.容器 ...

  7. python --- 19 判断对象所属,区分函数和对象, 反射

    一.判断对象所属 isinstance, type , issubclass 1.issubclass(x,y)    判断x是否是y 的子类 2.type(x)  精准返回x 的数据类型 3.isi ...

  8. Git和Jenkins日记之没有新提交代码

    日期:2017/3/9 今天查看Jenkins运行代码记录的日志时,发现并没有昨天新提交的代码,然后查看了Jenkins的测试项目中所有的自动化测试用例, 并没有看到昨天新提交的测试用例,又查看了gi ...

  9. 没有使用Material组件

    // 这个App没有使用Material组件, 如Scaffold. // 一般来说, app没有使用Scaffold的话,会有一个黑色的背景和一个默认为黑色的文本颜色. // 这个app,将背景色改 ...

  10. 终于知道linux firefox不能播放 web在线词典的单词发音了! --通过banshee安装gstreamer1-libav/-plugins-ugly/plugins-bad三个mp3插件.

    mpg123 是一个 命令行的播放器, 他没有gui界面. 直接用 mpg123 test.mp3 就可以直接播放, 而且, 最终要的是, mpg123 自带了mp3的解码器. mpeg: movin ...