mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
v3.2.668 #1237
This commit is contained in:
parent
090abfd36a
commit
2f790cce90
@ -11,7 +11,7 @@
|
|||||||
<!--
|
<!--
|
||||||
经常出于版本交叉问题,暂时关闭,在每个项目上设置版本号
|
经常出于版本交叉问题,暂时关闭,在每个项目上设置版本号
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
@ -31,7 +31,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\FreeSql\FreeSql.DbContext.csproj" />
|
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(TargetFramework)' == 'net40'">
|
||||||
|
<DefineConstants>net40</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,21 +1,52 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<doc>
|
<doc>
|
||||||
<assembly>
|
<assembly>
|
||||||
<name>FreeSql.Extensions.JsonMap</name>
|
<name>FreeSql.Extensions.AggregateRoot</name>
|
||||||
</assembly>
|
</assembly>
|
||||||
<members>
|
<members>
|
||||||
<member name="T:FreeSql.DataAnnotations.JsonMapAttribute">
|
<member name="T:FreeSql.DataAnnotations.AggregateRootBoundaryAttribute">
|
||||||
<summary>
|
<summary>
|
||||||
When the entity class property is <see cref="T:System.Object"/>, map storage in JSON format. <br />
|
设置 AggregateRootRepository 边界范围<para></para>
|
||||||
当实体类属性为【对象】时,以 JSON 形式映射存储
|
在边界范围之内的规则 :<para></para>
|
||||||
|
1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改<para></para>
|
||||||
|
2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略)<para></para>
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:FreeSqlJsonMapCoreExtensions.UseJsonMap(IFreeSql)">
|
<member name="P:FreeSql.DataAnnotations.AggregateRootBoundaryAttribute.Break">
|
||||||
<summary>
|
<summary>
|
||||||
When the entity class property is <see cref="T:System.Object"/> and the attribute is marked as <see cref="T:FreeSql.DataAnnotations.JsonMapAttribute"/>, map storage in JSON format. <br />
|
边界是否终止
|
||||||
当实体类属性为【对象】时,并且标记特性 [JsonMap] 时,该属性将以JSON形式映射存储
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:FreeSql.DataAnnotations.AggregateRootBoundaryAttribute.BreakThen">
|
||||||
|
<summary>
|
||||||
|
边界是否终止向下探测
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:FreeSql.AggregateRootRepository`1.Select">
|
||||||
|
<summary>
|
||||||
|
默认:创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)<para></para>
|
||||||
|
重写:使用
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:FreeSql.AggregateRootRepository`1.SelectDiy">
|
||||||
|
<summary>
|
||||||
|
创建查询对象(纯净)<para></para>
|
||||||
|
_<para></para>
|
||||||
|
聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发<para></para>
|
||||||
|
string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order))
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:FreeSql.AggregateRootRepository`1.SelectAggregateRoot">
|
||||||
|
<summary>
|
||||||
|
创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)
|
||||||
</summary>
|
</summary>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:FreeSql.AggregateRootRepository`1.SelectAggregateRootTracking(System.Object)">
|
||||||
|
<summary>
|
||||||
|
ISelect.TrackToList 委托,数据返回后自动 Attach
|
||||||
|
</summary>
|
||||||
|
<param name="list"></param>
|
||||||
|
</member>
|
||||||
</members>
|
</members>
|
||||||
</doc>
|
</doc>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<Title>$(AssemblyName)</Title>
|
<Title>$(AssemblyName)</Title>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||||
<PackageTags>FreeSql DbFirst 实体生成器</PackageTags>
|
<PackageTags>FreeSql DbFirst 实体生成器</PackageTags>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Sqlite", "
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Tests.DbContext2", "FreeSql.Tests\FreeSql.Tests.DbContext2\FreeSql.Tests.DbContext2.csproj", "{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Tests.DbContext2", "FreeSql.Tests\FreeSql.Tests.DbContext2\FreeSql.Tests.DbContext2.csproj", "{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.AggregateRoot", "Extensions\FreeSql.Extensions.AggregateRoot\FreeSql.Extensions.AggregateRoot.csproj", "{B8F84E4F-46F2-4048-B79B-49F0B8A95335}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -39,6 +41,10 @@ Global
|
|||||||
{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Release|Any CPU.Build.0 = Release|Any CPU
|
{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace FreeSql.DataAnnotations
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置 AggregateRootRepository 边界范围<para></para>
|
|
||||||
/// 在边界范围之内的规则 :<para></para>
|
|
||||||
/// 1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改<para></para>
|
|
||||||
/// 2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略)<para></para>
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
|
|
||||||
public class AggregateRootBoundaryAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 边界是否终止
|
|
||||||
/// </summary>
|
|
||||||
public bool Break { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 边界是否终止向下探测
|
|
||||||
/// </summary>
|
|
||||||
public bool BreakThen { get; set; }
|
|
||||||
|
|
||||||
public AggregateRootBoundaryAttribute(string name)
|
|
||||||
{
|
|
||||||
this.Name = name;
|
|
||||||
}
|
|
||||||
public AggregateRootBoundaryAttribute()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace FreeSql.Internal.Model
|
|
||||||
{
|
|
||||||
|
|
||||||
public class AggregateRootTrackingChangeInfo
|
|
||||||
{
|
|
||||||
|
|
||||||
public List<NativeTuple<Type, object>> InsertLog { get; } = new List<NativeTuple<Type, object>>();
|
|
||||||
|
|
||||||
public List<NativeTuple<Type, object, object, List<string>>> UpdateLog { get; } = new List<NativeTuple<Type, object, object, List<string>>>();
|
|
||||||
|
|
||||||
public List<NativeTuple<Type, object[]>> DeleteLog { get; } = new List<NativeTuple<Type, object[]>>();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,261 +0,0 @@
|
|||||||
using FreeSql.Extensions.EntityUtil;
|
|
||||||
using FreeSql.Internal.Model;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace FreeSql
|
|
||||||
{
|
|
||||||
public interface IAggregateRootRepository<TEntity>: IBaseRepository<TEntity> where TEntity : class
|
|
||||||
{
|
|
||||||
IBaseRepository<TEntity> ChangeBoundary(string name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial class AggregateRootRepository<TEntity> : IAggregateRootRepository<TEntity> where TEntity : class
|
|
||||||
{
|
|
||||||
readonly IBaseRepository<TEntity> _repository;
|
|
||||||
public AggregateRootRepository(IFreeSql fsql)
|
|
||||||
{
|
|
||||||
if (fsql == null) throw new ArgumentNullException(nameof(fsql));
|
|
||||||
_repository = fsql.GetRepository<TEntity>();
|
|
||||||
_repository.DbContextOptions.EnableCascadeSave = false;
|
|
||||||
}
|
|
||||||
public AggregateRootRepository(IFreeSql fsql, UnitOfWorkManager uowManager) : this(uowManager?.Orm ?? fsql)
|
|
||||||
{
|
|
||||||
uowManager?.Binding(_repository);
|
|
||||||
}
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
_repository.FlushState();
|
|
||||||
_repository.Dispose();
|
|
||||||
FlushState();
|
|
||||||
}
|
|
||||||
|
|
||||||
string _boundaryName = "";
|
|
||||||
public IBaseRepository<TEntity> ChangeBoundary(string name)
|
|
||||||
{
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
_repository.FlushState();
|
|
||||||
FlushState();
|
|
||||||
_boundaryName = string.Concat(name).Trim();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFreeSql Orm => _repository.Orm;
|
|
||||||
public IUnitOfWork UnitOfWork { get => _repository.UnitOfWork; set => _repository.UnitOfWork = value; }
|
|
||||||
public DbContextOptions DbContextOptions
|
|
||||||
{
|
|
||||||
get => _repository.DbContextOptions;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null) throw new ArgumentNullException(nameof(DbContextOptions));
|
|
||||||
_repository.DbContextOptions = value;
|
|
||||||
_repository.DbContextOptions.EnableCascadeSave = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void AsType(Type entityType) => _repository.AsType(entityType);
|
|
||||||
Func<string, string> _asTableRule;
|
|
||||||
public void AsTable(Func<string, string> rule)
|
|
||||||
{
|
|
||||||
_repository.AsTable(rule);
|
|
||||||
_asTableRule = rule;
|
|
||||||
}
|
|
||||||
public Type EntityType => _repository.EntityType;
|
|
||||||
public IDataFilter<TEntity> DataFilter => _repository.DataFilter;
|
|
||||||
|
|
||||||
public void Attach(TEntity entity)
|
|
||||||
{
|
|
||||||
var state = CreateEntityState(entity);
|
|
||||||
if (_states.ContainsKey(state.Key)) _states[state.Key] = state;
|
|
||||||
else _states.Add(state.Key, state);
|
|
||||||
}
|
|
||||||
public void Attach(IEnumerable<TEntity> entity)
|
|
||||||
{
|
|
||||||
foreach (var item in entity)
|
|
||||||
Attach(item);
|
|
||||||
}
|
|
||||||
public IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data) => _repository.AttachOnlyPrimary(data);
|
|
||||||
public Dictionary<string, object[]> CompareState(TEntity newdata)
|
|
||||||
{
|
|
||||||
if (newdata == null) return null;
|
|
||||||
var _table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
|
||||||
if (_table.Primarys.Any() == false) throw new Exception(DbContextStrings.Incomparable_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, newdata)));
|
|
||||||
var key = Orm.GetEntityKeyString(EntityType, newdata, false);
|
|
||||||
if (string.IsNullOrEmpty(key)) throw new Exception(DbContextStrings.Incomparable_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, newdata)));
|
|
||||||
if (_states.TryGetValue(key, out var oldState) == false || oldState == null) throw new Exception($"不可对比,数据未被跟踪:{Orm.GetEntityString(EntityType, newdata)}");
|
|
||||||
AggregateRootTrackingChangeInfo tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, oldState, newdata, null, tracking);
|
|
||||||
return new Dictionary<string, object[]>
|
|
||||||
{
|
|
||||||
["Insert"] = tracking.InsertLog.Select(a => new object[] { a.Item1, a.Item2 }).ToArray(),
|
|
||||||
["Delete"] = tracking.DeleteLog.Select(a => new object[] { a.Item1, a.Item2 }).ToArray(),
|
|
||||||
["Update"] = tracking.UpdateLog.Select(a => new object[] { a.Item1, a.Item2, a.Item3, a.Item4 }).ToArray(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public void FlushState()
|
|
||||||
{
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
_repository.FlushState();
|
|
||||||
_states.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IUpdate<TEntity> UpdateDiy => _repository.UpdateDiy;
|
|
||||||
public ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => Select.Where(exp);
|
|
||||||
public ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => Select.WhereIf(condition, exp);
|
|
||||||
|
|
||||||
readonly Dictionary<Type, IBaseRepository<object>> _childRepositorys = new Dictionary<Type, IBaseRepository<object>>();
|
|
||||||
IBaseRepository<object> GetChildRepository(Type type)
|
|
||||||
{
|
|
||||||
if (_childRepositorys.TryGetValue(type, out var repo) == false)
|
|
||||||
{
|
|
||||||
repo = Orm.GetRepository<object>();
|
|
||||||
repo.AsType(type);
|
|
||||||
_childRepositorys.Add(type, repo);
|
|
||||||
}
|
|
||||||
repo.UnitOfWork = UnitOfWork;
|
|
||||||
repo.DbContextOptions = DbContextOptions;
|
|
||||||
repo.DbContextOptions.EnableCascadeSave = false;
|
|
||||||
repo.AsTable(_asTableRule);
|
|
||||||
return repo;
|
|
||||||
}
|
|
||||||
void DisposeChildRepositorys()
|
|
||||||
{
|
|
||||||
foreach (var repo in _childRepositorys.Values)
|
|
||||||
{
|
|
||||||
repo.FlushState();
|
|
||||||
repo.Dispose();
|
|
||||||
}
|
|
||||||
_childRepositorys.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 状态管理
|
|
||||||
protected Dictionary<string, EntityState> _states = new Dictionary<string, EntityState>();
|
|
||||||
protected class EntityState
|
|
||||||
{
|
|
||||||
public EntityState(TEntity value, string key)
|
|
||||||
{
|
|
||||||
this.Value = value;
|
|
||||||
this.Key = key;
|
|
||||||
this.Time = DateTime.Now;
|
|
||||||
}
|
|
||||||
public TEntity OldValue { get; set; }
|
|
||||||
public TEntity Value { get; set; }
|
|
||||||
public string Key { get; set; }
|
|
||||||
public DateTime Time { get; set; }
|
|
||||||
}
|
|
||||||
EntityState CreateEntityState(TEntity data)
|
|
||||||
{
|
|
||||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
|
||||||
var key = Orm.GetEntityKeyString(EntityType, data, false);
|
|
||||||
var state = new EntityState((TEntity)EntityType.CreateInstanceGetDefaultValue(), key);
|
|
||||||
AggregateRootUtils.MapEntityValue(_boundaryName, Orm, EntityType, data, state.Value);
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
bool? ExistsInStates(object data)
|
|
||||||
{
|
|
||||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
|
||||||
var key = Orm.GetEntityKeyString(EntityType, data, false);
|
|
||||||
if (string.IsNullOrEmpty(key)) return null;
|
|
||||||
return _states.ContainsKey(key);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 查询数据
|
|
||||||
/// <summary>
|
|
||||||
/// 默认:创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)<para></para>
|
|
||||||
/// 重写:使用
|
|
||||||
/// </summary>
|
|
||||||
public virtual ISelect<TEntity> Select => SelectAggregateRoot;
|
|
||||||
/// <summary>
|
|
||||||
/// 创建查询对象(纯净)<para></para>
|
|
||||||
/// _<para></para>
|
|
||||||
/// 聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发<para></para>
|
|
||||||
/// string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order))
|
|
||||||
/// </summary>
|
|
||||||
protected ISelect<TEntity> SelectDiy => _repository.Select.TrackToList(SelectAggregateRootTracking);
|
|
||||||
/// <summary>
|
|
||||||
/// 创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected ISelect<TEntity> SelectAggregateRoot
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var query = _repository.Select.TrackToList(SelectAggregateRootTracking);
|
|
||||||
query = AggregateRootUtils.GetAutoIncludeQuery(_boundaryName, query);
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// ISelect.TrackToList 委托,数据返回后自动 Attach
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="list"></param>
|
|
||||||
protected void SelectAggregateRootTracking(object list)
|
|
||||||
{
|
|
||||||
if (list == null) return;
|
|
||||||
var ls = list as IEnumerable<TEntity>;
|
|
||||||
if (ls == null)
|
|
||||||
{
|
|
||||||
var ie = list as IEnumerable;
|
|
||||||
if (ie == null) return;
|
|
||||||
var isfirst = true;
|
|
||||||
foreach (var item in ie)
|
|
||||||
{
|
|
||||||
if (item == null) continue;
|
|
||||||
if (isfirst)
|
|
||||||
{
|
|
||||||
isfirst = false;
|
|
||||||
var itemType = item.GetType();
|
|
||||||
if (itemType == typeof(object)) return;
|
|
||||||
if (itemType.FullName.Contains("FreeSqlLazyEntity__")) itemType = itemType.BaseType;
|
|
||||||
if (Orm.CodeFirst.GetTableByEntity(itemType)?.Primarys.Any() != true) return;
|
|
||||||
if (itemType.GetConstructor(Type.EmptyTypes) == null) return;
|
|
||||||
}
|
|
||||||
if (item is TEntity item2) Attach(item2);
|
|
||||||
else return;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//void SelectAggregateRootNavigateReader<T1>(ISelect<T1> currentQuery, Type entityType, string navigatePath, Stack<Type> ignores)
|
|
||||||
//{
|
|
||||||
// if (ignores.Any(a => a == entityType)) return;
|
|
||||||
// ignores.Push(entityType);
|
|
||||||
// var table = Orm.CodeFirst.GetTableByEntity(entityType);
|
|
||||||
// if (table == null) return;
|
|
||||||
// if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}.";
|
|
||||||
// foreach (var tr in table.GetAllTableRef())
|
|
||||||
// {
|
|
||||||
// var tbref = tr.Value;
|
|
||||||
// if (tbref.Exception != null) continue;
|
|
||||||
// var navigateExpression = $"{navigatePath}{tr.Key}";
|
|
||||||
// switch (tbref.RefType)
|
|
||||||
// {
|
|
||||||
// case TableRefType.OneToOne:
|
|
||||||
// if (ignores.Any(a => a == tbref.RefEntityType)) break;
|
|
||||||
// currentQuery.IncludeByPropertyName(navigateExpression);
|
|
||||||
// SelectAggregateRootNavigateReader(currentQuery, tbref.RefEntityType, navigateExpression, ignores);
|
|
||||||
// break;
|
|
||||||
// case TableRefType.OneToMany:
|
|
||||||
// var ignoresCopy = new Stack<Type>(ignores.ToArray());
|
|
||||||
// currentQuery.IncludeByPropertyName(navigateExpression, then =>
|
|
||||||
// SelectAggregateRootNavigateReader(then, tbref.RefEntityType, "", ignoresCopy)); //variable 'then' of type 'FreeSql.ISelect`1[System.Object]' referenced from scope '', but it is not defined
|
|
||||||
// break;
|
|
||||||
// case TableRefType.ManyToMany:
|
|
||||||
// currentQuery.IncludeByPropertyName(navigateExpression);
|
|
||||||
// break;
|
|
||||||
// case TableRefType.PgArrayToMany:
|
|
||||||
// break;
|
|
||||||
// case TableRefType.ManyToOne:
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ignores.Pop();
|
|
||||||
//}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,297 +0,0 @@
|
|||||||
#if net40
|
|
||||||
#else
|
|
||||||
using FreeSql.Extensions.EntityUtil;
|
|
||||||
using FreeSql.Internal;
|
|
||||||
using FreeSql.Internal.Model;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FreeSql
|
|
||||||
{
|
|
||||||
partial class AggregateRootRepository<TEntity>
|
|
||||||
{
|
|
||||||
|
|
||||||
#region InsertAsync
|
|
||||||
async public virtual Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) => (await InsertAsync(new[] { entity }, cancellationToken)).FirstOrDefault();
|
|
||||||
async public virtual Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var repos = new Dictionary<Type, object>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var ret = await InsertWithinBoundaryStaticAsync(_boundaryName, _repository, GetChildRepository, entitys, null, cancellationToken);
|
|
||||||
Attach(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
_repository.FlushState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async Task<List<T1>> InsertWithinBoundaryStaticAsync<T1>(string boundaryName, IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, int[] affrows, CancellationToken cancellationToken) where T1 : class
|
|
||||||
{
|
|
||||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
|
||||||
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
|
|
||||||
var localAffrows = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await LocalInsertAsync(rootRepository, rootEntitys, true);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (affrows != null) affrows[0] = localAffrows;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocalCanInsert(Type entityType, object entity, bool isadd)
|
|
||||||
{
|
|
||||||
var stateKey = rootRepository.Orm.GetEntityKeyString(entityType, entity, false);
|
|
||||||
if (stateKey == null) return true;
|
|
||||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false)
|
|
||||||
{
|
|
||||||
if (isadd)
|
|
||||||
{
|
|
||||||
ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (stateKeys.ContainsKey(stateKey) == false)
|
|
||||||
{
|
|
||||||
if (isadd) stateKeys.Add(stateKey, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
async Task<List<T2>> LocalInsertAsync<T2>(IBaseRepository<T2> repository, IEnumerable<T2> entitys, bool cascade) where T2 : class
|
|
||||||
{
|
|
||||||
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
|
|
||||||
if (table.Primarys.Any(col => col.Attribute.IsIdentity))
|
|
||||||
{
|
|
||||||
foreach (var entity in entitys)
|
|
||||||
repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity);
|
|
||||||
}
|
|
||||||
var ret = await repository.InsertAsync(entitys, cancellationToken);
|
|
||||||
localAffrows += ret.Count;
|
|
||||||
foreach (var entity in entitys) LocalCanInsert(repository.EntityType, entity, true);
|
|
||||||
if (cascade == false) return ret;
|
|
||||||
|
|
||||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
|
||||||
var boundaryAttr = AggregateRootUtils.GetPropertyBoundaryAttribute(prop, boundaryName);
|
|
||||||
if (boundaryAttr?.Break == true) continue;
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
var otoList = ret.Select(entity =>
|
|
||||||
{
|
|
||||||
var otoItem = table.GetPropertyValue(entity, prop.Name);
|
|
||||||
if (LocalCanInsert(tbref.RefEntityType, otoItem, false) == false) return null;
|
|
||||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otoItem);
|
|
||||||
return otoItem;
|
|
||||||
}).Where(entity => entity != null).ToArray();
|
|
||||||
if (otoList.Any())
|
|
||||||
{
|
|
||||||
var repo = getChildRepository(tbref.RefEntityType);
|
|
||||||
await LocalInsertAsync(repo, otoList, boundaryAttr?.BreakThen != true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
var otmList = ret.Select(entity =>
|
|
||||||
{
|
|
||||||
var otmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
|
|
||||||
if (otmEach == null) return null;
|
|
||||||
var otmItems = new List<object>();
|
|
||||||
foreach (var otmItem in otmEach)
|
|
||||||
{
|
|
||||||
if (LocalCanInsert(tbref.RefEntityType, otmItem, false) == false) continue;
|
|
||||||
otmItems.Add(otmItem);
|
|
||||||
}
|
|
||||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otmItems);
|
|
||||||
return otmItems;
|
|
||||||
}).Where(entity => entity != null).SelectMany(entity => entity).ToArray();
|
|
||||||
if (otmList.Any())
|
|
||||||
{
|
|
||||||
var repo = getChildRepository(tbref.RefEntityType);
|
|
||||||
await LocalInsertAsync(repo, otmList, boundaryAttr?.BreakThen != true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
var mtmMidList = new List<object>();
|
|
||||||
ret.ForEach(entity =>
|
|
||||||
{
|
|
||||||
var mids = AggregateRootUtils.GetManyToManyObjects(repository.Orm, table, tbref, entity, prop);
|
|
||||||
if (mids != null) mtmMidList.AddRange(mids);
|
|
||||||
});
|
|
||||||
if (mtmMidList.Any())
|
|
||||||
{
|
|
||||||
var repo = getChildRepository(tbref.RefMiddleEntityType);
|
|
||||||
await LocalInsertAsync(repo, mtmMidList, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
async public virtual Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
|
||||||
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
|
||||||
if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotAdd_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, entity)));
|
|
||||||
|
|
||||||
var flagExists = ExistsInStates(entity);
|
|
||||||
if (flagExists == false)
|
|
||||||
{
|
|
||||||
var olddata = await Select.WhereDynamic(entity).FirstAsync(cancellationToken);
|
|
||||||
flagExists = olddata != null;
|
|
||||||
}
|
|
||||||
if (flagExists == true)
|
|
||||||
{
|
|
||||||
var affrows = await UpdateAsync(entity, cancellationToken);
|
|
||||||
if (affrows > 0) return entity;
|
|
||||||
}
|
|
||||||
if (table.Primarys.Where(a => a.Attribute.IsIdentity).Count() == table.Primarys.Length)
|
|
||||||
{
|
|
||||||
Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity);
|
|
||||||
return await InsertAsync(entity, cancellationToken);
|
|
||||||
}
|
|
||||||
throw new Exception(DbContextStrings.CannotAdd_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, entity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => UpdateAsync(new[] { entity }, cancellationToken);
|
|
||||||
public virtual Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
foreach (var entity in entitys)
|
|
||||||
{
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, null, tracking);
|
|
||||||
}
|
|
||||||
foreach (var entity in entitys)
|
|
||||||
Attach(entity);
|
|
||||||
|
|
||||||
return SaveTrackingChangeAsync(tracking, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public virtual Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => DeleteWithinBoundaryAsync(new[] { entity }, null, cancellationToken);
|
|
||||||
public virtual Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => DeleteWithinBoundaryAsync(entitys, null, cancellationToken);
|
|
||||||
async public virtual Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => await DeleteWithinBoundaryAsync(await SelectAggregateRoot.Where(predicate).ToListAsync(cancellationToken), null, cancellationToken);
|
|
||||||
async public virtual Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var deletedOutput = new List<object>();
|
|
||||||
await DeleteWithinBoundaryAsync(await SelectAggregateRoot.Where(predicate).ToListAsync(cancellationToken), deletedOutput, cancellationToken);
|
|
||||||
return deletedOutput;
|
|
||||||
}
|
|
||||||
async Task<int> DeleteWithinBoundaryAsync(IEnumerable<TEntity> entitys, List<object> deletedOutput, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
foreach (var entity in entitys)
|
|
||||||
{
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, entity, null, null, tracking);
|
|
||||||
_states.Remove(stateKey);
|
|
||||||
}
|
|
||||||
var affrows = 0;
|
|
||||||
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
|
||||||
{
|
|
||||||
affrows += await Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
|
||||||
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrowsAsync(cancellationToken);
|
|
||||||
if (deletedOutput != null) deletedOutput.AddRange(tracking.DeleteLog[a].Item2);
|
|
||||||
UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x =>
|
|
||||||
new DbContext.EntityChangeReport.ChangeInfo
|
|
||||||
{
|
|
||||||
Type = DbContext.EntityChangeType.Delete,
|
|
||||||
EntityType = tracking.DeleteLog[a].Item1,
|
|
||||||
Object = x
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return affrows;
|
|
||||||
}
|
|
||||||
|
|
||||||
async public virtual Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, propertyName, tracking);
|
|
||||||
Attach(entity); //应该只存储 propertyName 内容
|
|
||||||
await SaveTrackingChangeAsync(tracking, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async Task<int> SaveTrackingChangeAsync(AggregateRootTrackingChangeInfo tracking, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var affrows = 0;
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
var insertLogDict = tracking.InsertLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.InsertLog.Where(b => b.Item1 == a.Key).Select(b => b.Item2).ToArray());
|
|
||||||
foreach (var il in insertLogDict)
|
|
||||||
{
|
|
||||||
var repo = GetChildRepository(il.Key);
|
|
||||||
var affrowsOut = new int[1];
|
|
||||||
await InsertWithinBoundaryStaticAsync(_boundaryName, repo, GetChildRepository, il.Value, affrowsOut, cancellationToken);
|
|
||||||
affrows += affrowsOut[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
|
||||||
{
|
|
||||||
affrows += await Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
|
||||||
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrowsAsync(cancellationToken);
|
|
||||||
UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x =>
|
|
||||||
new DbContext.EntityChangeReport.ChangeInfo
|
|
||||||
{
|
|
||||||
Type = DbContext.EntityChangeType.Delete,
|
|
||||||
EntityType = tracking.DeleteLog[a].Item1,
|
|
||||||
Object = x
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateLogDict = tracking.UpdateLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.UpdateLog.Where(b => b.Item1 == a.Key).Select(b => new
|
|
||||||
{
|
|
||||||
BeforeObject = b.Item2,
|
|
||||||
AfterObject = b.Item3,
|
|
||||||
UpdateColumns = b.Item4,
|
|
||||||
UpdateColumnsString = string.Join(",", b.Item4.OrderBy(c => c))
|
|
||||||
}).ToArray());
|
|
||||||
var updateLogDict2 = updateLogDict.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.UpdateColumnsString, b => a.Value.Where(c => c.UpdateColumnsString == b.UpdateColumnsString).ToArray()));
|
|
||||||
foreach (var dl in updateLogDict2)
|
|
||||||
{
|
|
||||||
foreach (var dl2 in dl.Value)
|
|
||||||
{
|
|
||||||
affrows += await Orm.Update<object>().AsType(dl.Key).AsTable(_asTableRule)
|
|
||||||
.SetSource(dl2.Value.Select(a => a.AfterObject).ToArray())
|
|
||||||
.UpdateColumns(dl2.Value.First().UpdateColumns.ToArray())
|
|
||||||
.ExecuteAffrowsAsync(cancellationToken);
|
|
||||||
UnitOfWork?.EntityChangeReport?.Report.AddRange(dl2.Value.Select(x =>
|
|
||||||
new DbContext.EntityChangeReport.ChangeInfo
|
|
||||||
{
|
|
||||||
Type = DbContext.EntityChangeType.Update,
|
|
||||||
EntityType = dl.Key,
|
|
||||||
Object = x.AfterObject,
|
|
||||||
BeforeObject = x.BeforeObject
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
return affrows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,347 +0,0 @@
|
|||||||
using FreeSql.Extensions.EntityUtil;
|
|
||||||
using FreeSql.Internal;
|
|
||||||
using FreeSql.Internal.Model;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FreeSql
|
|
||||||
{
|
|
||||||
partial class AggregateRootRepository<TEntity>
|
|
||||||
{
|
|
||||||
|
|
||||||
#region BeginEdit/EndEdit
|
|
||||||
List<TEntity> _dataEditing;
|
|
||||||
ConcurrentDictionary<string, EntityState> _statesEditing = new ConcurrentDictionary<string, EntityState>();
|
|
||||||
public virtual void BeginEdit(List<TEntity> data)
|
|
||||||
{
|
|
||||||
if (data == null) return;
|
|
||||||
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
|
||||||
if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotEdit_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, data.First())));
|
|
||||||
_statesEditing.Clear();
|
|
||||||
_dataEditing = data;
|
|
||||||
foreach (var item in data)
|
|
||||||
{
|
|
||||||
var key = Orm.GetEntityKeyString(EntityType, item, false);
|
|
||||||
if (string.IsNullOrEmpty(key)) continue;
|
|
||||||
|
|
||||||
_statesEditing.AddOrUpdate(key, k => CreateEntityState(item), (k, ov) =>
|
|
||||||
{
|
|
||||||
AggregateRootUtils.MapEntityValue(_boundaryName, Orm, EntityType, item, ov.Value);
|
|
||||||
ov.Time = DateTime.Now;
|
|
||||||
return ov;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public virtual int EndEdit(List<TEntity> data = null)
|
|
||||||
{
|
|
||||||
if (data == null) data = _dataEditing;
|
|
||||||
if (data == null) return 0;
|
|
||||||
var tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var addList = new List<TEntity>();
|
|
||||||
var ediList = new List<TEntity>();
|
|
||||||
foreach (var item in data)
|
|
||||||
{
|
|
||||||
var key = Orm.GetEntityKeyString(EntityType, item, false);
|
|
||||||
if (_statesEditing.TryRemove(key, out var state) == false)
|
|
||||||
{
|
|
||||||
tracking.InsertLog.Add(NativeTuple.Create(EntityType, (object)item));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_states[key] = state;
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, item, null, tracking);
|
|
||||||
}
|
|
||||||
foreach (var item in _statesEditing.Values.OrderBy(a => a.Time))
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, item, null, null, tracking);
|
|
||||||
|
|
||||||
return SaveTrackingChange(tracking);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_dataEditing = null;
|
|
||||||
_statesEditing.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Insert
|
|
||||||
public virtual TEntity Insert(TEntity entity) => Insert(new[] { entity }).FirstOrDefault();
|
|
||||||
public virtual List<TEntity> Insert(IEnumerable<TEntity> entitys)
|
|
||||||
{
|
|
||||||
var repos = new Dictionary<Type, object>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var ret = InsertWithinBoundaryStatic(_boundaryName, _repository, GetChildRepository, entitys, out var affrows);
|
|
||||||
Attach(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
_repository.FlushState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static List<T1> InsertWithinBoundaryStatic<T1>(string boundaryName, IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows) where T1 : class {
|
|
||||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
|
||||||
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
|
|
||||||
var localAffrows = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return LocalInsert(rootRepository, rootEntitys, true);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
affrows = localAffrows;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocalCanInsert(Type entityType, object entity, bool isadd)
|
|
||||||
{
|
|
||||||
var stateKey = rootRepository.Orm.GetEntityKeyString(entityType, entity, false);
|
|
||||||
if (stateKey == null) return true;
|
|
||||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false)
|
|
||||||
{
|
|
||||||
if (isadd)
|
|
||||||
{
|
|
||||||
ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (stateKeys.ContainsKey(stateKey) == false)
|
|
||||||
{
|
|
||||||
if (isadd) stateKeys.Add(stateKey, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
List<T2> LocalInsert<T2>(IBaseRepository<T2> repository, IEnumerable<T2> entitys, bool cascade) where T2 : class
|
|
||||||
{
|
|
||||||
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
|
|
||||||
if (table.Primarys.Any(col => col.Attribute.IsIdentity))
|
|
||||||
{
|
|
||||||
foreach (var entity in entitys)
|
|
||||||
repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity);
|
|
||||||
}
|
|
||||||
var ret = repository.Insert(entitys);
|
|
||||||
localAffrows += ret.Count;
|
|
||||||
foreach (var entity in entitys) LocalCanInsert(repository.EntityType, entity, true);
|
|
||||||
if (cascade == false) return ret;
|
|
||||||
|
|
||||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
|
||||||
var boundaryAttr = AggregateRootUtils.GetPropertyBoundaryAttribute(prop, boundaryName);
|
|
||||||
if (boundaryAttr?.Break == true) continue;
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
var otoList = ret.Select(entity =>
|
|
||||||
{
|
|
||||||
var otoItem = table.GetPropertyValue(entity, prop.Name);
|
|
||||||
if (LocalCanInsert(tbref.RefEntityType, otoItem, false) == false) return null;
|
|
||||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otoItem);
|
|
||||||
return otoItem;
|
|
||||||
}).Where(entity => entity != null).ToArray();
|
|
||||||
if (otoList.Any())
|
|
||||||
{
|
|
||||||
var repo = getChildRepository(tbref.RefEntityType);
|
|
||||||
LocalInsert(repo, otoList, boundaryAttr?.BreakThen != true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
var otmList = ret.Select(entity =>
|
|
||||||
{
|
|
||||||
var otmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
|
|
||||||
if (otmEach == null) return null;
|
|
||||||
var otmItems = new List<object>();
|
|
||||||
foreach (var otmItem in otmEach)
|
|
||||||
{
|
|
||||||
if (LocalCanInsert(tbref.RefEntityType, otmItem, false) == false) continue;
|
|
||||||
otmItems.Add(otmItem);
|
|
||||||
}
|
|
||||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otmItems);
|
|
||||||
return otmItems;
|
|
||||||
}).Where(entity => entity != null).SelectMany(entity => entity).ToArray();
|
|
||||||
if (otmList.Any())
|
|
||||||
{
|
|
||||||
var repo = getChildRepository(tbref.RefEntityType);
|
|
||||||
LocalInsert(repo, otmList, boundaryAttr?.BreakThen != true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
var mtmMidList = new List<object>();
|
|
||||||
ret.ForEach(entity =>
|
|
||||||
{
|
|
||||||
var mids = AggregateRootUtils.GetManyToManyObjects(repository.Orm, table, tbref, entity, prop);
|
|
||||||
if (mids != null) mtmMidList.AddRange(mids);
|
|
||||||
});
|
|
||||||
if (mtmMidList.Any())
|
|
||||||
{
|
|
||||||
var repo = getChildRepository(tbref.RefMiddleEntityType);
|
|
||||||
LocalInsert(repo, mtmMidList, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public virtual TEntity InsertOrUpdate(TEntity entity)
|
|
||||||
{
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
|
||||||
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
|
||||||
if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotAdd_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, entity)));
|
|
||||||
|
|
||||||
var flagExists = ExistsInStates(entity);
|
|
||||||
if (flagExists == false)
|
|
||||||
{
|
|
||||||
var olddata = Select.WhereDynamic(entity).First();
|
|
||||||
flagExists = olddata != null;
|
|
||||||
}
|
|
||||||
if (flagExists == true)
|
|
||||||
{
|
|
||||||
var affrows = Update(entity);
|
|
||||||
if (affrows > 0) return entity;
|
|
||||||
}
|
|
||||||
if (table.Primarys.Where(a => a.Attribute.IsIdentity).Count() == table.Primarys.Length)
|
|
||||||
{
|
|
||||||
Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity);
|
|
||||||
return Insert(entity);
|
|
||||||
}
|
|
||||||
throw new Exception(DbContextStrings.CannotAdd_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, entity)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual int Update(TEntity entity) => Update(new[] { entity });
|
|
||||||
public virtual int Update(IEnumerable<TEntity> entitys)
|
|
||||||
{
|
|
||||||
var tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
foreach(var entity in entitys)
|
|
||||||
{
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, null, tracking);
|
|
||||||
}
|
|
||||||
foreach (var entity in entitys)
|
|
||||||
Attach(entity);
|
|
||||||
|
|
||||||
return SaveTrackingChange(tracking);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual int Delete(TEntity entity) => DeleteWithinBoundary(new[] { entity }, null);
|
|
||||||
public virtual int Delete(IEnumerable<TEntity> entitys) => DeleteWithinBoundary(entitys, null);
|
|
||||||
public virtual int Delete(Expression<Func<TEntity, bool>> predicate) => DeleteWithinBoundary(SelectAggregateRoot.Where(predicate).ToList(), null);
|
|
||||||
public virtual List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate)
|
|
||||||
{
|
|
||||||
var deletedOutput = new List<object>();
|
|
||||||
DeleteWithinBoundary(SelectAggregateRoot.Where(predicate).ToList(), deletedOutput);
|
|
||||||
return deletedOutput;
|
|
||||||
}
|
|
||||||
int DeleteWithinBoundary(IEnumerable<TEntity> entitys, List<object> deletedOutput)
|
|
||||||
{
|
|
||||||
var tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
foreach (var entity in entitys)
|
|
||||||
{
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, entity, null, null, tracking);
|
|
||||||
_states.Remove(stateKey);
|
|
||||||
}
|
|
||||||
var affrows = 0;
|
|
||||||
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
|
||||||
{
|
|
||||||
affrows += Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
|
||||||
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrows();
|
|
||||||
if (deletedOutput != null) deletedOutput.AddRange(tracking.DeleteLog[a].Item2);
|
|
||||||
UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x =>
|
|
||||||
new DbContext.EntityChangeReport.ChangeInfo
|
|
||||||
{
|
|
||||||
Type = DbContext.EntityChangeType.Delete,
|
|
||||||
EntityType = tracking.DeleteLog[a].Item1,
|
|
||||||
Object = x
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return affrows;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SaveMany(TEntity entity, string propertyName)
|
|
||||||
{
|
|
||||||
var tracking = new AggregateRootTrackingChangeInfo();
|
|
||||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
|
||||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
|
|
||||||
AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, propertyName, tracking);
|
|
||||||
Attach(entity); //应该只存储 propertyName 内容
|
|
||||||
SaveTrackingChange(tracking);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SaveTrackingChange(AggregateRootTrackingChangeInfo tracking)
|
|
||||||
{
|
|
||||||
var affrows = 0;
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
var insertLogDict = tracking.InsertLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.InsertLog.Where(b => b.Item1 == a.Key).Select(b => b.Item2).ToArray());
|
|
||||||
foreach (var il in insertLogDict)
|
|
||||||
{
|
|
||||||
var repo = GetChildRepository(il.Key);
|
|
||||||
InsertWithinBoundaryStatic(_boundaryName, repo, GetChildRepository, il.Value, out var affrowsOut);
|
|
||||||
affrows += affrowsOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
|
||||||
{
|
|
||||||
affrows += Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
|
||||||
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrows();
|
|
||||||
UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x =>
|
|
||||||
new DbContext.EntityChangeReport.ChangeInfo
|
|
||||||
{
|
|
||||||
Type = DbContext.EntityChangeType.Delete,
|
|
||||||
EntityType = tracking.DeleteLog[a].Item1,
|
|
||||||
Object = x
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateLogDict = tracking.UpdateLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.UpdateLog.Where(b => b.Item1 == a.Key).Select(b => new
|
|
||||||
{
|
|
||||||
BeforeObject = b.Item2,
|
|
||||||
AfterObject = b.Item3,
|
|
||||||
UpdateColumns = b.Item4,
|
|
||||||
UpdateColumnsString = string.Join(",", b.Item4.OrderBy(c => c))
|
|
||||||
}).ToArray());
|
|
||||||
var updateLogDict2 = updateLogDict.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.UpdateColumnsString, b => a.Value.Where(c => c.UpdateColumnsString == b.UpdateColumnsString).ToArray()));
|
|
||||||
foreach (var dl in updateLogDict2)
|
|
||||||
{
|
|
||||||
foreach (var dl2 in dl.Value)
|
|
||||||
{
|
|
||||||
affrows += Orm.Update<object>().AsType(dl.Key).AsTable(_asTableRule)
|
|
||||||
.SetSource(dl2.Value.Select(a => a.AfterObject).ToArray())
|
|
||||||
.UpdateColumns(dl2.Value.First().UpdateColumns.ToArray())
|
|
||||||
.ExecuteAffrows();
|
|
||||||
UnitOfWork?.EntityChangeReport?.Report.AddRange(dl2.Value.Select(x =>
|
|
||||||
new DbContext.EntityChangeReport.ChangeInfo
|
|
||||||
{
|
|
||||||
Type = DbContext.EntityChangeType.Update,
|
|
||||||
EntityType = dl.Key,
|
|
||||||
Object = x.AfterObject,
|
|
||||||
BeforeObject = x.BeforeObject
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DisposeChildRepositorys();
|
|
||||||
return affrows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,652 +0,0 @@
|
|||||||
using FreeSql;
|
|
||||||
using FreeSql.DataAnnotations;
|
|
||||||
using FreeSql.Extensions.EntityUtil;
|
|
||||||
using FreeSql.Internal;
|
|
||||||
using FreeSql.Internal.CommonProvider;
|
|
||||||
using FreeSql.Internal.Model;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace FreeSql
|
|
||||||
{
|
|
||||||
public class AggregateRootUtils
|
|
||||||
{
|
|
||||||
static ConcurrentDictionary<PropertyInfo, ConcurrentDictionary<string, AggregateRootBoundaryAttribute>> _dicGetPropertyBoundaryAttribute = new ConcurrentDictionary<PropertyInfo, ConcurrentDictionary<string, AggregateRootBoundaryAttribute>>();
|
|
||||||
public static AggregateRootBoundaryAttribute GetPropertyBoundaryAttribute(PropertyInfo prop, string boundaryName)
|
|
||||||
{
|
|
||||||
if (boundaryName == null) return null;
|
|
||||||
return _dicGetPropertyBoundaryAttribute.GetOrAdd(prop, tp => new ConcurrentDictionary<string, AggregateRootBoundaryAttribute>())
|
|
||||||
.GetOrAdd(boundaryName, bn =>
|
|
||||||
{
|
|
||||||
var attrs = prop.GetCustomAttributes(typeof(AggregateRootBoundaryAttribute), false);
|
|
||||||
if (attrs == null || attrs.Any() == false) return null;
|
|
||||||
return attrs.Select(a => a as AggregateRootBoundaryAttribute).Where(a => a.Name == bn).FirstOrDefault();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CompareEntityValue(string boundaryName, IFreeSql fsql, Type rootEntityType, object rootEntityBefore, object rootEntityAfter, string rootNavigatePropertyName, AggregateRootTrackingChangeInfo tracking)
|
|
||||||
{
|
|
||||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
|
||||||
LocalCompareEntityValue(rootEntityType, rootEntityBefore, rootEntityAfter, rootNavigatePropertyName, true);
|
|
||||||
ignores.Clear();
|
|
||||||
|
|
||||||
void LocalCompareEntityValue(Type entityType, object entityBefore, object entityAfter, string navigatePropertyName, bool cascade)
|
|
||||||
{
|
|
||||||
if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType();
|
|
||||||
|
|
||||||
if (entityBefore != null)
|
|
||||||
{
|
|
||||||
var stateKey = $":before://{fsql.GetEntityKeyString(entityType, entityBefore, false)}";
|
|
||||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
|
||||||
if (stateKeys.ContainsKey(stateKey)) return;
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
}
|
|
||||||
if (entityAfter != null)
|
|
||||||
{
|
|
||||||
var stateKey = $":after://{fsql.GetEntityKeyString(entityType, entityAfter, false)}";
|
|
||||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
|
||||||
if (stateKeys.ContainsKey(stateKey)) return;
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var table = fsql.CodeFirst.GetTableByEntity(entityType);
|
|
||||||
if (table == null) return;
|
|
||||||
if (entityBefore == null && entityAfter == null) return;
|
|
||||||
if (entityBefore == null && entityAfter != null)
|
|
||||||
{
|
|
||||||
tracking.InsertLog.Add(NativeTuple.Create(entityType, entityAfter));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (entityBefore != null && entityAfter == null)
|
|
||||||
{
|
|
||||||
tracking.DeleteLog.Add(NativeTuple.Create(entityType, new[] { entityBefore }));
|
|
||||||
NavigateReader(boundaryName, fsql, entityType, entityBefore, (path, tr, ct, stackvs) =>
|
|
||||||
{
|
|
||||||
var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() };
|
|
||||||
tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist));
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var changes = new List<string>();
|
|
||||||
foreach (var col in table.ColumnsByCs.Values)
|
|
||||||
{
|
|
||||||
if (table.ColumnsByCsIgnore.ContainsKey(col.CsName)) continue;
|
|
||||||
if (table.ColumnsByCs.ContainsKey(col.CsName))
|
|
||||||
{
|
|
||||||
if (col.Attribute.IsVersion) continue;
|
|
||||||
var propvalBefore = table.GetPropertyValue(entityBefore, col.CsName);
|
|
||||||
var propvalAfter = table.GetPropertyValue(entityAfter, col.CsName);
|
|
||||||
//if (object.Equals(propvalBefore, propvalAfter) == false) changes.Add(col.CsName);
|
|
||||||
if (CompareEntityPropertyValue(col.CsType, propvalBefore, propvalAfter) == false) changes.Add(col.CsName);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changes.Any()) tracking.UpdateLog.Add(NativeTuple.Create(entityType, entityBefore, entityAfter, changes));
|
|
||||||
if (cascade == false) return;
|
|
||||||
|
|
||||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
|
||||||
var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName);
|
|
||||||
if (boundaryAttr?.Break == true) continue;
|
|
||||||
if (navigatePropertyName != null && prop.Name != navigatePropertyName) continue;
|
|
||||||
var propvalBefore = table.GetPropertyValue(entityBefore, prop.Name);
|
|
||||||
var propvalAfter = table.GetPropertyValue(entityAfter, prop.Name);
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
|
|
||||||
LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null, boundaryAttr?.BreakThen != true);
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
|
|
||||||
LocalCompareEntityValueCollection(tbref.RefEntityType, propvalBefore as IEnumerable, propvalAfter as IEnumerable, boundaryAttr?.BreakThen != true);
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
var middleValuesBefore = GetManyToManyObjects(fsql, table, tbref, entityBefore, prop);
|
|
||||||
var middleValuesAfter = GetManyToManyObjects(fsql, table, tbref, entityAfter, prop);
|
|
||||||
LocalCompareEntityValueCollection(tbref.RefMiddleEntityType, middleValuesBefore as IEnumerable, middleValuesAfter as IEnumerable, false);
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void LocalCompareEntityValueCollection(Type elementType, IEnumerable collectionBefore, IEnumerable collectionAfter, bool cascade)
|
|
||||||
{
|
|
||||||
if (collectionBefore == null && collectionAfter == null) return;
|
|
||||||
if (collectionBefore == null && collectionAfter != null)
|
|
||||||
{
|
|
||||||
foreach (var item in collectionAfter)
|
|
||||||
tracking.InsertLog.Add(NativeTuple.Create(elementType, item));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (collectionBefore != null && collectionAfter == null)
|
|
||||||
{
|
|
||||||
//foreach (var item in collectionBefore as IEnumerable)
|
|
||||||
//{
|
|
||||||
// changelog.DeleteLog.Add(NativeTuple.Create(elementType, new[] { item }));
|
|
||||||
// NavigateReader(boundaryName, fsql, elementType, item, (path, tr, ct, stackvs) =>
|
|
||||||
// {
|
|
||||||
// var dellist = stackvs.Last() as object[] ?? new [] { stackvs.Last() };
|
|
||||||
// changelog.DeleteLog.Add(NativeTuple.Create(ct, dellist));
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Dictionary<string, object> dictBefore = new Dictionary<string, object>();
|
|
||||||
Dictionary<string, object> dictAfter = new Dictionary<string, object>();
|
|
||||||
foreach (var item in collectionBefore as IEnumerable)
|
|
||||||
{
|
|
||||||
var key = fsql.GetEntityKeyString(elementType, item, false);
|
|
||||||
if (key != null) dictBefore.Add(key, item);
|
|
||||||
}
|
|
||||||
foreach (var item in collectionAfter as IEnumerable)
|
|
||||||
{
|
|
||||||
var key = fsql.GetEntityKeyString(elementType, item, false);
|
|
||||||
if (key != null)
|
|
||||||
{
|
|
||||||
if (dictAfter.ContainsKey(key) == false)
|
|
||||||
dictAfter.Add(key, item);
|
|
||||||
}
|
|
||||||
else tracking.InsertLog.Add(NativeTuple.Create(elementType, item));
|
|
||||||
}
|
|
||||||
foreach (var key in dictBefore.Keys.ToArray())
|
|
||||||
{
|
|
||||||
if (dictAfter.ContainsKey(key) == false)
|
|
||||||
{
|
|
||||||
var value = dictBefore[key];
|
|
||||||
tracking.DeleteLog.Add(NativeTuple.Create(elementType, new[] { value }));
|
|
||||||
NavigateReader(boundaryName, fsql, elementType, value, (path, tr, ct, stackvs) =>
|
|
||||||
{
|
|
||||||
var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() };
|
|
||||||
tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist));
|
|
||||||
});
|
|
||||||
dictBefore.Remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var key in dictAfter.Keys.ToArray())
|
|
||||||
{
|
|
||||||
if (dictBefore.ContainsKey(key) == false)
|
|
||||||
{
|
|
||||||
tracking.InsertLog.Add(NativeTuple.Create(elementType, dictAfter[key]));
|
|
||||||
dictAfter.Remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var key in dictBefore.Keys)
|
|
||||||
LocalCompareEntityValue(elementType, dictBefore[key], dictAfter[key], null, cascade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentDictionary<Type, bool> _dicCompareEntityPropertyValue = new ConcurrentDictionary<Type, bool>
|
|
||||||
{
|
|
||||||
[typeof(string)] = true,
|
|
||||||
[typeof(DateTime)] = true,
|
|
||||||
[typeof(DateTime?)] = true,
|
|
||||||
[typeof(DateTimeOffset)] = true,
|
|
||||||
[typeof(DateTimeOffset?)] = true,
|
|
||||||
[typeof(TimeSpan)] = true,
|
|
||||||
[typeof(TimeSpan?)] = true,
|
|
||||||
};
|
|
||||||
public static bool CompareEntityPropertyValue(Type type, object propvalBefore, object propvalAfter)
|
|
||||||
{
|
|
||||||
if (propvalBefore == null && propvalAfter == null) return true;
|
|
||||||
if (type.IsNumberType() ||
|
|
||||||
_dicCompareEntityPropertyValue.ContainsKey(type) ||
|
|
||||||
type.IsEnum ||
|
|
||||||
type.IsValueType ||
|
|
||||||
type.NullableTypeOrThis().IsEnum) return object.Equals(propvalBefore, propvalAfter);
|
|
||||||
if (propvalBefore == null && propvalAfter != null) return false;
|
|
||||||
if (propvalBefore != null && propvalAfter == null) return false;
|
|
||||||
|
|
||||||
if (FreeSql.Internal.Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(type)) {
|
|
||||||
if (type.FullName.StartsWith("Newtonsoft."))
|
|
||||||
return object.Equals(propvalBefore.ToString(), propvalAfter.ToString());
|
|
||||||
|
|
||||||
if (typeof(IDictionary).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
var dictBefore = (propvalBefore as IDictionary);
|
|
||||||
var dictAfter = (propvalAfter as IDictionary);
|
|
||||||
if (dictBefore.Count != dictAfter.Count) return false;
|
|
||||||
foreach (var key in dictBefore.Keys)
|
|
||||||
{
|
|
||||||
if (dictAfter.Contains(key) == false) return false;
|
|
||||||
var valBefore = dictBefore[key];
|
|
||||||
var valAfter = dictAfter[key];
|
|
||||||
if (valBefore == null && valAfter == null) continue;
|
|
||||||
if (valBefore == null && valAfter != null) return false;
|
|
||||||
if (valBefore != null && valAfter == null) return false;
|
|
||||||
if (CompareEntityPropertyValue(valBefore.GetType(), valBefore, valAfter) == false) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.IsArrayOrList())
|
|
||||||
{
|
|
||||||
var enumableBefore = propvalBefore as IEnumerable;
|
|
||||||
var enumableAfter = propvalAfter as IEnumerable;
|
|
||||||
var itorBefore = enumableBefore.GetEnumerator();
|
|
||||||
var itorAfter = enumableAfter.GetEnumerator();
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var moveNextBefore = itorBefore.MoveNext();
|
|
||||||
var moveNextAfter = itorAfter.MoveNext();
|
|
||||||
if (moveNextBefore != moveNextAfter) return false;
|
|
||||||
if (moveNextBefore == false) break;
|
|
||||||
var currentBefore = itorBefore.Current;
|
|
||||||
var currentAfter = itorAfter.Current;
|
|
||||||
if (currentBefore == null && enumableAfter == null) continue;
|
|
||||||
if (currentBefore == null && currentAfter != null) return false;
|
|
||||||
if (currentBefore != null && currentAfter == null) return false;
|
|
||||||
if (CompareEntityPropertyValue(currentBefore.GetType(), currentBefore, currentAfter) == false) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.FullName.StartsWith("System.") ||
|
|
||||||
type.FullName.StartsWith("Npgsql.") ||
|
|
||||||
type.FullName.StartsWith("NetTopologySuite."))
|
|
||||||
return object.Equals(propvalBefore, propvalAfter);
|
|
||||||
|
|
||||||
if (type.IsClass)
|
|
||||||
{
|
|
||||||
foreach (var prop in type.GetProperties())
|
|
||||||
{
|
|
||||||
var valBefore = prop.GetValue(propvalBefore, new object[0]);
|
|
||||||
var valAfter = prop.GetValue(propvalAfter, new object[0]);
|
|
||||||
if (CompareEntityPropertyValue(prop.PropertyType, valBefore, valAfter) == false) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return object.Equals(propvalBefore, propvalAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void NavigateReader(string boundaryName, IFreeSql fsql, Type rootType, object rootEntity, Action<string, TableRef, Type, List<object>> callback)
|
|
||||||
{
|
|
||||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
|
||||||
var statckPath = new Stack<string>();
|
|
||||||
var stackValues = new List<object>();
|
|
||||||
statckPath.Push("_");
|
|
||||||
stackValues.Add(rootEntity);
|
|
||||||
LocalNavigateReader(rootType, rootEntity);
|
|
||||||
ignores.Clear();
|
|
||||||
|
|
||||||
void LocalNavigateReader(Type entityType, object entity)
|
|
||||||
{
|
|
||||||
if (entity == null) return;
|
|
||||||
if (entityType == null) entityType = entity.GetType();
|
|
||||||
var table = fsql.CodeFirst.GetTableByEntity(entityType);
|
|
||||||
if (table == null) return;
|
|
||||||
|
|
||||||
var stateKey = fsql.GetEntityKeyString(entityType, entity, false);
|
|
||||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
|
||||||
if (stateKeys.ContainsKey(stateKey)) return;
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
|
|
||||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
|
||||||
var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName);
|
|
||||||
if (boundaryAttr?.Break == true) continue;
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
var propval = table.GetPropertyValue(entity, prop.Name);
|
|
||||||
if (propval == null) continue;
|
|
||||||
statckPath.Push(prop.Name);
|
|
||||||
stackValues.Add(propval);
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propval);
|
|
||||||
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
|
|
||||||
if (boundaryAttr?.BreakThen != true)
|
|
||||||
LocalNavigateReader(tbref.RefEntityType, propval);
|
|
||||||
stackValues.RemoveAt(stackValues.Count - 1);
|
|
||||||
statckPath.Pop();
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
var propvalOtm = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
|
|
||||||
if (propvalOtm == null) continue;
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalOtm);
|
|
||||||
var propvalOtmList = new List<object>();
|
|
||||||
foreach (var val in propvalOtm)
|
|
||||||
propvalOtmList.Add(val);
|
|
||||||
statckPath.Push($"{prop.Name}[]");
|
|
||||||
stackValues.Add(propvalOtmList.ToArray());
|
|
||||||
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
|
|
||||||
if (boundaryAttr?.BreakThen != true)
|
|
||||||
foreach (var val in propvalOtm)
|
|
||||||
LocalNavigateReader(tbref.RefEntityType, val);
|
|
||||||
stackValues.RemoveAt(stackValues.Count - 1);
|
|
||||||
statckPath.Pop();
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
var middleValues = GetManyToManyObjects(fsql, table, tbref, entity, prop).ToArray();
|
|
||||||
if (middleValues == null) continue;
|
|
||||||
statckPath.Push($"{prop.Name}[]");
|
|
||||||
stackValues.Add(middleValues);
|
|
||||||
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefMiddleEntityType, stackValues);
|
|
||||||
stackValues.RemoveAt(stackValues.Count - 1);
|
|
||||||
statckPath.Pop();
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapEntityValue(string boundaryName, IFreeSql fsql, Type rootEntityType, object rootEntityFrom, object rootEntityTo)
|
|
||||||
{
|
|
||||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
|
||||||
LocalMapEntityValue(rootEntityType, rootEntityFrom, rootEntityTo, true);
|
|
||||||
ignores.Clear();
|
|
||||||
|
|
||||||
void LocalMapEntityValue(Type entityType, object entityFrom, object entityTo, bool cascade)
|
|
||||||
{
|
|
||||||
if (entityFrom == null || entityTo == null) return;
|
|
||||||
if (entityType == null) entityType = entityFrom?.GetType() ?? entityTo?.GetType();
|
|
||||||
var table = fsql.CodeFirst.GetTableByEntity(entityType);
|
|
||||||
if (table == null) return;
|
|
||||||
|
|
||||||
var stateKey = fsql.GetEntityKeyString(entityType, entityFrom, false);
|
|
||||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
|
||||||
if (stateKeys.ContainsKey(stateKey)) return;
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
|
|
||||||
foreach (var prop in table.Properties.Values)
|
|
||||||
{
|
|
||||||
if (table.ColumnsByCsIgnore.ContainsKey(prop.Name)) continue;
|
|
||||||
if (table.ColumnsByCs.ContainsKey(prop.Name))
|
|
||||||
{
|
|
||||||
table.SetPropertyValue(entityTo, prop.Name, table.GetPropertyValue(entityFrom, prop.Name));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cascade == false) continue;
|
|
||||||
var tbref = table.GetTableRef(prop.Name, false);
|
|
||||||
if (tbref == null) continue;
|
|
||||||
var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName);
|
|
||||||
if (boundaryAttr?.Break == true) continue;
|
|
||||||
var propvalFrom = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, entityType, entityFrom, prop.Name);
|
|
||||||
if (propvalFrom == null)
|
|
||||||
{
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
var propvalTo = tbref.RefEntityType.CreateInstanceGetDefaultValue();
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
|
|
||||||
LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo, boundaryAttr?.BreakThen != true);
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo);
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
|
|
||||||
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, boundaryAttr?.BreakThen != true);
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, false);
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void LocalMapEntityValueCollection(Type entityType, object entityFrom, object entityTo, TableRef tbref, IEnumerable propvalFrom, PropertyInfo prop, bool cascade)
|
|
||||||
{
|
|
||||||
var propvalTo = typeof(List<>).MakeGenericType(tbref.RefEntityType).CreateInstanceGetDefaultValue();
|
|
||||||
var propvalToIList = propvalTo as IList;
|
|
||||||
foreach (var fromItem in propvalFrom)
|
|
||||||
{
|
|
||||||
var toItem = tbref.RefEntityType.CreateInstanceGetDefaultValue();
|
|
||||||
LocalMapEntityValue(tbref.RefEntityType, fromItem, toItem, cascade);
|
|
||||||
propvalToIList.Add(toItem);
|
|
||||||
}
|
|
||||||
var propvalType = prop.PropertyType.GetGenericTypeDefinition();
|
|
||||||
if (propvalType == typeof(List<>) || propvalType == typeof(ICollection<>))
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo);
|
|
||||||
else if (propvalType == typeof(ObservableCollection<>))
|
|
||||||
{
|
|
||||||
//var propvalTypeOcCtor = typeof(ObservableCollection<>).MakeGenericType(tbref.RefEntityType).GetConstructor(new[] { typeof(List<>).MakeGenericType(tbref.RefEntityType) });
|
|
||||||
var propvalTypeOc = Activator.CreateInstance(typeof(ObservableCollection<>).MakeGenericType(tbref.RefEntityType), new object[] { propvalTo });
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTypeOc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentDictionary<string, ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>> _dicGetAutoIncludeQuery = new ConcurrentDictionary<string, ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>>();
|
|
||||||
public static ISelect<TEntity> GetAutoIncludeQuery<TEntity>(string boundaryName, ISelect<TEntity> select)
|
|
||||||
{
|
|
||||||
var select0p = select as Select0Provider;
|
|
||||||
var table0Type = select0p._tables[0].Table.Type;
|
|
||||||
var func = _dicGetAutoIncludeQuery.GetOrAdd(boundaryName ?? "", bn => new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>())
|
|
||||||
.GetOrAdd(typeof(TEntity), t => new ConcurrentDictionary<Type, Action<ISelect0>>())
|
|
||||||
.GetOrAdd(table0Type, t =>
|
|
||||||
{
|
|
||||||
var parmExp1 = Expression.Parameter(typeof(ISelect0));
|
|
||||||
var parmNavigateParameterExp = Expression.Parameter(typeof(TEntity), "a");
|
|
||||||
var parmQueryExp = Expression.Convert(parmExp1, typeof(ISelect<>).MakeGenericType(typeof(TEntity)));
|
|
||||||
var exp = LocalGetAutoIncludeQuery(parmQueryExp, 1, t, parmNavigateParameterExp, parmNavigateParameterExp, new Stack<Type>());
|
|
||||||
return Expression.Lambda<Action<ISelect0>>(exp, parmExp1).Compile();
|
|
||||||
});
|
|
||||||
func(select);
|
|
||||||
return select;
|
|
||||||
Expression LocalGetAutoIncludeQuery(Expression queryExp, int depth, Type entityType, ParameterExpression navigateParameterExp, Expression navigatePathExp, Stack<Type> ignores)
|
|
||||||
{
|
|
||||||
if (ignores.Any(a => a == entityType)) return queryExp;
|
|
||||||
ignores.Push(entityType);
|
|
||||||
var table = select0p._orm.CodeFirst.GetTableByEntity(entityType);
|
|
||||||
if (table == null) return queryExp;
|
|
||||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
|
||||||
var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName);
|
|
||||||
if (boundaryAttr?.Break == true) continue;
|
|
||||||
Expression navigateExp = Expression.MakeMemberAccess(navigatePathExp, prop);
|
|
||||||
//var lambdaAlias = (char)((byte)'a' + (depth - 1));
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
if (ignores.Any(a => a == tbref.RefEntityType)) break;
|
|
||||||
LocalInclude(tbref, navigateExp);
|
|
||||||
if (boundaryAttr?.BreakThen != true)
|
|
||||||
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores);
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen != true);
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false);
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
if (boundaryAttr?.Break == false)
|
|
||||||
LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false);
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
|
|
||||||
if (boundaryAttr?.Break == false)
|
|
||||||
{
|
|
||||||
LocalInclude(tbref, navigateExp);
|
|
||||||
if (boundaryAttr?.BreakThen == false)
|
|
||||||
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ignores.Pop();
|
|
||||||
return queryExp;
|
|
||||||
void LocalInclude(TableRef tbref, Expression exp)
|
|
||||||
{
|
|
||||||
var incMethod = queryExp.Type.GetMethod("Include");
|
|
||||||
if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany.Replace("IncludeMany", "Include"));
|
|
||||||
queryExp = Expression.Call(queryExp, incMethod.MakeGenericMethod(tbref.RefEntityType),
|
|
||||||
Expression.Lambda(typeof(Func<,>).MakeGenericType(entityType, tbref.RefEntityType), exp, navigateParameterExp));
|
|
||||||
}
|
|
||||||
void LocalIncludeMany(TableRef tbref, Expression exp, bool isthen)
|
|
||||||
{
|
|
||||||
var funcType = typeof(Func<,>).MakeGenericType(entityType, typeof(IEnumerable<>).MakeGenericType(tbref.RefEntityType));
|
|
||||||
var navigateSelector = Expression.Lambda(funcType, exp, navigateParameterExp);
|
|
||||||
var incMethod = queryExp.Type.GetMethod("IncludeMany");
|
|
||||||
if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany);
|
|
||||||
LambdaExpression navigateThen = null;
|
|
||||||
var navigateThenType = typeof(Action<>).MakeGenericType(typeof(ISelect<>).MakeGenericType(tbref.RefEntityType));
|
|
||||||
var thenParameter = Expression.Parameter(typeof(ISelect<>).MakeGenericType(tbref.RefEntityType), "then");
|
|
||||||
Expression paramQueryExp = thenParameter;
|
|
||||||
var paramNavigateParameterExp = Expression.Parameter(tbref.RefEntityType, string.Concat((char)((byte)'a' + (depth - 1))));
|
|
||||||
if (isthen) paramQueryExp = LocalGetAutoIncludeQuery(paramQueryExp, depth + 1, tbref.RefEntityType, paramNavigateParameterExp, paramNavigateParameterExp, ignores);
|
|
||||||
navigateThen = Expression.Lambda(navigateThenType, paramQueryExp, thenParameter);
|
|
||||||
queryExp = Expression.Call(queryExp, incMethod.MakeGenericMethod(tbref.RefEntityType), navigateSelector, navigateThen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetAutoIncludeQueryStaicCode(string boundaryName, IFreeSql fsql, Type rootEntityType)
|
|
||||||
{
|
|
||||||
return $"//fsql.Select<{rootEntityType.Name}>()\r\nSelectDiy{LocalGetAutoIncludeQueryStaicCode(1, rootEntityType, "", new Stack<Type>())}";
|
|
||||||
string LocalGetAutoIncludeQueryStaicCode(int depth, Type entityType, string navigatePath, Stack<Type> ignores)
|
|
||||||
{
|
|
||||||
var code = new StringBuilder();
|
|
||||||
if (ignores.Any(a => a == entityType)) return null;
|
|
||||||
ignores.Push(entityType);
|
|
||||||
var table = fsql.CodeFirst.GetTableByEntity(entityType);
|
|
||||||
if (table == null) return null;
|
|
||||||
if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}.";
|
|
||||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
|
||||||
var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName);
|
|
||||||
if (boundaryAttr?.Break == true) continue;
|
|
||||||
var navigateExpression = $"{navigatePath}{tr.Key}";
|
|
||||||
var depthTab = "".PadLeft(depth * 4);
|
|
||||||
var lambdaAlias = (char)((byte)'a' + (depth - 1));
|
|
||||||
var lambdaStr = $"{lambdaAlias} => {lambdaAlias}.";
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
if (ignores.Any(a => a == tbref.RefEntityType)) break;
|
|
||||||
code.Append("\r\n").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
|
|
||||||
if (boundaryAttr?.BreakThen != true)
|
|
||||||
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores));
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
|
|
||||||
if (boundaryAttr?.BreakThen != true)
|
|
||||||
{
|
|
||||||
var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
|
|
||||||
if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
|
|
||||||
}
|
|
||||||
code.Append(")");
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
|
|
||||||
if (boundaryAttr?.BreakThen == false)
|
|
||||||
{
|
|
||||||
var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
|
|
||||||
if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
|
|
||||||
}
|
|
||||||
code.Append(")");
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
code.Append("\r\n").Append(boundaryAttr != null ? "" : "//").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
|
|
||||||
if (boundaryAttr?.BreakThen == false)
|
|
||||||
{
|
|
||||||
var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
|
|
||||||
if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
|
|
||||||
}
|
|
||||||
code.Append(")");
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
|
|
||||||
code.Append("\r\n").Append(boundaryAttr != null ? "" : "//").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
|
|
||||||
if (boundaryAttr?.BreakThen == false)
|
|
||||||
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ignores.Pop();
|
|
||||||
return code.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<object> GetManyToManyObjects(IFreeSql fsql, TableInfo table, TableRef tbref, object entity, PropertyInfo prop)
|
|
||||||
{
|
|
||||||
if (tbref.RefType != TableRefType.ManyToMany) return null;
|
|
||||||
var rights = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
|
|
||||||
if (rights == null) return null;
|
|
||||||
var middles = new List<object>();
|
|
||||||
var leftpkvals = new object[tbref.Columns.Count];
|
|
||||||
for (var x = 0; x < tbref.Columns.Count; x++)
|
|
||||||
leftpkvals[x] = Utils.GetDataReaderValue(tbref.MiddleColumns[x].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, table.Type, entity, tbref.Columns[x].CsName));
|
|
||||||
foreach (var right in rights)
|
|
||||||
{
|
|
||||||
var midval = tbref.RefMiddleEntityType.CreateInstanceGetDefaultValue();
|
|
||||||
for (var x = 0; x < tbref.Columns.Count; x++)
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, tbref.RefMiddleEntityType, midval, tbref.MiddleColumns[x].CsName, leftpkvals[x]);
|
|
||||||
|
|
||||||
for (var x = tbref.Columns.Count; x < tbref.MiddleColumns.Count; x++)
|
|
||||||
{
|
|
||||||
var refcol = tbref.RefColumns[x - tbref.Columns.Count];
|
|
||||||
var refval = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, tbref.RefEntityType, right, refcol.CsName);
|
|
||||||
if (refval == refcol.CsType.CreateInstanceGetDefaultValue()) throw new Exception($"ManyToMany 关联对象的主键属性({tbref.RefEntityType.DisplayCsharp()}.{refcol.CsName})不能为空");
|
|
||||||
refval = Utils.GetDataReaderValue(tbref.MiddleColumns[x].CsType, refval);
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, tbref.RefMiddleEntityType, midval, tbref.MiddleColumns[x].CsName, refval);
|
|
||||||
}
|
|
||||||
middles.Add(midval);
|
|
||||||
}
|
|
||||||
return middles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetNavigateRelationshipValue(IFreeSql orm, TableRef tbref, Type leftType, object leftItem, object rightItem)
|
|
||||||
{
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
|
||||||
case TableRefType.OneToOne:
|
|
||||||
if (rightItem == null) return;
|
|
||||||
for (var idx = 0; idx < tbref.Columns.Count; idx++)
|
|
||||||
{
|
|
||||||
var colval = Utils.GetDataReaderValue(tbref.RefColumns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName));
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName, colval);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
if (rightItem == null) return;
|
|
||||||
var rightEachOtm = rightItem as IEnumerable;
|
|
||||||
if (rightEachOtm == null) break;
|
|
||||||
var leftColValsOtm = new object[tbref.Columns.Count];
|
|
||||||
for (var idx = 0; idx < tbref.Columns.Count; idx++)
|
|
||||||
leftColValsOtm[idx] = Utils.GetDataReaderValue(tbref.RefColumns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName));
|
|
||||||
foreach (var rightEle in rightEachOtm)
|
|
||||||
for (var idx = 0; idx < tbref.Columns.Count; idx++)
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightEle, tbref.RefColumns[idx].CsName, leftColValsOtm[idx]);
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToOne:
|
|
||||||
for (var idx = 0; idx < tbref.RefColumns.Count; idx++)
|
|
||||||
{
|
|
||||||
var colval = rightItem == null ?
|
|
||||||
tbref.Columns[idx].CsType.CreateInstanceGetDefaultValue() :
|
|
||||||
Utils.GetDataReaderValue(tbref.Columns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName));
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName, colval);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using FreeSql;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
public static class FreeSqlAggregateRootRepositoryGlobalExtensions
|
|
||||||
{
|
|
||||||
public static IBaseRepository<TEntity> GetAggregateRootRepository<TEntity>(this IFreeSql that) where TEntity : class
|
|
||||||
{
|
|
||||||
return new AggregateRootRepository<TEntity>(that);
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.666</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -4,50 +4,6 @@
|
|||||||
<name>FreeSql.DbContext</name>
|
<name>FreeSql.DbContext</name>
|
||||||
</assembly>
|
</assembly>
|
||||||
<members>
|
<members>
|
||||||
<member name="T:FreeSql.DataAnnotations.AggregateRootBoundaryAttribute">
|
|
||||||
<summary>
|
|
||||||
设置 AggregateRootRepository 边界范围<para></para>
|
|
||||||
在边界范围之内的规则 :<para></para>
|
|
||||||
1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改<para></para>
|
|
||||||
2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略)<para></para>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:FreeSql.DataAnnotations.AggregateRootBoundaryAttribute.Break">
|
|
||||||
<summary>
|
|
||||||
边界是否终止
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:FreeSql.DataAnnotations.AggregateRootBoundaryAttribute.BreakThen">
|
|
||||||
<summary>
|
|
||||||
边界是否终止向下探测
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:FreeSql.AggregateRootRepository`1.Select">
|
|
||||||
<summary>
|
|
||||||
默认:创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)<para></para>
|
|
||||||
重写:使用
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:FreeSql.AggregateRootRepository`1.SelectDiy">
|
|
||||||
<summary>
|
|
||||||
创建查询对象(纯净)<para></para>
|
|
||||||
_<para></para>
|
|
||||||
聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发<para></para>
|
|
||||||
string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order))
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:FreeSql.AggregateRootRepository`1.SelectAggregateRoot">
|
|
||||||
<summary>
|
|
||||||
创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:FreeSql.AggregateRootRepository`1.SelectAggregateRootTracking(System.Object)">
|
|
||||||
<summary>
|
|
||||||
ISelect.TrackToList 委托,数据返回后自动 Attach
|
|
||||||
</summary>
|
|
||||||
<param name="list"></param>
|
|
||||||
</member>
|
|
||||||
<member name="P:FreeSql.DbContext.Orm">
|
<member name="P:FreeSql.DbContext.Orm">
|
||||||
<summary>
|
<summary>
|
||||||
该对象 Select/Delete/Insert/Update/InsertOrUpdate 与 DbContext 事务保持一致,可省略传递 WithTransaction
|
该对象 Select/Delete/Insert/Update/InsertOrUpdate 与 DbContext 事务保持一致,可省略传递 WithTransaction
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.AggregateRoot\FreeSql.Extensions.AggregateRoot.csproj" />
|
||||||
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
|
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
|
||||||
<ProjectReference Include="..\..\FreeSql.Repository\FreeSql.Repository.csproj" />
|
<ProjectReference Include="..\..\FreeSql.Repository\FreeSql.Repository.csproj" />
|
||||||
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
|
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Sqlite\FreeSql.Provider.Sqlite.csproj" />
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<SignAssembly>False</SignAssembly>
|
<SignAssembly>False</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<Title>$(AssemblyName)</Title>
|
<Title>$(AssemblyName)</Title>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<Title>$(AssemblyName)</Title>
|
<Title>$(AssemblyName)</Title>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<Title>$(AssemblyName)</Title>
|
<Title>$(AssemblyName)</Title>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
<DelaySign>false</DelaySign>
|
<DelaySign>false</DelaySign>
|
||||||
<Version>3.2.667</Version>
|
<Version>3.2.668</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user