Compare commits
2 Commits
master
...
subtype-da
Author | SHA1 | Date | |
---|---|---|---|
ec2aadfd6b | |||
2405ae8d0e |
@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BinaryDad.AggregateDal
|
||||
{
|
||||
public class AggregateDataAccess<T> where T : class
|
||||
{
|
||||
private static ICollection<Type> dataAccessTypes;
|
||||
|
||||
static AggregateDataAccess() => LoadInstances();
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a method for all instances of <typeparamref name="T"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="func"></param>
|
||||
/// <returns></returns>
|
||||
protected TResult Invoke<TResult>(Func<T, TResult> func)
|
||||
{
|
||||
var results = new List<TResult>();
|
||||
|
||||
foreach (var dataAccessType in dataAccessTypes)
|
||||
{
|
||||
var instance = Activator.CreateInstance(dataAccessType) as T;
|
||||
|
||||
results.Add(func(instance));
|
||||
}
|
||||
|
||||
// if the "EF" version invokes first, return that value
|
||||
return results.FirstOrDefault(r => !r.Equals(default(T)));
|
||||
}
|
||||
|
||||
private static void LoadInstances()
|
||||
{
|
||||
if (dataAccessTypes == null)
|
||||
{
|
||||
var type = typeof(T);
|
||||
var aggregateType = typeof(AggregateDataAccess<T>);
|
||||
|
||||
// load all types except for 1) the interface itself, 2) any interface, and 3) is not implementing AggregateDataAccess<T>
|
||||
// NOTE: the "EF" version will load first, allowing for the "QuickBase" version to run last, in a separate thread if desired
|
||||
dataAccessTypes = Assembly.GetExecutingAssembly()
|
||||
.ExportedTypes
|
||||
.Where(t => type.IsAssignableFrom(t) && !t.IsInterface && !aggregateType.IsAssignableFrom(t))
|
||||
.OrderBy(t => t.Name.StartsWith("QuickBase", StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -46,7 +46,6 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AggregateDataAccess.cs" />
|
||||
<Compile Include="Models\BaseModel.cs" />
|
||||
<Compile Include="SettlementAttempt\EFSettlementAttemptDataAccess.cs" />
|
||||
<Compile Include="SettlementAttempt\ISettlementAttemptDataAccess.cs" />
|
||||
@ -54,7 +53,6 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Models\SettlementAttempt.cs" />
|
||||
<Compile Include="SettlementAttempt\QuickBaseSettlementAttemptDataAccess.cs" />
|
||||
<Compile Include="SettlementAttempt\SettlementAttemptDataAccess.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
|
24
Program.cs
24
Program.cs
@ -32,6 +32,15 @@ namespace BinaryDad.AggregateDal
|
||||
|
||||
#endregion
|
||||
|
||||
#region Update attempt
|
||||
|
||||
newAttempt.ClientFirstName = "Rick";
|
||||
newAttempt.ClientLastName = "Martini";
|
||||
|
||||
settlementAttemptDataAccess.UpdateAttempt(newAttempt);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get attempt
|
||||
|
||||
var attempt = settlementAttemptDataAccess.GetAttempt(1234);
|
||||
@ -52,21 +61,10 @@ namespace BinaryDad.AggregateDal
|
||||
|
||||
private static IContainer CreateContainer()
|
||||
{
|
||||
/* NOTES:
|
||||
* The StructureMap container will drive the instances of the DALs that will be invoked.
|
||||
* DALs that inherit from AggregateDataAccess<T> will automatically aggregate types implementing T
|
||||
* and will invoke them. This means we can have implementations of QuickBaseXxxDataAccess and
|
||||
* EfXxxDataAccess that focus solely on their own operations. Once we decide to turn off a feature
|
||||
* (i.e., we no longer need to save to QB for a particular DAL), we can change the .Use<T>()
|
||||
* to use a regular EfXxxDataAccess instance.
|
||||
*
|
||||
* NO OTHER PARTS OF THE CODEBASE WOULD NEED TO BE TOUCHED, as we're only changing the instance
|
||||
* of a DAL from an aggregate to a singular instance */
|
||||
|
||||
return new Container(c =>
|
||||
{
|
||||
// when both DALs are needed (SQL and QuickBase)
|
||||
c.For<ISettlementAttemptDataAccess>().Singleton().Use<SettlementAttemptDataAccess>();
|
||||
// when saving to QuickBase is needed (as well as SQL)
|
||||
c.For<ISettlementAttemptDataAccess>().Singleton().Use<QuickBaseSettlementAttemptDataAccess>();
|
||||
|
||||
// when only SQL is needed
|
||||
//c.For<ISettlementAttemptDataAccess>().Singleton().Use<EfSettlementAttemptDataAccess>();
|
||||
|
@ -5,7 +5,7 @@ namespace BinaryDad.AggregateDal
|
||||
{
|
||||
public class EFSettlementAttemptDataAccess : ISettlementAttemptDataAccess
|
||||
{
|
||||
public bool AddAttempt(SettlementAttempt attempt)
|
||||
public virtual bool AddAttempt(SettlementAttempt attempt)
|
||||
{
|
||||
// 1. create attempt in SQL using entity framework
|
||||
// 2. update the "Id" property/PK of attempt upon insertion
|
||||
@ -19,6 +19,15 @@ namespace BinaryDad.AggregateDal
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool UpdateAttempt(SettlementAttempt attempt)
|
||||
{
|
||||
Console.WriteLine($"Updating attempt ID {attempt.Id} to SQL");
|
||||
|
||||
// use EF to attach to context and SaveChanges()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public SettlementAttempt GetAttempt(int recordId)
|
||||
{
|
||||
Console.WriteLine($"Getting attempt {recordId} from SQL");
|
||||
|
@ -11,6 +11,13 @@ namespace BinaryDad.AggregateDal
|
||||
/// <returns>Whether the add operation was successful</returns>
|
||||
bool AddAttempt(SettlementAttempt attempt);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an attempt
|
||||
/// </summary>
|
||||
/// <param name="attempt">An existing attempt to update</param>
|
||||
/// <returns></returns>
|
||||
bool UpdateAttempt(SettlementAttempt attempt);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an existing settlement attempt
|
||||
/// </summary>
|
||||
|
@ -3,10 +3,13 @@ using System;
|
||||
|
||||
namespace BinaryDad.AggregateDal
|
||||
{
|
||||
public class QuickBaseSettlementAttemptDataAccess : ISettlementAttemptDataAccess
|
||||
public class QuickBaseSettlementAttemptDataAccess : EFSettlementAttemptDataAccess
|
||||
{
|
||||
public bool AddAttempt(SettlementAttempt attempt)
|
||||
public override bool AddAttempt(SettlementAttempt attempt)
|
||||
{
|
||||
// save to SQL first
|
||||
base.AddAttempt(attempt);
|
||||
|
||||
// 1. create attempt in QuickBase ("Id" SQL PK should already be populated)
|
||||
// 2. update the "RecordId" property/PK of attempt upon insertion
|
||||
// 3. return if operation is successful
|
||||
@ -19,7 +22,18 @@ namespace BinaryDad.AggregateDal
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: we have no need to retrieve a record from QB, as it will come from SQL
|
||||
public SettlementAttempt GetAttempt(int recordId) => null;
|
||||
public override bool UpdateAttempt(SettlementAttempt attempt)
|
||||
{
|
||||
// update SQL first
|
||||
base.UpdateAttempt(attempt);
|
||||
|
||||
// use mapping of the properties to build a request using QB API
|
||||
// consider using reflection on the type, such as use of a [QuickBaseField(SettlementAttemptFieldMap.ClientFirstName)]
|
||||
// to build list of mappings in QB API
|
||||
|
||||
Console.WriteLine($"Updating attempt ID {attempt.RecordId} to Quickbase with SQL ID {attempt.Id}");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
using BinaryDad.AggregateDal.Models;
|
||||
using System.Linq;
|
||||
|
||||
namespace BinaryDad.AggregateDal
|
||||
{
|
||||
public class SettlementAttemptDataAccess : AggregateDataAccess<ISettlementAttemptDataAccess>, ISettlementAttemptDataAccess
|
||||
{
|
||||
public bool AddAttempt(SettlementAttempt attempt) => Invoke(d => d.AddAttempt(attempt));
|
||||
public SettlementAttempt GetAttempt(int recordId) => Invoke(d => d.GetAttempt(recordId));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user