Created
November 2, 2016 10:08
-
-
Save odytrice/4067d4d35497fb51bec1c16a59151360 to your computer and use it in GitHub Desktop.
Merge Command For Fluent Migrator written in F#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace FluentMigrator.Extensions | |
open FluentMigrator.Expressions | |
open FluentMigrator.Infrastructure | |
open System | |
open System.Collections.Generic | |
open System.Linq | |
open FluentMigrator | |
open FluentMigrator.Model | |
open System.Data | |
open System.ComponentModel | |
type IMergeDataOrMatchSyntax<'T> = | |
abstract member Row : 'T -> IMergeDataOrMatchSyntax<'T> | |
abstract member Match: ('T -> 'M) -> unit; | |
type IMergeDataSyntax = | |
abstract member Row: 'T -> IMergeDataOrMatchSyntax<'T> | |
and IMergeDataOrInSchemaSyntax = | |
inherit IMergeDataSyntax | |
abstract member InSchema: string -> IMergeDataSyntax; | |
type IMergeExpressionRoot = | |
abstract member IntoTable : string -> IMergeDataOrInSchemaSyntax | |
type MergeDataExpression() = | |
inherit MigrationExpressionBase() | |
let rows = ResizeArray<InsertionDataDefinition>(); | |
let matchColumns = ResizeArray<string>(); | |
let additionalFeatures = new Dictionary<string, obj>(); | |
member val SchemaName = Unchecked.defaultof<string> with get,set | |
member val TableName = Unchecked.defaultof<string> with get,set | |
member this.Rows with get() = rows | |
member this.AdditionalFeatures with get() = additionalFeatures | |
member this.MatchColumns with get() = matchColumns | |
override this.CollectValidationErrors (errors: ICollection<string>) = () | |
override this.ExecuteWith (processor : IMigrationProcessor) = | |
let existingDataSet = processor.ReadTableData(this.SchemaName, this.TableName) | |
let existingTable = existingDataSet.Tables.[0] | |
for row in rows do | |
let anyColumn (r:DataRow) = | |
let select (mc:string) = | |
let ex = r.[mc] | |
let nw = row.Where(fun p -> p.Key = mc).Select(fun p -> p.Value).SingleOrDefault() | |
match ex, nw with | |
| null, _ -> ex = nw | |
| _ , null -> ex = nw | |
| _ -> ex.Equals(nw) | |
matchColumns.Select(select).All(fun m -> m); | |
let exists = existingTable.Rows.OfType<DataRow>().Any(Func<DataRow,bool> anyColumn) | |
if exists then | |
this.ExecuteUpdateWith(processor, row) | |
else | |
this.ExecuteInsertWith(processor, row) | |
member this.ExecuteUpdateWith (processor:IMigrationProcessor, row: ResizeArray<KeyValuePair<string, obj>>) = | |
let update = UpdateDataExpression( | |
SchemaName = this.SchemaName, | |
TableName = this.TableName, | |
IsAllRows = false, | |
Set = row.Where(fun p -> not(matchColumns.Contains(p.Key))).ToList(), | |
Where = matchColumns.Select(fun mc -> | |
let v = row.Where(fun p -> p.Key = mc).Select(fun p -> p.Value).SingleOrDefault() | |
new KeyValuePair<string, obj>(mc, v) | |
).ToList() | |
) | |
processor.Process(update); | |
member this.ExecuteInsertWith (processor:IMigrationProcessor, row:InsertionDataDefinition) = | |
let insert = InsertDataExpression( | |
SchemaName = this.SchemaName, | |
TableName = this.TableName) | |
for af in additionalFeatures do | |
insert.AdditionalFeatures.Add(af.Key, af.Value) | |
insert.Rows.Add(row) | |
processor.Process(insert) | |
[<AbstractClass>] | |
type MergeDataExpressionBuilderBase(expression: MergeDataExpression) = | |
member this.ExtractData(dataAsAnonymousType:obj): IDictionary<string, obj> = | |
let data = new Dictionary<string, obj>(); | |
let properties = TypeDescriptor.GetProperties(dataAsAnonymousType); | |
for property in properties do | |
data.Add(property.Name, property.GetValue(dataAsAnonymousType)) | |
data :> _ | |
interface ISupportAdditionalFeatures with | |
member this.AddAdditionalFeature (feature: string, value: obj) = | |
if (expression.AdditionalFeatures.ContainsKey(feature) |> not) then | |
expression.AdditionalFeatures.Add(feature, value); | |
else | |
expression.AdditionalFeatures.[feature] <- value; | |
type MergeDataExpressionTypedBuilder<'T>(expression: MergeDataExpression) = | |
inherit MergeDataExpressionBuilderBase(expression) | |
interface IMergeDataOrMatchSyntax<'T> with | |
member this.Row (dataAsAnonymousType: 'T) :IMergeDataOrMatchSyntax<'T> = | |
let data = this.ExtractData(dataAsAnonymousType) | |
let dataDefinition = new InsertionDataDefinition() | |
dataDefinition.AddRange(data) | |
expression.Rows.Add(dataDefinition) | |
this :> _ | |
member this.Match (f:'T -> 'M) = | |
let properties = TypeDescriptor.GetProperties(typeof<'M>) | |
for property in properties do | |
expression.MatchColumns.Add(property.Name) | |
type MergeDataExpressionStartBuilder(expression) = | |
inherit MergeDataExpressionBuilderBase(expression) | |
interface IMergeDataOrInSchemaSyntax with | |
member this.Row(dataAsAnonymousType: 'T) = | |
let typed = MergeDataExpressionTypedBuilder<'T>(expression) :> IMergeDataOrMatchSyntax<_> | |
typed.Row(dataAsAnonymousType) | |
member this.InSchema(schemaName) = | |
expression.SchemaName <- schemaName; | |
this :> _ | |
type MergeExpressionRoot(context:IMigrationContext) = | |
interface IMergeExpressionRoot with | |
member this.IntoTable(tableName: string): IMergeDataOrInSchemaSyntax= | |
let expression = MergeDataExpression( TableName = tableName ); | |
context.Expressions.Add(expression); | |
MergeDataExpressionStartBuilder(expression) :> _ | |
[<AbstractClass>] | |
type MigrationExtension = | |
inherit Migration | |
member this.Merge = | |
let context = | |
this.GetType() | |
.GetField("_context", System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Instance) | |
.GetValue(this) :?> IMigrationContext | |
new MergeExpressionRoot(context) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment