Compare commits

..

2 Commits

7 changed files with 46 additions and 83 deletions

View File

@ -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();
}
}
}
}

View File

@ -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" />

View File

@ -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>();

View File

@ -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");

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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));
}
}