.NET Core Entity Framework Core
1. 安装 Entity Framework Core
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design
Install-Package Microsoft.EntityFrameworkCore.Tools
2. 根据数据库生成模型及 DbContext
Scaffold-DbContext "Server=192.168.0.1;Database=SampleDB;uid=sa;pwd=123456;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
默认上面的数据库连接字符串会被保存在 SampleDBContext
的 OnConfiguring
方法中。
生成的模型会保存在 OutputDir
属性中设置的目录中,命名空间同目录结构一致。
生成的类名会去除特殊符号,所以数据表命名时不要出现去除特殊符号后名字一样的情况,否则后生成的模型会覆盖前面的模型,另外如果只是大小写不一样,还可能会导致文件名和类名不一致。所以应该在创建表时避免出现这种情况。
3. 配置数据库链接
在
appsettings.json
中配置ConnectionStrings
。json{ "ConnectionStrings": { "Sample": "Server=192.168.0.1;Database=SampleDB;uid=sa;pwd=123456;" } }
新增
SampleDBContext
的构造函数cspublic partial class SampleDBContext : DbContext { public OctopusB2bContext(DbContextOptions<SampleDBContext> options) : base(options) { } }
在
Startup.cs
中注册该数据库连接cspublic void ConfigureServices(IServiceCollection services) { // 注册数据库 services.AddDbContext<SampleDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Sample"))); services.AddMvc(); // ... }
4. 在 Controller
中使用该 DBContext
[Produces("application/json")]
[Route("api/User")]
public class UserController : Controller
{
private readonly SampleDBContext _context;
public UserController(SampleDBContext context)
{
_context = context;
}
// GET: api/User/5
[HttpGet("{id}")]
public async Task<IActionResult> GetTSysUser([FromRoute] Guid id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var tSysUser = await _context.TSysUser.SingleOrDefaultAsync(m => m.Guid == id);
if (tSysUser == null)
{
return NotFound();
}
return Ok(tSysUser);
}
}
6. 在 Service
中使用该 DBContext
创建
Service
interface
cspublic interface IAccountService { List<UserModel> GetUsers(); }
实现
cspublic class AccountService : IAccountService { private readonly SampleDBContext _context; public AccountService(SampleDBContext context) { _context = context; } public List<UserModel> GetUsers() { // 使用 Select 转型 List<UserModel> uls1 = _context.TSysUser .FromSql("SELECT * FROM T_SYS_User") .Take(10) .Select(u => new UserModel() { Guid = u.Guid, Account = u.Account, Name = u.Name, }).ToList(); //// 下面代码运行会出错 //List<UserModel> uls2 = _context.UserLogin.FromSql("SELECT A.Guid, A.Account, A.Name, B.Guid BuyerStoreGuid, B.Name BuyerStoreName " // + "FROM T_SYS_User A INNER JOIN T_CORP_BuyerStore B ON A.DepartmentGuid = B.Guid") // .Take(10) // .ToList(); // 使用自定义的扩展方法实现查询结果直接返回需要的模型 List<UserModel> uls3 = _context.ExecSQL<UserModel>("SELECT TOP 10 A.Guid, A.Account, A.Name, A.DepartmentGuid FROM T_SYS_User A WHERE AccountType = 4 AND DepartmentGuid IS NOT NULL"); List<TCorpBuyerStore> buyerStores = _context.TCorpBuyerStore.Where(b => uls3.Select(u => u.DepartmentGuid).Contains(b.Guid.ToString())).ToList(); foreach (var user in uls3) { TCorpBuyerStore buyerStore = buyerStores.FirstOrDefault(b => b.Guid.ToString() == user.DepartmentGuid); if (buyerStore != null) { user.BuyerStoreGuid = buyerStore.Guid; user.BuyerStoreName = buyerStore.Name; } } //List<UserModel> uls4 = _context.ExecSQL<UserModel>("SELECT TOP 10 A.Guid, A.Account, A.Name, B.Guid BuyerStoreGuid, B.Name BuyerStoreName " // + "FROM T_SYS_User A INNER JOIN T_CORP_BuyerStore B ON A.DepartmentGuid = CAST(B.Guid AS nvarchar(500))"); return uls3; } }
在
Startup.cs
中注册服务cs// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // 注册数据库 services.AddDbContext<SampleDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("OCTOPUS3_0"))); services.AddMvc(); // 注册 Service services.AddScoped<IAccountService, AccountService>(); }
在
Controller
中调用服务cs[Produces("application/json")] [Route("api/Account")] public class AccountController : Controller { private readonly IAccountService _service; public AccountController(IAccountService service) { _service = service; } [HttpGet("")] public List<UserModel> GetUsers() { return _service.GetUsers(); } }
附
在
Entity Framework
中可以通过_context.Database.SqlQuery<T>()
方法实现自定义的查询及返回自定义的模型,但在Entity Framework Core
中,无法实现该功能(仅有DbSet<T>.FromSql
方法)。csvar blogs = db.Database.SqlQuery<HomeCardModel>(sql, sqlParams.ToArray()) .OrderByDescending(b => b.CreateTime) .ToList().ToPagedList(page, size);
使用
DbSet<T>.FromSql
功能结合IQueryable<T>.Select
方法可以实现部分效果,但局限比较大,只能在Select
的TResult
的字段都包含在T
中时,才可以使用。cs// 使用 Select 转型 List<UserModel> uls1 = _context.TSysUser .FromSql("SELECT * FROM T_SYS_User") .Take(10) .Select(u => new UserModel() { Guid = u.Guid, Account = u.Account, Name = u.Name, }).ToList();
在 Raw store access APIs: Support for ad hoc mapping of arbitrary types 中给了一个暂时的替代方法。下面代码是在此基础上稍微修改后的版本,虽然可以实现该功能,但并不推荐使用。
cspublic static List<T> ExecSQL<T>(this SampleDBContext context, string query) { using (var command = context.Database.GetDbConnection().CreateCommand()) { command.CommandText = query; command.CommandType = CommandType.Text; // 判断连接是否已打开 if (context.Database.GetDbConnection().State == ConnectionState.Closed) { context.Database.OpenConnection(); } using (var result = command.ExecuteReader()) { List<T> list = new List<T>(); T obj = default(T); while (result.Read()) { obj = Activator.CreateInstance<T>(); foreach (PropertyInfo prop in obj.GetType().GetProperties()) { if (ReaderExists(result, prop.Name) && !object.Equals(result[prop.Name], DBNull.Value)) { prop.SetValue(obj, result[prop.Name], null); } } list.Add(obj); } return list; } } } private static bool ReaderExists(DbDataReader dr, string columnName) { dr.GetSchemaTable().DefaultView.RowFilter = "ColumnName= '" + columnName + "'"; return (dr.GetSchemaTable().DefaultView.Count > 0); }
cs// 使用自定义的扩展方法实现查询结果直接返回需要的模型 List<UserModel> uls3 = _context.ExecSQL<UserModel>("SELECT TOP 10 A.Guid, A.Account, A.Name, A.DepartmentGuid FROM T_SYS_User A WHERE AccountType = 4 AND DepartmentGuid IS NOT NULL");
生成模型时报 PowerShell 版本过低错误
The Entity Framework Core Package Manager Console Tools don't support PowerShell version 2.0. Upgrade to PowerShell version 3.0 or higher, restart Visual Studio, and try again.
需要安装新版的 PowerShell,参照 Powershell 介绍和安装 安装即可。最新版 PowerShell 4.0 的 介绍 及 下载地址。
在一个有 Docker 支持的项目中使用
Scaffold-DbContext
命令生成模型时,发生了如下错误:powershellPM> Scaffold-DbContext "Server=192.168.0.1;Database=testdb;uid=sa;pwd=sa;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models Scaffold-DbContext : 使用“1”个参数调用“Build”时发生异常:“The DOCKER_REGISTRY variable is not set. Defaulting to a blank string. The Compose file is invalid because: Service octopus.cloud has neither an image nor a build context specified. At least one must be provided.。 如需更多故障排除信息,请转到 http://aka.ms/DockerToolsTroubleshooting”所在位置 行:1 字符:1 + Scaffold-DbContext "Server=192.168.0.1;Database=testdb;uid=sa ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Scaffold-DbContext], MethodInvocationException + FullyQualifiedErrorId : ComMethodTargetInvocation,Scaffold-DbContext
说是
DOCKER_REGISTRY
变量没有设置,但是这个项目是可以正常通过 Docker 启动的。对策
由于不清楚是在哪里设置这个
DOCKER_REGISTRY
变量,所以使用了暂时卸载项目的方式。右键 docker-compose 项目,选择 卸载项目 ,然后再执行就 OK 了。