同步OpenAuth.Core最新代码

This commit is contained in:
yubaolee
2020-12-17 23:04:04 +08:00
parent 7ce8a219cf
commit 7217e7a924
61 changed files with 1112 additions and 315 deletions

View File

@@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Infrastructure;
using Microsoft.EntityFrameworkCore;
using OpenAuth.Repository.Core;
@@ -29,7 +30,7 @@ namespace OpenAuth.Repository
return Filter(exp);
}
public bool IsExist(Expression<Func<T, bool>> exp)
public bool Any(Expression<Func<T, bool>> exp)
{
return _context.Set<T>().Any(exp);
}
@@ -37,7 +38,7 @@ namespace OpenAuth.Repository
/// <summary>
/// 查找单个,且不被上下文所跟踪
/// </summary>
public T FindSingle(Expression<Func<T, bool>> exp)
public T FirstOrDefault(Expression<Func<T, bool>> exp)
{
return _context.Set<T>().AsNoTracking().FirstOrDefault(exp);
}
@@ -61,7 +62,7 @@ namespace OpenAuth.Repository
/// <summary>
/// 根据过滤条件获取记录数
/// </summary>
public int GetCount(Expression<Func<T, bool>> exp = null)
public int Count(Expression<Func<T, bool>> exp = null)
{
return Filter(exp).Count();
}
@@ -116,7 +117,6 @@ namespace OpenAuth.Repository
Save();
}
/// <summary>
/// 实现按需要只更新部分更新
/// <para>如Update(u =>u.Id==1,u =>new User{Name="ok"});</para>
@@ -132,7 +132,7 @@ namespace OpenAuth.Repository
{
_context.Set<T>().Where(exp).Delete();
}
public void Save()
{
try
@@ -169,7 +169,7 @@ namespace OpenAuth.Repository
return dbSet;
}
public int ExecuteSql(string sql)
public int ExecuteSqlRaw(string sql)
{
return _context.Database.ExecuteSqlRaw(sql);
}
@@ -193,5 +193,129 @@ namespace OpenAuth.Repository
{
return _context.Query<T>().FromSqlRaw(sql, parameters);
}
#region
/// <summary>
/// 异步执行sql
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public async Task<int> ExecuteSqlRawAsync(string sql)
{
return await _context.Database.ExecuteSqlRawAsync(sql);
}
public async Task<int> AddAsync(T entity)
{
if (entity.KeyIsNull())
{
entity.GenerateDefaultKeyVal();
}
_context.Set<T>().Add(entity);
return await SaveAsync();
//_context.Entry(entity).State = EntityState.Detached;
}
public async Task<int> BatchAddAsync(T[] entities)
{
foreach (var entity in entities)
{
if (entity.KeyIsNull())
{
entity.GenerateDefaultKeyVal();
}
}
await _context.Set<T>().AddRangeAsync(entities);
return await SaveAsync();
}
/// <summary>
/// 异步更新
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public async Task<int> UpdateAsync(T entity)
{
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
//如果数据没有发生变化
if (!this._context.ChangeTracker.HasChanges())
{
return 0;
}
return await SaveAsync();
}
/// <summary>
/// 异步删除
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public async Task<int> DeleteAsync(T entity)
{
_context.Set<T>().Remove(entity);
return await SaveAsync();
}
/// <summary>
/// 异步保存
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<int> SaveAsync()
{
try
{
var entities = _context.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added
|| e.State == EntityState.Modified)
.Select(e => e.Entity);
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext, validateAllProperties: true);
}
return await _context.SaveChangesAsync();
}
catch (ValidationException exc)
{
Console.WriteLine($"{nameof(Save)} validation exception: {exc?.Message}");
throw (exc.InnerException as Exception ?? exc);
}
catch (Exception ex) //DbUpdateException
{
throw (ex.InnerException as Exception ?? ex);
}
}
/// <summary>
/// 根据过滤条件获取记录数
/// </summary>
public async Task<int> CountAsync(Expression<Func<T, bool>> exp = null)
{
return await Filter(exp).CountAsync();
}
public async Task<bool> AnyAsync(Expression<Func<T, bool>> exp)
{
return await _context.Set<T>().AnyAsync(exp);
}
/// <summary>
/// 查找单个,且不被上下文所跟踪
/// </summary>
public async Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> exp)
{
return await _context.Set<T>().AsNoTracking().FirstOrDefaultAsync(exp);
}
#endregion
}
}

View File

@@ -15,29 +15,30 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace OpenAuth.Repository.Interface
{
public interface IRepository<T> where T : class
{
T FindSingle(Expression<Func<T, bool>> exp = null);
bool IsExist(Expression<Func<T, bool>> exp);
/// <summary>
/// 返回一个单独的实体如果记录多于1个则取第一个
/// </summary>
T FirstOrDefault(Expression<Func<T, bool>> exp = null);
/// <summary>
/// 判断指定条件的记录是否存在
/// </summary>
bool Any(Expression<Func<T, bool>> exp);
IQueryable<T> Find(Expression<Func<T, bool>> exp = null);
IQueryable<T> Find(int pageindex = 1, int pagesize = 10, string orderby = "",
Expression<Func<T, bool>> exp = null);
int GetCount(Expression<Func<T, bool>> exp = null);
int Count(Expression<Func<T, bool>> exp = null);
void Add(T entity);
void BatchAdd(T[] entities);
/// <summary>
/// 更新一个实体的所有属性
/// </summary>
void Update(T entity);
void Delete(T entity);
@@ -48,6 +49,7 @@ namespace OpenAuth.Repository.Interface
/// <param name="where">更新条件</param>
/// <param name="entity">更新后的实体</param>
void Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> entity);
/// <summary>
/// 批量删除
/// </summary>
@@ -55,19 +57,35 @@ namespace OpenAuth.Repository.Interface
void Save();
int ExecuteSql(string sql);
/// <summary>
int ExecuteSqlRaw(string sql);
/// <summary>
/// 使用SQL脚本查询
/// </summary>
/// <typeparam name="T"> T为数据库实体</typeparam>
/// <returns></returns>
IQueryable<T> FromSql(string sql, params object[] parameters);
/// <summary>
/// 使用SQL脚本查询
/// </summary>
/// <typeparam name="T"> T为非数据库实体需要在DbContext中增加对应的DbQuery</typeparam>
/// <returns></returns>
/// <summary>
/// 使用SQL脚本查询
/// </summary>
/// <typeparam name="T"> T为非数据库实体需要在DbContext中增加对应的DbQuery</typeparam>
/// <returns></returns>
IQueryable<T> Query(string sql, params object[] parameters);
#region
Task<int> ExecuteSqlRawAsync(string sql);
Task<int> AddAsync(T entity);
Task<int> BatchAddAsync(T[] entities);
Task<int> UpdateAsync(T entity);
Task<int> DeleteAsync(T entity);
Task<int> SaveAsync();
Task<int> CountAsync(Expression<Func<T, bool>> exp = null);
Task<bool> AnyAsync(Expression<Func<T, bool>> exp);
Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> exp);
#endregion
}
}

View File

@@ -4,7 +4,7 @@
// Created : 04-29-2016
//
// Last Modified By : yubaolee
// Last Modified On : 04-29-2016
// Last Modified On : 12-15-2020
// Contact : Microsoft
// File: IUnitWork.cs
// ***********************************************************************
@@ -12,12 +12,14 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using OpenAuth.Repository.Core;
namespace OpenAuth.Repository.Interface
{
/// <summary>
/// 工作单元接口
/// 使用详见http://doc.openauth.me/core/unitwork.html
/// <para> 适合在一下情况使用:</para>
/// <para>1 在同一事务中进行多表操作</para>
/// <para>2 需要多表联合查询</para>
@@ -25,18 +27,39 @@ namespace OpenAuth.Repository.Interface
/// </summary>
public interface IUnitWork
{
/// <summary>
/// EF默认情况下每调用一次SaveChanges()都会执行一个单独的事务
/// 本接口实现在一个事务中可以多次执行SaveChanges()方法
/// </summary>
void ExecuteWithTransaction(Action action);
/// <summary>
/// 返回DbContext,用于多线程等极端情况
/// </summary>
/// <returns></returns>
OpenAuthDBContext GetDbContext();
T FindSingle<T>(Expression<Func<T, bool>> exp = null) where T:class;
bool IsExist<T>(Expression<Func<T, bool>> exp) where T:class;
/// <summary>
/// 返回一个单独的实体如果记录多于1个则取第一个
/// </summary>
T FirstOrDefault<T>(Expression<Func<T, bool>> exp = null) where T:class;
/// <summary>
/// 判断指定条件的记录是否存在
/// </summary>
bool Any<T>(Expression<Func<T, bool>> exp) where T:class;
IQueryable<T> Find<T>(Expression<Func<T, bool>> exp = null) where T:class;
IQueryable<T> Find<T>(int pageindex = 1, int pagesize = 10, string orderby = "",
Expression<Func<T, bool>> exp = null) where T:class;
int GetCount<T>(Expression<Func<T, bool>> exp = null) where T:class;
int Count<T>(Expression<Func<T, bool>> exp = null) where T:class;
/// <summary>
/// 新增对象如果Id为空则会自动创建默认Id
/// </summary>
void Add<T>(T entity) where T:BaseEntity;
/// <summary>
/// 批量新增对象如果对象Id为空则会自动创建默认Id
/// </summary>
void BatchAdd<T>(T[] entities) where T:BaseEntity;
/// <summary>
@@ -49,18 +72,25 @@ namespace OpenAuth.Repository.Interface
/// <summary>
/// 实现按需要只更新部分更新
/// <para>如Update<T>(u =>u.Id==1,u =>new User{Name="ok"}) where T:class;</para>
/// <para>如Update&lt;User&gt;(u =>u.Id==1,u =>new User{Name="ok"})</para>
/// <para>该方法内部自动调用了SaveChanges()需要ExecuteWithTransaction配合才能实现事务控制</para>
/// </summary>
/// <param name="where">更新条件</param>
/// <param name="entity">更新后的实体</param>
void Update<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> entity) where T:class;
/// <summary>
/// 批量删除
/// <para>该方法内部自动调用了SaveChanges()需要ExecuteWithTransaction配合才能实现事务控制</para>
/// </summary>
void Delete<T>(Expression<Func<T, bool>> exp) where T:class;
void Save();
/// <summary>
/// 该方法不支持EF自带的事务,需要ExecuteWithTransaction配合才能实现事务控制,详见http://doc.openauth.me/core/unitwork.html
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
int ExecuteSql(string sql);
/// <summary>
@@ -75,5 +105,15 @@ namespace OpenAuth.Repository.Interface
/// <typeparam name="T"> T为非数据库实体需要在DbContext中增加对应的DbQuery</typeparam>
/// <returns></returns>
IQueryable<T> Query<T>(string sql, params object[] parameters) where T : class;
#region
Task<int> ExecuteSqlRawAsync(string sql);
Task<int> SaveAsync();
Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null) where T : class;
Task<bool> AnyAsync<T>(Expression<Func<T, bool>> exp) where T : class;
Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp) where T : class;
#endregion
}
}

View File

@@ -8,6 +8,10 @@ using OpenAuth.Repository.Interface;
namespace OpenAuth.Repository.Test
{
/// <summary>
/// Repository测试基类
/// 测试用于测试DbContext、UnitWork、Repository如果需要测试业务逻辑请使用OpenAuth.App里面的单元测试
/// </summary>
public class TestBase
{
protected AutofacServiceProvider _autofacServiceProvider;
@@ -19,6 +23,7 @@ namespace OpenAuth.Repository.Test
serviceCollection.AddMemoryCache();
serviceCollection.AddOptions();
serviceCollection.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>));
serviceCollection.AddScoped(typeof(IUnitWork), typeof(UnitWork));
serviceCollection.AddDbContext<OpenAuthDBContext>(options =>
options.UseSqlServer("Data Source=.;Initial Catalog=OpenAuthDB;User=sa;Password=000000;Integrated Security=True"));
@@ -28,7 +33,6 @@ namespace OpenAuth.Repository.Test
//注册repository层
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly());
// builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>)).PropertiesAutowired();
builder.Populate(serviceCollection);
var _container = builder.Build();

View File

@@ -94,24 +94,24 @@ namespace OpenAuth.Repository.Test
Id = id
});
var user = repository.FindSingle(u => u.Id == id);
var user = repository.FirstOrDefault(u => u.Id == id);
Assert.NotNull(user);
//修改一个实体
account = "newuser_" + DateTime.Now.ToString("yyyy_MM_dd HH:mm:ss");
user.Account = account;
repository.Update(user);
var newuser = repository.FindSingle(u => u.Account == account);
var newuser = repository.FirstOrDefault(u => u.Account == account);
Assert.NotNull(newuser);
//批量修改
repository.Update(u => u.Id == id, u =>new User{ Name = account});
newuser = repository.FindSingle(u => u.Name == account);
newuser = repository.FirstOrDefault(u => u.Name == account);
Assert.NotNull(newuser);
//删除
repository.Delete(u =>u.Id == id);
newuser = repository.FindSingle(u => u.Id == id);
newuser = repository.FirstOrDefault(u => u.Id == id);
Assert.IsNull(newuser);
}
}

View File

@@ -0,0 +1,86 @@
using System;
using NUnit.Framework;
using Microsoft.Extensions.DependencyInjection;
using OpenAuth.Repository.Domain;
using OpenAuth.Repository.Interface;
namespace OpenAuth.Repository.Test
{
/// <summary>
/// 测试事务
/// </summary>
class TestTransaction : TestBase
{
/// <summary>
/// 测试事务正常提交
/// </summary>
[Test]
public void NormalSubmit()
{
var unitWork = _autofacServiceProvider.GetService<IUnitWork>();
unitWork.ExecuteWithTransaction(() =>
{
var account = "user_" + DateTime.Now.ToString("yyyy_MM_dd HH:mm:ss");
AddAndUpdate(account, unitWork);
});
}
/// <summary>
/// 测试事务回滚
/// </summary>
[Test]
public void SubmitWithRollback()
{
var unitWork = _autofacServiceProvider.GetService<IUnitWork>();
var account = "user_" + DateTime.Now.ToString("yyyy_MM_dd HH:mm:ss");
try
{
unitWork.ExecuteWithTransaction(() =>
{
AddAndUpdate(account, unitWork);
throw new Exception("模拟异常");
});
}
catch (Exception e)
{
Console.WriteLine(e);
}
//如果没有插入成功,表示事务发生了回滚
Assert.IsFalse(unitWork.Any<User>( u=>u.Id == account));
}
/// <summary>
/// 测试添加单个修改Z.EntityFramework.Plus条件修改
/// </summary>
private void AddAndUpdate(string account, IUnitWork unitWork)
{
var user = new User
{
Id = account,
Account = account,
Name = account,
};
unitWork.Add(user);
unitWork.Save();
user.Account = "Trans_" + user.Account;
user.Name = "Trans_" + user.Name;
unitWork.Update(user);
unitWork.Save();
unitWork.Update<User>(u => u.Id == account, u => new User
{
Account = "Trans2_" + user.Account
});
}
}
}

View File

@@ -2,8 +2,10 @@
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using OpenAuth.Repository.Core;
using OpenAuth.Repository.Interface;
using Z.EntityFramework.Plus;
@@ -18,6 +20,31 @@ namespace OpenAuth.Repository
{
_context = context;
}
/// <summary>
/// EF默认情况下每调用一次SaveChanges()都会执行一个单独的事务
/// 本接口实现在一个事务中可以多次执行SaveChanges()方法
/// </summary>
public void ExecuteWithTransaction(Action action)
{
using (IDbContextTransaction transaction = _context.Database.BeginTransaction())
{
try
{
action();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw ex;
}
}
}
/// <summary>
/// 返回DbContext,用于多线程等极端情况
/// </summary>
public OpenAuthDBContext GetDbContext()
{
return _context;
@@ -32,7 +59,7 @@ namespace OpenAuth.Repository
return Filter(exp);
}
public bool IsExist<T>(Expression<Func<T, bool>> exp) where T : class
public bool Any<T>(Expression<Func<T, bool>> exp) where T : class
{
return _context.Set<T>().Any(exp);
}
@@ -40,7 +67,7 @@ namespace OpenAuth.Repository
/// <summary>
/// 查找单个
/// </summary>
public T FindSingle<T>(Expression<Func<T, bool>> exp) where T:class
public T FirstOrDefault<T>(Expression<Func<T, bool>> exp) where T:class
{
return _context.Set<T>().AsNoTracking().FirstOrDefault(exp);
}
@@ -63,11 +90,14 @@ namespace OpenAuth.Repository
/// <summary>
/// 根据过滤条件获取记录数
/// </summary>
public int GetCount<T>(Expression<Func<T, bool>> exp = null) where T : class
public int Count<T>(Expression<Func<T, bool>> exp = null) where T : class
{
return Filter(exp).Count();
}
/// <summary>
/// 新增对象如果Id为空则会自动创建默认Id
/// </summary>
public void Add<T>(T entity) where T : BaseEntity
{
if (entity.KeyIsNull())
@@ -78,9 +108,8 @@ namespace OpenAuth.Repository
}
/// <summary>
/// 批量添加
/// 批量新增对象如果对象Id为空则会自动创建默认Id
/// </summary>
/// <param name="entities">The entities.</param>
public void BatchAdd<T>(T[] entities) where T : BaseEntity
{
foreach (var entity in entities)
@@ -113,18 +142,23 @@ namespace OpenAuth.Repository
/// <summary>
/// 实现按需要只更新部分更新
/// <para>如Update(u =>u.Id==1,u =>new User{Name="ok"});</para>
/// <para>如Update&lt;User&gt;(u =>u.Id==1,u =>new User{Name="ok"})</para>
/// <para>该方法内部自动调用了SaveChanges()需要ExecuteWithTransaction配合才能实现事务控制</para>
/// </summary>
/// <param name="where">The where.</param>
/// <param name="entity">The entity.</param>
/// <param name="where">更新条件</param>
/// <param name="entity">更新后的实体</param>
public void Update<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> entity) where T:class
{
_context.Set<T>().Where(where).Update(entity);
}
/// <summary>
/// 批量删除
/// <para>该方法内部自动调用了SaveChanges()需要ExecuteWithTransaction配合才能实现事务控制</para>
/// </summary>
public virtual void Delete<T>(Expression<Func<T, bool>> exp) where T : class
{
_context.Set<T>().RemoveRange(Filter(exp));
_context.Set<T>().Where(exp).Delete();
}
public void Save()
@@ -178,5 +212,74 @@ namespace OpenAuth.Repository
{
return _context.Query<T>().FromSqlRaw(sql, parameters);
}
#region
/// <summary>
/// 异步执行sql
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public async Task<int> ExecuteSqlRawAsync(string sql)
{
return await _context.Database.ExecuteSqlRawAsync(sql);
}
/// <summary>
/// 异步保存
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<int> SaveAsync()
{
try
{
var entities = _context.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added
|| e.State == EntityState.Modified)
.Select(e => e.Entity);
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext, validateAllProperties: true);
}
return await _context.SaveChangesAsync();
}
catch (ValidationException exc)
{
Console.WriteLine($"{nameof(Save)} validation exception: {exc?.Message}");
throw (exc.InnerException as Exception ?? exc);
}
catch (Exception ex) //DbUpdateException
{
throw (ex.InnerException as Exception ?? ex);
}
}
/// <summary>
/// 根据过滤条件获取记录数
/// </summary>
public async Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null) where T : class
{
return await Filter(exp).CountAsync();
}
public async Task<bool> AnyAsync<T>(Expression<Func<T, bool>> exp) where T : class
{
return await _context.Set<T>().AnyAsync(exp);
}
/// <summary>
/// 查找单个,且不被上下文所跟踪
/// </summary>
public async Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp) where T : class
{
return await _context.Set<T>().AsNoTracking().FirstOrDefaultAsync(exp);
}
#endregion
}
}