Commit 2d9f0252 authored by 阿星Plus's avatar 阿星Plus

entity framework

parent dbf60efb
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
namespace Plus.EntityFramework
{
/// <summary>
/// DatabaseFacadeExtensions
/// </summary>
public static class DatabaseFacadeExtensions
{
public static bool IsRelational(this DatabaseFacade database)
{
return ServiceProviderServiceExtensions.GetService<IRelationalConnection>(AccessorExtensions.GetInfrastructure(database)) != null;
}
}
}
\ No newline at end of file
using Plus.Domain.Uow;
namespace Plus.EntityFramework
{
/// <summary>
/// DbContextTypeMatcher
/// </summary>
public class DbContextTypeMatcher : DbContextTypeMatcher<PlusDbContext>
{
public DbContextTypeMatcher(ICurrentUnitOfWorkProvider currentUnitOfWorkProvider)
: base(currentUnitOfWorkProvider)
{
}
}
}
\ No newline at end of file
using Plus.Dependency;
using Plus.Domain.Repositories;
using Plus.Domain.Uow;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Plus.EntityFramework
{
/// <summary>
/// DbContextTypeMatcher
/// </summary>
/// <typeparam name="TBaseDbContext"></typeparam>
public abstract class DbContextTypeMatcher<TBaseDbContext> : IDbContextTypeMatcher, ISingletonDependency
{
private readonly ICurrentUnitOfWorkProvider _currentUnitOfWorkProvider;
private readonly Dictionary<Type, List<Type>> _dbContextTypes;
protected DbContextTypeMatcher(ICurrentUnitOfWorkProvider currentUnitOfWorkProvider)
{
_currentUnitOfWorkProvider = currentUnitOfWorkProvider;
_dbContextTypes = new Dictionary<Type, List<Type>>();
}
public void Populate(Type[] dbContextTypes)
{
foreach (Type type in dbContextTypes)
{
List<Type> list = new List<Type>();
AddWithBaseTypes(type, list);
foreach (Type item in list)
{
Add(item, type);
}
}
}
public virtual Type GetConcreteType(Type sourceDbContextType)
{
if (!sourceDbContextType.GetTypeInfo().IsAbstract)
{
return sourceDbContextType;
}
List<Type> orDefault = _dbContextTypes.GetOrDefault(sourceDbContextType);
if (orDefault.IsNullOrEmpty())
{
throw new PlusException("Could not find a concrete implementation of given DbContext type: " + sourceDbContextType.AssemblyQualifiedName);
}
if (orDefault.Count == 1)
{
return orDefault[0];
}
CheckCurrentUow();
return GetDefaultDbContextType(orDefault, sourceDbContextType);
}
private void CheckCurrentUow()
{
if (_currentUnitOfWorkProvider.Current == null)
{
throw new PlusException("GetConcreteType method should be called in a UOW.");
}
}
private static Type GetDefaultDbContextType(List<Type> dbContextTypes, Type sourceDbContextType)
{
List<Type> list = (from type in dbContextTypes
where !type.GetTypeInfo().IsDefined(typeof(AutoRepositoryTypesAttribute), inherit: true)
select type).ToList();
if (list.Count == 1)
{
return list[0];
}
list = (from type in list
where type.GetTypeInfo().IsDefined(typeof(DefaultDbContextAttribute), inherit: true)
select type).ToList();
if (list.Count == 1)
{
return list[0];
}
throw new PlusException($"Found more than one concrete type for given DbContext Type ({sourceDbContextType}). Found types: {(from c in dbContextTypes select c.AssemblyQualifiedName).JoinAsString(", ")}.");
}
private static void AddWithBaseTypes(Type dbContextType, List<Type> types)
{
types.Add(dbContextType);
if (dbContextType != typeof(TBaseDbContext))
{
AddWithBaseTypes(dbContextType.GetTypeInfo().BaseType, types);
}
}
private void Add(Type sourceDbContextType, Type targetDbContextType)
{
if (!_dbContextTypes.ContainsKey(sourceDbContextType))
{
_dbContextTypes[sourceDbContextType] = new List<Type>();
}
_dbContextTypes[sourceDbContextType].Add(targetDbContextType);
}
}
}
\ No newline at end of file
using System;
namespace Plus.EntityFramework
{
/// <summary>
/// DefaultDbContextAttribute
/// </summary>
public class DefaultDbContextAttribute : Attribute
{
}
}
\ No newline at end of file
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Linq;
namespace Plus.EntityFramework.Extensions
{
public static class EntityEntryExtensions
{
/// <summary>
/// 检查实体及其关联的所属实体是否已更改
/// </summary>
/// <param name="entry"></param>
/// <returns></returns>
public static bool CheckOwnedEntityChange(this EntityEntry entry)
{
return entry.State == EntityState.Modified ||
entry.References.Any(r =>
r.TargetEntry != null && r.TargetEntry.Metadata.IsOwned() && CheckOwnedEntityChange(r.TargetEntry));
}
}
}
\ No newline at end of file
using System;
namespace Plus.EntityFramework
{
/// <summary>
/// IDbContextTypeMatcher
/// </summary>
public interface IDbContextTypeMatcher
{
void Populate(Type[] dbContextTypes);
Type GetConcreteType(Type sourceDbContextType);
}
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Plus\Plus.csproj" />
</ItemGroup>
</Project>
This diff is collapsed.
using System;
namespace Plus.Domain.Entities.Auditing
{
public static class EntityAuditingHelper
{
public static void SetCreationAuditProperties(object entityAsObj, long? userId)
{
IHasCreationTime hasCreationTime = entityAsObj as IHasCreationTime;
if (hasCreationTime == null)
{
return;
}
if (hasCreationTime.CreationTime == default(DateTime))
{
hasCreationTime.CreationTime = DateTime.Now;
}
if (entityAsObj is ICreationAudited && userId.HasValue)
{
ICreationAudited creationAudited = entityAsObj as ICreationAudited;
if (!creationAudited.CreatorUserId.HasValue)
{
creationAudited.CreatorUserId = userId;
}
}
}
public static void SetModificationAuditProperties(object entityAsObj, long? userId)
{
if (entityAsObj is IHasModificationTime)
{
Extensions.As<IHasModificationTime>(entityAsObj).LastModificationTime = DateTime.Now;
}
if (entityAsObj is IModificationAudited)
{
IModificationAudited modificationAudited = Extensions.As<IModificationAudited>(entityAsObj);
if (!userId.HasValue)
{
modificationAudited.LastModifierUserId = null;
}
else
{
modificationAudited.LastModifierUserId = userId;
}
}
}
}
}
\ No newline at end of file
namespace Plus.Domain.Entities.Auditing
{
public interface ICreationAudited : IHasCreationTime
{
long? CreatorUserId
{
get;
set;
}
}
public interface ICreationAudited<TUser> : ICreationAudited, IHasCreationTime where TUser : IEntity<long>
{
TUser CreatorUser
{
get;
set;
}
}
}
\ No newline at end of file
namespace Plus.Domain.Entities.Auditing
{
public interface IDeletionAudited : IHasDeletionTime, ISoftDelete
{
long? DeleterUserId
{
get;
set;
}
}
public interface IDeletionAudited<TUser> : IDeletionAudited, IHasDeletionTime, ISoftDelete where TUser : IEntity<long>
{
TUser DeleterUser
{
get;
set;
}
}
}
\ No newline at end of file
using System;
namespace Plus.Domain.Entities.Auditing
{
public interface IHasCreationTime
{
DateTime CreationTime
{
get;
set;
}
}
}
using System;
namespace Plus.Domain.Entities.Auditing
{
public interface IHasDeletionTime : ISoftDelete
{
DateTime? DeletionTime
{
get;
set;
}
}
}
\ No newline at end of file
using System;
namespace Plus.Domain.Entities.Auditing
{
public interface IHasModificationTime
{
DateTime? LastModificationTime
{
get;
set;
}
}
}
\ No newline at end of file
namespace Plus.Domain.Entities.Auditing
{
public interface IModificationAudited : IHasModificationTime
{
long? LastModifierUserId
{
get;
set;
}
}
public interface IModificationAudited<TUser> : IModificationAudited, IHasModificationTime where TUser : IEntity<long>
{
TUser LastModifierUser
{
get;
set;
}
}
}
\ No newline at end of file
using Plus.Reflection;
using System;
namespace Plus.Domain.Entities
{
/// <summary>
/// EntityHelper
/// </summary>
public static class EntityHelper
{
public static bool IsEntity(Type type)
{
return ReflectionHelper.IsAssignableToGenericType(type, typeof(IEntity<>));
}
public static Type GetPrimaryKeyType<TEntity>()
{
return GetPrimaryKeyType(typeof(TEntity));
}
public static Type GetPrimaryKeyType(Type entityType)
{
Type[] interfaces = entityType.GetInterfaces();
foreach (Type type in interfaces)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEntity<>))
{
return type.GenericTypeArguments[0];
}
}
throw new PlusException("Can not find primary key type of given entity type: " + entityType + ". Be sure that this entity type implements IEntity<TPrimaryKey> interface");
}
public static object GetEntityId(object entity)
{
if (!ReflectionHelper.IsAssignableToGenericType(entity.GetType(), typeof(IEntity<>)))
{
throw new PlusException(entity.GetType() + " is not an Entity !");
}
return ReflectionHelper.GetValueByPath(entity, entity.GetType(), "Id");
}
public static string GetHardDeleteKey(object entity)
{
return entity.GetType().FullName + ";Id=" + GetEntityId(entity);
}
}
}
\ No newline at end of file
using Plus.Event.Bus;
using System.Collections.Generic;
namespace Plus.Domain.Entities
{
/// <summary>
/// IGeneratesDomainEvents
/// </summary>
public interface IGeneratesDomainEvents
{
ICollection<IEventData> DomainEvents
{
get;
}
}
}
\ No newline at end of file
namespace Plus.Domain.Entities
{
public interface ISoftDelete
{
bool IsDeleted
{
get;
set;
}
}
}
\ No newline at end of file
......@@ -3,7 +3,7 @@
/// <summary>
/// UnitOfWorkExtensionDataTypes
/// </summary>
internal class UnitOfWorkExtensionDataTypes
public class UnitOfWorkExtensionDataTypes
{
public static string HardDelete { get; } = "HardDelete";
}
......
using System;
using System.Runtime.Serialization;
namespace Plus.Domain.Uow
{
/// <summary>
/// PlusDbConcurrencyException
/// </summary>
[Serializable]
public class PlusDbConcurrencyException : PlusException
{
public PlusDbConcurrencyException()
{
}
public PlusDbConcurrencyException(SerializationInfo serializationInfo, StreamingContext context)
: base(serializationInfo, context)
{
}
public PlusDbConcurrencyException(string message)
: base(message)
{
}
public PlusDbConcurrencyException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
\ No newline at end of file
using System;
namespace Plus.Event.Bus.Entities
{
[Serializable]
public class DomainEventEntry
{
public object SourceEntity
{
get;
}
public IEventData EventData
{
get;
}
public DomainEventEntry(object sourceEntity, IEventData eventData)
{
SourceEntity = sourceEntity;
EventData = eventData;
}
}
}
\ No newline at end of file
using System;
namespace Plus.Event.Bus.Entities
{
[Serializable]
public class EntityChangeEntry
{
public object Entity
{
get;
set;
}
public EntityChangeType ChangeType
{
get;
set;
}
public EntityChangeEntry(object entity, EntityChangeType changeType)
{
Entity = entity;
ChangeType = changeType;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
namespace Plus.Event.Bus.Entities
{
public class EntityChangeReport
{
public List<EntityChangeEntry> ChangedEntities
{
get;
}
public List<DomainEventEntry> DomainEvents
{
get;
}
public EntityChangeReport()
{
ChangedEntities = new List<EntityChangeEntry>();
DomainEvents = new List<DomainEventEntry>();
}
public bool IsEmpty()
{
return ChangedEntities.Count <= 0 && DomainEvents.Count <= 0;
}
public override string ToString()
{
return $"[EntityChangeReport] ChangedEntities: {ChangedEntities.Count}, DomainEvents: {DomainEvents.Count}";
}
}
}
\ No newline at end of file
namespace Plus.Event.Bus.Entities
{
public enum EntityChangeType
{
Created,
Updated,
Deleted
}
}
\ No newline at end of file
using System.Threading.Tasks;
namespace Plus.Event.Bus.Entities
{
public interface IEntityChangeEventHelper
{
void TriggerEvents(EntityChangeReport changeReport);
Task TriggerEventsAsync(EntityChangeReport changeReport);
void TriggerEntityCreatingEvent(object entity);
void TriggerEntityCreatedEventOnUowCompleted(object entity);
void TriggerEntityUpdatingEvent(object entity);
void TriggerEntityUpdatedEventOnUowCompleted(object entity);
void TriggerEntityDeletingEvent(object entity);
void TriggerEntityDeletedEventOnUowCompleted(object entity);
}
}
\ No newline at end of file
using System.Threading.Tasks;
namespace Plus.Event.Bus.Entities
{
public class NullEntityChangeEventHelper : IEntityChangeEventHelper
{
public static NullEntityChangeEventHelper Instance
{
get;
} = new NullEntityChangeEventHelper();
private NullEntityChangeEventHelper()
{
}
public void TriggerEntityCreatingEvent(object entity)
{
}
public void TriggerEntityCreatedEventOnUowCompleted(object entity)
{
}
public void TriggerEntityUpdatingEvent(object entity)
{
}
public void TriggerEntityUpdatedEventOnUowCompleted(object entity)
{
}
public void TriggerEntityDeletingEvent(object entity)
{
}
public void TriggerEntityDeletedEventOnUowCompleted(object entity)
{
}
public void TriggerEvents(EntityChangeReport changeReport)
{
}
public Task TriggerEventsAsync(EntityChangeReport changeReport)
{
return Task.FromResult(0);
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment