Skip to content

Commit

Permalink
PT-12212: Ability to override the DbContextUnitOfWork (#2666)
Browse files Browse the repository at this point in the history
feat: Ability to override the DbContextUnitOfWork
  • Loading branch information
OlegoO committed Jun 27, 2023
1 parent 13d5f3c commit e2beb49
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 30 deletions.
200 changes: 177 additions & 23 deletions src/VirtoCommerce.Platform.Core/Domain/AbstractTypeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
namespace VirtoCommerce.Platform.Core.Common
{
/// <summary>
/// Abstract static type factory. With supports of type overriding and sets special factories.
/// Represents factory that supports type overriding and provides special factory capabilities.
/// </summary>
/// <typeparam name="BaseType"></typeparam>
public static class AbstractTypeFactory<BaseType>
{
private static readonly List<TypeInfo<BaseType>> _typeInfos = new List<TypeInfo<BaseType>>();

/// <summary>
/// All registered type mapping informations within current factory instance
/// Gets all registered type mapping information within the current factory instance.
/// </summary>
public static IEnumerable<TypeInfo<BaseType>> AllTypeInfos
{
Expand All @@ -23,9 +23,10 @@ public static IEnumerable<TypeInfo<BaseType>> AllTypeInfos
}
}

#pragma warning disable S2743 // Static fields should not be used in generic types
// False-positive SLint warning disabled.
// This field really need for every class applied by the template
#pragma warning disable S2743
/// <summary>
/// Gets a value indicating whether there are any type overrides registered in the factory.
/// </summary>
public static bool HasOverrides
#pragma warning restore S2743 // Static fields should not be used in generic types
{
Expand All @@ -36,14 +37,21 @@ public static bool HasOverrides
}

/// <summary>
/// Register new type (fluent method)
/// Registers a new type in the factory and returns a TypeInfo instance for further configuration.
/// </summary>
/// <returns>TypeInfo instance to continue configuration through fluent syntax</returns>
/// <typeparam name="T">The type to be registered.</typeparam>
/// <returns>TypeInfo instance for the registered type.</returns>
public static TypeInfo<BaseType> RegisterType<T>() where T : BaseType
{
return RegisterType(typeof(T));
}

/// <summary>
/// Registers a new type in the factory and returns a TypeInfo instance for further configuration.
/// </summary>
/// <param name="type">The type to be registered.</param>
/// <returns>TypeInfo instance for the registered type.</returns>
/// <exception cref="ArgumentNullException"></exception>
public static TypeInfo<BaseType> RegisterType(Type type)
{
if (type == null)
Expand All @@ -61,21 +69,27 @@ public static TypeInfo<BaseType> RegisterType(Type type)
return result;
}


/// <summary>
/// Override already registered type with a new one.
/// Overrides an already registered type with a new one and returns a TypeInfo instance for further configuration.
/// </summary>
/// <returns>TypeInfo instance to continue configuration through fluent syntax</returns>
/// <typeparam name="OldType">The currently registered type.</typeparam>
/// <typeparam name="NewType">The currently registered type.</typeparam>
/// <returns>TypeInfo instance for the overridden type.</returns>
public static TypeInfo<BaseType> OverrideType<OldType, NewType>() where NewType : BaseType
{
return OverrideType(typeof(OldType), typeof(NewType));
}



/// <summary>
/// Override already registered type with a new one.
/// Overrides an already registered type with a new one and returns a TypeInfo instance for further configuration.
/// </summary>
/// <param name="oldType">The currently registered type</param>
/// <param name="newType">The type to override <paramref name="oldType"/> with</param>
/// <returns>TypeInfo instance to continue configuration through fluent syntax</returns>
/// <param name="oldType">The currently registered type.</param>
/// <param name="newType">The type to override oldType with.</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static TypeInfo<BaseType> OverrideType(Type oldType, Type newType)
{
if (!typeof(BaseType).IsAssignableFrom(newType))
Expand All @@ -95,36 +109,115 @@ public static TypeInfo<BaseType> OverrideType(Type oldType, Type newType)
}

/// <summary>
/// Create BaseType instance considering type mapping information
/// Creates an instance of the base type using the type name.
/// </summary>
/// <returns></returns>
/// <returns>An instance of the base type.</returns>
public static BaseType TryCreateInstance()
{
return TryCreateInstance(typeof(BaseType).Name);
}

/// <summary>
/// Create derived from BaseType specified type instance considering type mapping information
/// Creates an instance of the base type using the type name and a default object.
/// </summary>
/// <returns></returns>
/// <param name="defaultObj"> The default object to use if the instance cannot be created.</param>
/// <returns>An instance of the base type.</returns>
public static BaseType TryCreateInstance(BaseType defaultObj)
{
return TryCreateInstance(typeof(BaseType).Name, defaultObj);
}

/// <summary>
/// Creates an instance of the base type using the type name, a default object, and additional constructor arguments.
/// </summary>
/// <param name="defaultObj">The default object to use if the instance cannot be created.</param>
/// <param name="args">Additional constructor arguments.</param>
/// <returns>An instance of the base type.</returns>
public static BaseType TryCreateInstance(BaseType defaultObj, params object[] args)
{
return TryCreateInstance(typeof(BaseType).Name, defaultObj, args);
}

/// <summary>
/// Creates an instance of the specified generic type using the type name.
/// </summary>
/// <typeparam name="T">Generic type</typeparam>
/// <returns>An instance of the specified generic type.</returns>
public static T TryCreateInstance<T>() where T : BaseType
{
return (T)TryCreateInstance(typeof(T).Name);
}

/// <summary>
/// Creates an instance of the specified generic type using the type name and a default object.
/// </summary>
/// <typeparam name="T">Generic type</typeparam>
/// <param name="defaultObj">The default object to use if the instance cannot be created.</param>
/// <returns>An instance of the specified generic type.</returns>
public static T TryCreateInstance<T>(T defaultObj) where T : BaseType
{
return (T)TryCreateInstance(typeof(T).Name, defaultObj);
}

/// <summary>
/// Creates an instance of the specified generic type using the type name, a default object, and additional constructor arguments.
/// </summary>
/// <typeparam name="T">Generic type</typeparam>
/// <param name="defaultObj">The default object to use if the instance cannot be created.</param>
/// <param name="args"> Additional constructor arguments.</param>
/// <returns>An instance of the specified generic type.</returns>
public static T TryCreateInstance<T>(T defaultObj, params object[] args) where T : BaseType
{
return (T)TryCreateInstance(typeof(T).Name, defaultObj, args);
}

/// <summary>
/// Creates an instance of the base type using the specified type name.
/// </summary>
/// <param name="typeName"> The name of the type to create.</param>
/// <returns>An instance of the base type.</returns>
public static BaseType TryCreateInstance(string typeName)
{
return TryCreateInstance(typeName, null);
}

/// <summary>
/// Creates an instance of the base type using the specified type name and a default object.
/// </summary>
/// <param name="typeName">The name of the type to create.</param>
/// <param name="defaultObj">The default object to use if the instance cannot be created.</param>
/// <returns>An instance of the base type.</returns>
public static BaseType TryCreateInstance(string typeName, BaseType defaultObj)
{
return TryCreateInstance(typeName, defaultObj, null);
}

/// <summary>
/// Creates an instance of the base type using the specified type name, a default object, and additional constructor arguments.
/// </summary>
/// <param name="typeName">The name of the type to create.</param>
/// <param name="defaultObj">The default object to use if the instance cannot be created.</param>
/// <param name="args">Additional constructor arguments.</param>
/// <returns>An instance of the base type.</returns>
public static BaseType TryCreateInstance(string typeName, BaseType defaultObj, params object[] args)
{
var result = defaultObj;
var typeInfo = FindTypeInfoByName(typeName);
if (typeInfo != null)
{
result = TryCreateInstance(typeName);
result = TryCreateInstance(typeName, args);
}
return result;
}


public static BaseType TryCreateInstance(string typeName)
/// <summary>
/// Creates an instance of the base type using the specified type name and additional constructor arguments.
/// </summary>
/// <param name="typeName">The name of the type to create.</param>
/// <param name="args">Additional constructor arguments.</param>
/// <returns>An instance of the base type.</returns>
/// <exception cref="OperationCanceledException"></exception>
public static BaseType TryCreateInstance(string typeName, params object[] args)
{
BaseType result;
var typeInfo = FindTypeInfoByName(typeName);
Expand All @@ -136,7 +229,7 @@ public static BaseType TryCreateInstance(string typeName)
}
else
{
result = (BaseType)Activator.CreateInstance(typeInfo.Type);
result = (BaseType)Activator.CreateInstance(typeInfo.Type, args);
}
typeInfo.SetupAction?.Invoke(result);
}
Expand All @@ -154,6 +247,11 @@ public static BaseType TryCreateInstance(string typeName)
return result;
}

/// <summary>
/// Finds the type information for the specified type name.
/// </summary>
/// <param name="typeName">The name of the type to find.</param>
/// <returns>The TypeInfo instance for the specified type name.</returns>
public static TypeInfo<BaseType> FindTypeInfoByName(string typeName)
{
//Try find first direct type match from registered types
Expand All @@ -168,7 +266,7 @@ public static TypeInfo<BaseType> FindTypeInfoByName(string typeName)
}

/// <summary>
/// Helper class contains type mapping information
/// Helper class that contains type mapping information.
/// </summary>
public class TypeInfo<BaseType>
{
Expand All @@ -179,18 +277,47 @@ public TypeInfo(Type type)
TypeName = type.Name;
}

/// <summary>
/// Gets or sets the name of the type.
/// </summary>
public string TypeName { get; private set; }
/// <summary>
/// Gets or sets the factory function used to create an instance of the type.
/// </summary>
public Func<BaseType> Factory { get; private set; }
/// <summary>
/// Gets or sets the setup action to be performed on the created instance.
/// </summary>
public Action<BaseType> SetupAction { get; private set; }
/// <summary>
/// Gets or sets the type associated with the type mapping information.
/// </summary>
public Type Type { get; private set; }
/// <summary>
/// Gets or sets the mapped type that the associated type should be mapped to.
/// </summary>
public Type MappedType { get; set; }
/// <summary>
/// Gets or sets the mapped type that the associated type should be mapped to.
/// </summary>
public ICollection<object> Services { get; set; }

/// <summary>
/// Gets the service of the specified type from the collection of services.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>The service of the specified type, or default(T) if the service is not found.</returns>
public T GetService<T>()
{
return Services.OfType<T>().FirstOrDefault();
}

/// <summary>
/// Adds the specified service to the collection of services.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="service"></param>
/// <returns>The setup action.</returns>
public TypeInfo<BaseType> WithService<T>(T service)
{
if (!Services.Contains(service))
Expand All @@ -200,36 +327,63 @@ public TypeInfo<BaseType> WithService<T>(T service)
return this;
}

/// <summary>
/// Maps the associated type to the specified type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>The setup action.</returns>
public TypeInfo<BaseType> MapToType<T>()
{
MappedType = typeof(T);
return this;
}

/// <summary>
/// Maps the associated type to the specified type.
/// </summary>
/// <param name="factory">The factory function.</param>
/// <returns>The setup action.</returns>
public TypeInfo<BaseType> WithFactory(Func<BaseType> factory)
{
Factory = factory;
return this;
}

/// <summary>
/// Maps the associated type to the specified type.
/// </summary>
/// <param name="setupAction">The setup action.</param>
/// <returns>The setup action.</returns>
public TypeInfo<BaseType> WithSetupAction(Action<BaseType> setupAction)
{
SetupAction = setupAction;
return this;
}

/// <summary>
/// Sets the name of the type.
/// </summary>
/// <param name="name">Sets the name of the type.</param>
/// <returns>Sets the name of the type.</returns>
public TypeInfo<BaseType> WithTypeName(string name)
{
TypeName = name;
return this;
}


/// <summary>
/// Checks if the associated type is assignable to the specified type name.
/// </summary>
/// <param name="typeName">The name of the type to check.</param>
/// <returns>true if the associated type is assignable to the specified type name; otherwise, false.</returns>
public bool IsAssignableTo(string typeName)
{
return Type.GetTypeInheritanceChainTo(typeof(BaseType)).Concat(new[] { typeof(BaseType) }).Any(t => typeName.EqualsInvariant(t.Name));
}

/// <summary>
/// Gets all subclasses of the associated type.
/// </summary>
public IEnumerable<Type> AllSubclasses
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ protected DbContextRepositoryBase(TContext dbContext, IUnitOfWork unitOfWork = n
DbContext.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
DbContext.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;

UnitOfWork = unitOfWork ?? new DbContextUnitOfWork(dbContext);
UnitOfWork = unitOfWork ?? AbstractTypeFactory<DbContextUnitOfWork>.TryCreateInstance(new DbContextUnitOfWork(dbContext), (DbContext)dbContext);

var connectionDb = dbContext.Database.GetDbConnection();
var connectionTimeout = connectionDb.ConnectionTimeout;
dbContext.Database.SetCommandTimeout(connectionTimeout);
}


public TContext DbContext { get; private set; }

#region IRepository Members
Expand Down
Loading

0 comments on commit e2beb49

Please sign in to comment.