From 33cb3e2dae5174c6834a387f4c514e59c29b07d5 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Sun, 29 Sep 2019 15:02:08 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20DbContext/Repository?= =?UTF-8?q?=20ManyToMany=E8=81=94=E7=BA=A7=E4=BF=9D=E5=AD=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=88=E4=B9=8B=E5=89=8D=E5=B7=B2=E6=94=AF=E6=8C=81?= =?UTF-8?q?OneToMany=EF=BC=89=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.DbContext/DbContext/DbContext.cs | 5 +- FreeSql.DbContext/DbContext/DbContextAsync.cs | 5 +- .../DbContext/DbContextOptions.cs | 11 +- FreeSql.DbContext/DbContext/DbContextSync.cs | 5 +- FreeSql.DbContext/DbSet/DbSet.cs | 7 +- FreeSql.DbContext/DbSet/DbSetAsync.cs | 140 +++++++++++--- FreeSql.DbContext/DbSet/DbSetSync.cs | 116 ++++++++++-- FreeSql.DbContext/FreeSql.DbContext.xml | 22 ++- .../Repository/Repository/BaseRepository.cs | 1 + .../Repository/Repository/IBaseRepository.cs | 5 + .../RepositoryTests.cs | 176 ++++++++++++++++++ 11 files changed, 435 insertions(+), 58 deletions(-) diff --git a/FreeSql.DbContext/DbContext/DbContext.cs b/FreeSql.DbContext/DbContext/DbContext.cs index b5ab0cee..bce37fb8 100644 --- a/FreeSql.DbContext/DbContext/DbContext.cs +++ b/FreeSql.DbContext/DbContext/DbContext.cs @@ -148,14 +148,15 @@ namespace FreeSql public ExecCommandInfoType actionType { get; set; } public IDbSet dbSet { get; set; } public Type stateType { get; set; } + public Type entityType { get; set; } public object state { get; set; } } internal enum ExecCommandInfoType { Insert, Update, Delete } Queue _actions = new Queue(); internal int _affrows = 0; - internal void EnqueueAction(ExecCommandInfoType actionType, IDbSet dbSet, Type stateType, object state) => - _actions.Enqueue(new ExecCommandInfo { actionType = actionType, dbSet = dbSet, stateType = stateType, state = state }); + internal void EnqueueAction(ExecCommandInfoType actionType, IDbSet dbSet, Type stateType, Type entityType, object state) => + _actions.Enqueue(new ExecCommandInfo { actionType = actionType, dbSet = dbSet, stateType = stateType, entityType = entityType, state = state }); #endregion ~DbContext() => this.Dispose(); diff --git a/FreeSql.DbContext/DbContext/DbContextAsync.cs b/FreeSql.DbContext/DbContext/DbContextAsync.cs index 698a1200..cf4bf849 100644 --- a/FreeSql.DbContext/DbContext/DbContextAsync.cs +++ b/FreeSql.DbContext/DbContext/DbContextAsync.cs @@ -97,10 +97,11 @@ namespace FreeSql if (_actions.Any() == false && states.Any() || info != null && oldinfo.actionType != info.actionType || - info != null && oldinfo.stateType != info.stateType) + info != null && oldinfo.stateType != info.stateType || + info != null && oldinfo.entityType != info.entityType) { - if (info != null && oldinfo.actionType == info.actionType && oldinfo.stateType == info.stateType) + if (info != null && oldinfo.actionType == info.actionType && oldinfo.stateType == info.stateType && oldinfo.entityType == info.entityType) { //最后一个,合起来发送 states.Add(info.state); diff --git a/FreeSql.DbContext/DbContext/DbContextOptions.cs b/FreeSql.DbContext/DbContext/DbContextOptions.cs index 9ff21d0c..4841ded3 100644 --- a/FreeSql.DbContext/DbContext/DbContextOptions.cs +++ b/FreeSql.DbContext/DbContext/DbContextOptions.cs @@ -5,7 +5,16 @@ namespace FreeSql { /// - /// 是否开启一对多,联级保存功能 + /// 是否开启一对多,多对多联级保存功能 + /// + /// 【一对多】模型下, 保存时可联级保存实体的属性集合。出于使用安全考虑我们没做完整对比,只实现实体属性集合的添加或更新操作,所以不会删除实体属性集合的数据。 + /// 完整对比的功能使用起来太危险,试想下面的场景: + /// - 保存的时候,实体的属性集合是空的,如何操作?记录全部删除? + /// - 保存的时候,由于数据库中记录非常之多,那么只想保存子表的部分数据,或者只需要添加,如何操作? + /// + /// 【多对多】模型下,我们对中间表的保存是完整对比操作,对外部实体的操作只作新增(注意不会更新) + /// - 属性集合为空时,删除他们的所有关联数据(中间表) + /// - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录 /// public bool EnableAddOrUpdateNavigateList { get; set; } = true; } diff --git a/FreeSql.DbContext/DbContext/DbContextSync.cs b/FreeSql.DbContext/DbContext/DbContextSync.cs index 4c8f2c3b..3de59884 100644 --- a/FreeSql.DbContext/DbContext/DbContextSync.cs +++ b/FreeSql.DbContext/DbContext/DbContextSync.cs @@ -97,10 +97,11 @@ namespace FreeSql if (_actions.Any() == false && states.Any() || info != null && oldinfo.actionType != info.actionType || - info != null && oldinfo.stateType != info.stateType) + info != null && oldinfo.stateType != info.stateType || + info != null && oldinfo.entityType != info.entityType) { - if (info != null && oldinfo.actionType == info.actionType && oldinfo.stateType == info.stateType) + if (info != null && oldinfo.actionType == info.actionType && oldinfo.stateType == info.stateType && oldinfo.entityType == info.entityType) { //最后一个,合起来发送 states.Add(info.state); diff --git a/FreeSql.DbContext/DbSet/DbSet.cs b/FreeSql.DbContext/DbSet/DbSet.cs index fa02eeb2..fbbbaa7c 100644 --- a/FreeSql.DbContext/DbSet/DbSet.cs +++ b/FreeSql.DbContext/DbSet/DbSet.cs @@ -60,7 +60,7 @@ namespace FreeSql protected virtual IDelete OrmDelete(object dywhere) => _db.Orm.Delete().AsType(_entityType).WithTransaction(_uow?.GetOrBeginTransaction()).WhereDynamic(dywhere); internal void EnqueueToDbContext(DbContext.ExecCommandInfoType actionType, EntityState state) => - _db.EnqueueAction(actionType, this, typeof(EntityState), state); + _db.EnqueueAction(actionType, this, typeof(EntityState), _entityType, state); internal void IncrAffrows(int affrows) => _db._affrows += affrows; @@ -117,14 +117,15 @@ namespace FreeSql /// /// /// - public void AsType(Type entityType) + public DbSet AsType(Type entityType) { if (entityType == typeof(object)) throw new Exception("ISelect.AsType 参数不支持指定为 object"); - if (entityType == _entityType) return; + if (entityType == _entityType) return this; var newtb = _db.Orm.CodeFirst.GetTableByEntity(entityType); _entityType = entityType; _tablePriv = newtb ?? throw new Exception("DbSet.AsType 参数错误,请传入正确的实体类型"); _tableIdentitysPriv = null; + return this; } public class EntityState diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 0ae3f8d3..dd385d01 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -4,6 +4,8 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; +using System.Reflection; using System.Threading.Tasks; namespace FreeSql @@ -131,46 +133,132 @@ namespace FreeSql { if (_table.ColumnsByCsIgnore.ContainsKey(prop.Key)) continue; if (_table.ColumnsByCs.ContainsKey(prop.Key)) continue; + var tref = _table.GetTableRef(prop.Key, true); if (tref == null) continue; - switch (tref.RefType) { case Internal.Model.TableRefType.OneToOne: case Internal.Model.TableRefType.ManyToOne: - case Internal.Model.TableRefType.ManyToMany: continue; - case Internal.Model.TableRefType.OneToMany: - if (itemType == null) itemType = item.GetType(); - if (_table.TypeLazy != null && itemType == _table.TypeLazy) - { - var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary()).GetOrAdd(prop.Key, propName => - _table.TypeLazy.GetField($"__lazy__{propName}", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); - if (lazyField != null) - { - var lazyFieldValue = (bool)lazyField.GetValue(item); - if (lazyFieldValue == false) continue; - } - } - var propVal = prop.Value.GetValue(item); - var propValEach = propVal as IEnumerable; - if (propValEach == null) continue; - object dbset = null; - System.Reflection.MethodInfo dbsetAddOrUpdate = null; + } + + object propVal = null; + if (itemType == null) itemType = item.GetType(); + if (_table.TypeLazy != null && itemType == _table.TypeLazy) + { + var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary()).GetOrAdd(prop.Key, propName => + _table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance)); + if (lazyField != null) + { + var lazyFieldValue = (bool)lazyField.GetValue(item); + if (lazyFieldValue == false) continue; + } + propVal = prop.Value.GetValue(item); + } + else + { + propVal = prop.Value.GetValue(item); + if (propVal == null) continue; + } + + var propValEach = propVal as IEnumerable; + if (propValEach == null) continue; + DbSet refSet = _db.Set().AsType(tref.RefEntityType); + switch (tref.RefType) + { + case Internal.Model.TableRefType.ManyToMany: + var curList = new List(); foreach (var propValItem in propValEach) { - if (dbset == null) + curList.Add(propValItem); + var flagExists = refSet.ExistsInStates(propValItem); + if (flagExists == false) + flagExists = await refSet.Select.WhereDynamic(propValItem).AnyAsync(); + if (refSet.CanAdd(propValItem, false) && flagExists != true) + await refSet.AddAsync(propValItem); + } + var midSelectParam = Expression.Parameter(typeof(object), "a"); + var midWheres = new List>>(); + for (var colidx = 0; colidx < tref.Columns.Count; colidx++) + midWheres.Add(Expression.Lambda>(Expression.Equal( + Expression.MakeMemberAccess(Expression.Convert(midSelectParam, tref.RefMiddleEntityType), tref.MiddleColumns[colidx].Table.Properties[tref.MiddleColumns[colidx].CsName]), + Expression.Constant( + FreeSql.Internal.Utils.GetDataReaderValue( + tref.MiddleColumns[colidx].CsType, + _db.Orm.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName)), tref.MiddleColumns[colidx].CsType) + ), midSelectParam)); + + if (curList.Any() == false) //全部删除 + { + var delall = _db.Orm.Delete().AsType(tref.RefMiddleEntityType); + foreach (var midWhere in midWheres) delall.Where(midWhere); + await delall.ExecuteAffrowsAsync(); + } + else //保存 + { + var midSet = _db.Set().AsType(tref.RefMiddleEntityType); + var midSelect = midSet.Select; + foreach (var midWhere in midWheres) midSelect.Where(midWhere); + var midList = await midSelect.ToListAsync(); + var midListDel = new List(); + var midListAdd = new List(); + + foreach (var midItem in midList) { - dbset = _db.Set(tref.RefEntityType); - dbsetAddOrUpdate = dbset.GetType().GetMethod("AddOrUpdateAsync", new Type[] { tref.RefEntityType }); + var curContains = new List(); + for (var curIdx = 0; curIdx < curList.Count; curIdx++) + { + var isEquals = true; + for (var midcolidx = tref.Columns.Count; midcolidx < tref.MiddleColumns.Count; midcolidx++) + { + var refcol = tref.Columns[midcolidx - tref.Columns.Count]; + var midval = FreeSql.Internal.Utils.GetDataReaderValue(refcol.CsType, _db.Orm.GetEntityValueWithPropertyName(tref.RefMiddleEntityType, midItem, tref.MiddleColumns[midcolidx].CsName)); + var refval = FreeSql.Internal.Utils.GetDataReaderValue(refcol.CsType, _db.Orm.GetEntityValueWithPropertyName(tref.RefEntityType, curList[curIdx], tref.Columns[midcolidx - tref.Columns.Count].CsName)); + if (object.Equals(midval, refval) == false) + { + isEquals = false; + break; + } + } + if (isEquals) + curContains.Add(curIdx); + } + if (curContains.Any()) + foreach (var curIdx in curContains) + curList.RemoveAt(curIdx); + else + midListDel.Add(midItem); } + midSet.RemoveRange(midListDel); //删除未保存的项 + foreach (var curItem in curList) + { + var newItem = Activator.CreateInstance(tref.RefMiddleEntityType); + for (var colidx = 0; colidx < tref.Columns.Count; colidx++) + { + var val = FreeSql.Internal.Utils.GetDataReaderValue(tref.MiddleColumns[colidx].CsType, _db.Orm.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName)); + _db.Orm.SetEntityValueWithPropertyName(tref.RefMiddleEntityType, newItem, tref.MiddleColumns[colidx].CsName, val); + } + for (var midcolidx = tref.Columns.Count; midcolidx < tref.MiddleColumns.Count; midcolidx++) + { + var refcol = tref.Columns[midcolidx - tref.Columns.Count]; + var refval = FreeSql.Internal.Utils.GetDataReaderValue(tref.MiddleColumns[midcolidx].CsType, _db.Orm.GetEntityValueWithPropertyName(tref.RefEntityType, curItem, refcol.CsName)); + _db.Orm.SetEntityValueWithPropertyName(tref.RefMiddleEntityType, newItem, tref.MiddleColumns[midcolidx].CsName, refval); + } + midListAdd.Add(newItem); + } + await midSet.AddRangeAsync(midListAdd); + } + break; + case Internal.Model.TableRefType.OneToMany: + foreach (var propValItem in propValEach) + { for (var colidx = 0; colidx < tref.Columns.Count; colidx++) { - tref.RefColumns[colidx].Table.Properties[tref.RefColumns[colidx].CsName] - .SetValue(propValItem, tref.Columns[colidx].Table.Properties[tref.Columns[colidx].CsName].GetValue(item)); + var val = FreeSql.Internal.Utils.GetDataReaderValue(tref.RefColumns[colidx].CsType, _db.Orm.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName)); + _db.Orm.SetEntityValueWithPropertyName(tref.RefEntityType, propValItem, tref.RefColumns[colidx].CsName, val); } - Task task = dbsetAddOrUpdate.Invoke(dbset, new object[] { propValItem }) as Task; - await task; + await refSet.AddOrUpdateAsync(propValItem); } break; } diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 7cb270b5..8e0a7729 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; using System.Reflection; +using System.Text; +using System.Linq.Expressions; namespace FreeSql { @@ -137,8 +139,16 @@ namespace FreeSql if (_table.ColumnsByCsIgnore.ContainsKey(prop.Key)) continue; if (_table.ColumnsByCs.ContainsKey(prop.Key)) continue; - object propVal = null; + var tref = _table.GetTableRef(prop.Key, true); + if (tref == null) continue; + switch (tref.RefType) + { + case Internal.Model.TableRefType.OneToOne: + case Internal.Model.TableRefType.ManyToOne: + continue; + } + object propVal = null; if (itemType == null) itemType = item.GetType(); if (_table.TypeLazy != null && itemType == _table.TypeLazy) { @@ -157,33 +167,103 @@ namespace FreeSql if (propVal == null) continue; } - var tref = _table.GetTableRef(prop.Key, true); - if (tref == null) continue; - + var propValEach = propVal as IEnumerable; + if (propValEach == null) continue; + DbSet refSet = _db.Set().AsType(tref.RefEntityType); switch (tref.RefType) { - case Internal.Model.TableRefType.OneToOne: - case Internal.Model.TableRefType.ManyToOne: case Internal.Model.TableRefType.ManyToMany: - continue; - case Internal.Model.TableRefType.OneToMany: - var propValEach = propVal as IEnumerable; - if (propValEach == null) continue; - object dbset = null; - MethodInfo dbsetAddOrUpdate = null; + var curList = new List(); foreach (var propValItem in propValEach) { - if (dbset == null) + curList.Add(propValItem); + var flagExists = refSet.ExistsInStates(propValItem); + if (flagExists == false) + flagExists = refSet.Select.WhereDynamic(propValItem).Any(); + if (refSet.CanAdd(propValItem, false) && flagExists != true) + refSet.Add(propValItem); + } + var midSelectParam = Expression.Parameter(typeof(object), "a"); + var midWheres = new List>>(); + for (var colidx = 0; colidx < tref.Columns.Count; colidx++) + midWheres.Add(Expression.Lambda>(Expression.Equal( + Expression.MakeMemberAccess(Expression.Convert(midSelectParam, tref.RefMiddleEntityType), tref.MiddleColumns[colidx].Table.Properties[tref.MiddleColumns[colidx].CsName]), + Expression.Constant( + FreeSql.Internal.Utils.GetDataReaderValue( + tref.MiddleColumns[colidx].CsType, + _db.Orm.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName)), tref.MiddleColumns[colidx].CsType) + ), midSelectParam)); + + if (curList.Any() == false) //全部删除 + { + var delall = _db.Orm.Delete().AsType(tref.RefMiddleEntityType); + foreach (var midWhere in midWheres) delall.Where(midWhere); + delall.ExecuteAffrows(); + } + else //保存 + { + var midSet = _db.Set().AsType(tref.RefMiddleEntityType); + var midSelect = midSet.Select; + foreach (var midWhere in midWheres) midSelect.Where(midWhere); + var midList = midSelect.ToList(); + var midListDel = new List(); + var midListAdd = new List(); + + foreach (var midItem in midList) { - dbset = _db.Set(tref.RefEntityType); - dbsetAddOrUpdate = dbset.GetType().GetMethod("AddOrUpdate", new Type[] { tref.RefEntityType }); + var curContains = new List(); + for(var curIdx = 0; curIdx < curList.Count; curIdx ++) + { + var isEquals = true; + for (var midcolidx = tref.Columns.Count; midcolidx < tref.MiddleColumns.Count; midcolidx++) + { + var refcol = tref.Columns[midcolidx - tref.Columns.Count]; + var midval = FreeSql.Internal.Utils.GetDataReaderValue(refcol.CsType, _db.Orm.GetEntityValueWithPropertyName(tref.RefMiddleEntityType, midItem, tref.MiddleColumns[midcolidx].CsName)); + var refval = FreeSql.Internal.Utils.GetDataReaderValue(refcol.CsType, _db.Orm.GetEntityValueWithPropertyName(tref.RefEntityType, curList[curIdx], tref.Columns[midcolidx - tref.Columns.Count].CsName)); + if (object.Equals(midval, refval) == false) + { + isEquals = false; + break; + } + } + if (isEquals) + curContains.Add(curIdx); + } + if (curContains.Any()) + foreach (var curIdx in curContains) + curList.RemoveAt(curIdx); + else + midListDel.Add(midItem); } + midSet.RemoveRange(midListDel); //删除未保存的项 + foreach (var curItem in curList) + { + var newItem = Activator.CreateInstance(tref.RefMiddleEntityType); + for (var colidx = 0; colidx < tref.Columns.Count; colidx++) + { + var val = FreeSql.Internal.Utils.GetDataReaderValue(tref.MiddleColumns[colidx].CsType, _db.Orm.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName)); + _db.Orm.SetEntityValueWithPropertyName(tref.RefMiddleEntityType, newItem, tref.MiddleColumns[colidx].CsName, val); + } + for (var midcolidx = tref.Columns.Count; midcolidx < tref.MiddleColumns.Count; midcolidx++) + { + var refcol = tref.Columns[midcolidx - tref.Columns.Count]; + var refval = FreeSql.Internal.Utils.GetDataReaderValue(tref.MiddleColumns[midcolidx].CsType, _db.Orm.GetEntityValueWithPropertyName(tref.RefEntityType, curItem, refcol.CsName)); + _db.Orm.SetEntityValueWithPropertyName(tref.RefMiddleEntityType, newItem, tref.MiddleColumns[midcolidx].CsName, refval); + } + midListAdd.Add(newItem); + } + midSet.AddRange(midListAdd); + } + break; + case Internal.Model.TableRefType.OneToMany: + foreach (var propValItem in propValEach) + { for (var colidx = 0; colidx < tref.Columns.Count; colidx++) { - tref.RefColumns[colidx].Table.Properties[tref.RefColumns[colidx].CsName] - .SetValue(propValItem, tref.Columns[colidx].Table.Properties[tref.Columns[colidx].CsName].GetValue(item)); + var val = FreeSql.Internal.Utils.GetDataReaderValue(tref.RefColumns[colidx].CsType, _db.Orm.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName)); + _db.Orm.SetEntityValueWithPropertyName(tref.RefEntityType, propValItem, tref.RefColumns[colidx].CsName, val); } - dbsetAddOrUpdate.Invoke(dbset, new object[] { propValItem }); + refSet.AddOrUpdate(propValItem); } break; } diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 42dd9adc..755141c4 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -48,7 +48,16 @@ - 是否开启一对多,联级保存功能 + 是否开启一对多,多对多联级保存功能 + + 【一对多】模型下, 保存时可联级保存实体的属性集合。出于使用安全考虑我们没做完整对比,只实现实体属性集合的添加或更新操作,所以不会删除实体属性集合的数据。 + 完整对比的功能使用起来太危险,试想下面的场景: + - 保存的时候,实体的属性集合是空的,如何操作?记录全部删除? + - 保存的时候,由于数据库中记录非常之多,那么只想保存子表的部分数据,或者只需要添加,如何操作? + + 【多对多】模型下,我们对中间表的保存是完整对比操作,对外部实体的操作只作新增(注意不会更新) + - 属性集合为空时,删除他们的所有关联数据(中间表) + - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录 @@ -158,6 +167,11 @@ + + + 设置 DbContext 选项 + + 清空状态数据 @@ -193,14 +207,14 @@ 开启工作单元 - + 创建普通数据上下文档对象 - + 不跟踪查询的实体数据(在不需要更新其数据时使用),可提长查询性能 @@ -208,7 +222,7 @@ - + 设置 DbContext 选项设置 diff --git a/FreeSql.DbContext/Repository/Repository/BaseRepository.cs b/FreeSql.DbContext/Repository/Repository/BaseRepository.cs index 2fbcc0cc..f7775429 100644 --- a/FreeSql.DbContext/Repository/Repository/BaseRepository.cs +++ b/FreeSql.DbContext/Repository/Repository/BaseRepository.cs @@ -59,6 +59,7 @@ namespace FreeSql } public Type EntityType => _dbsetPriv?.EntityType ?? typeof(TEntity); public void AsType(Type entityType) => _dbset.AsType(entityType); + public DbContextOptions DbContextOptions { get => _db.Options; set => _db.Options = value; } public IFreeSql Orm { get; private set; } public IUnitOfWork UnitOfWork { get; set; } diff --git a/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs b/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs index 432697d8..effe252b 100644 --- a/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs +++ b/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs @@ -17,6 +17,11 @@ namespace FreeSql /// /// void AsType(Type entityType); + + /// + /// 设置 DbContext 选项 + /// + DbContextOptions DbContextOptions { get; set; } } public interface IBaseRepository : IReadOnlyRepository, IBasicRepository diff --git a/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs b/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs index 95319a9c..c16fda68 100644 --- a/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs +++ b/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs @@ -1,4 +1,6 @@ +using FreeSql.DataAnnotations; using System; +using System.Collections.Generic; using Xunit; namespace FreeSql.Tests @@ -277,5 +279,179 @@ namespace FreeSql.Tests repos.DataFilter.Apply("xxx", a => (a as AddUpdateInfo).Clicks == 2); Assert.Null(repos.Find(item.Id)); } + + [Fact] + public void EnableAddOrUpdateNavigateList_OneToMany() + { + var repo = g.sqlite.GetRepository(); + var cts = new[] { + new Cagetory + { + Name = "1", + Goodss = new List(new[] + { + new Goods { Name = "Ʒ1" }, + new Goods { Name = "Ʒ2" }, + new Goods { Name = "Ʒ3" } + }) + }, + new Cagetory + { + Name = "2", + Goodss = new List(new[] + { + new Goods { Name = "Ʒ4" }, + new Goods { Name = "Ʒ5" } + }) + } + }; + repo.Insert(cts); + cts[0].Name = "11"; + cts[0].Goodss.Clear(); + cts[1].Name = "22"; + cts[1].Goodss.Clear(); + repo.Update(cts); + cts[0].Name = "111"; + cts[0].Goodss.Clear(); + cts[0].Goodss.Add(new Goods { Name = "Ʒ33" }); + cts[1].Name = "222"; + cts[1].Goodss.Clear(); + cts[1].Goodss.Add(new Goods { Name = "Ʒ55" }); + repo.Update(cts); + } + [Table(Name = "EAUNL_OTM_CT")] + class Cagetory + { + public Guid Id { get; set; } + public string Name { get; set; } + + [Navigate("CagetoryId")] + public List Goodss { get; set; } + } + [Table(Name = "EAUNL_OTM_GD")] + class Goods + { + public Guid Id { get; set; } + public Guid CagetoryId { get; set; } + public string Name { get; set; } + } + + [Fact] + public void EnableAddOrUpdateNavigateList_OneToMany_Parent() + { + var repo = g.sqlite.GetRepository(); + var cts = new[] { + new CagetoryParent + { + Name = "1", + Childs = new List(new[] + { + new CagetoryParent { Name = "1_1" }, + new CagetoryParent { Name = "1_2" }, + new CagetoryParent { Name = "1_3" } + }) + }, + new CagetoryParent + { + Name = "2", + Childs = new List(new[] + { + new CagetoryParent { Name = "2_1" }, + new CagetoryParent { Name = "2_2" } + }) + } + }; + repo.Insert(cts); + cts[0].Name = "11"; + cts[0].Childs.Clear(); + cts[1].Name = "22"; + cts[1].Childs.Clear(); + repo.Update(cts); + cts[0].Name = "111"; + cts[0].Childs.Clear(); + cts[0].Childs.Add(new CagetoryParent { Name = "1_33" }); + cts[1].Name = "222"; + cts[1].Childs.Clear(); + cts[1].Childs.Add(new CagetoryParent { Name = "2_22" }); + repo.Update(cts); + } + [Table(Name = "EAUNL_OTMP_CT")] + class CagetoryParent + { + public Guid Id { get; set; } + public string Name { get; set; } + + public Guid ParentId { get; set; } + [Navigate("ParentId")] + public List Childs { get; set; } + } + + [Fact] + public void EnableAddOrUpdateNavigateList_ManyToMany() + { + var tags = new[] { + new Tag { TagName = "" }, + new Tag { TagName = "80" }, + new Tag { TagName = "00" }, + new Tag { TagName = "ҡ" } + }; + var ss = new[] + { + new Song + { + Name = "һ.mp3", + Tags = new List(new[] + { + tags[0], tags[1] + }) + }, + new Song + { + Name = ".mp3", + Tags = new List(new[] + { + tags[0], tags[2] + }) + } + }; + var repo = g.sqlite.GetRepository(); + repo.Insert(ss); + + ss[0].Name = "һ.mp5"; + ss[0].Tags.Clear(); + ss[0].Tags.Add(tags[0]); + ss[1].Name = ".mp5"; + ss[1].Tags.Clear(); + ss[1].Tags.Add(tags[3]); + repo.Update(ss); + + ss[0].Name = "һ.mp4"; + ss[0].Tags.Clear(); + ss[1].Name = ".mp4"; + ss[1].Tags.Clear(); + repo.Update(ss); + } + [Table(Name = "EAUNL_MTM_SONG")] + class Song + { + public Guid Id { get; set; } + public string Name { get; set; } + public List Tags { get; set; } + } + [Table(Name = "EAUNL_MTM_TAG")] + class Tag + { + public Guid Id { get; set; } + public string TagName { get; set; } + public List Songs { get; set; } + } + [Table(Name = "EAUNL_MTM_SONGTAG")] + class SongTag + { + public Guid SongId { get; set; } + public Song Song { get; set; } + public Guid TagId { get; set; } + public Tag Tag { get; set; } + } } }