What I would like to do is have a method call like:
Database.Modify.InsertOrUpdate<ADDRESS>(address,
() =>
{
var result = ctx.ADDRESS.Where(o => o.JURIDICAL_ADDRESS == "Y")
.OrderByDescending(o => o.ID)
.FirstOrDefault();
return (result != null ? result.ID : 0);
},
() => ctx.ADDRESS.Add(address),
() => address.ID = resultId.Value);
and generic method like:
public static bool InsertOrUpdate<TTable>(ADDRESS address, Func<decimal?> exist, Action insert, Action update)
{
decimal? resultId = null;
Helper.Console.Try(()=> resultId = exist.Invoke(),
string.Format("Finding ({0}) information ...", table.GetType()),
string.Format("Finding ({0}) information done", table.GetType()),
string.Format("Finding ({0}) information failed", table.GetType())
);
if( resultId == null || resultId == 0 )
{
return Helper.Console.Try(insert,
string.Format("Adding ({0}) information in local context ...", table.GetType()),
string.Format("Adding ({0}) information in local context done", table.GetType()),
string.Format("Adding ({0}) information in local context failed", table.GetType())
);
}
return Helper.Console.Try(update,
string.Format("Updateing ({0}) information in local context ...", table.GetType()),
string.Format("Updateing ({0}) information in local context done", table.GetType()),
string.Format("Updateing ({0}) information in local context failed", table.GetType())
);
}
At the moment I have :
public static bool InsertOrUpdate(ADDRESS address, Entities ctx, KnowledgeBase knowledgeBase)
{
decimal? resultId = null;
Helper.Console.Try(() =>
{
var result = ctx.ADDRESS.Where(o => o.JURIDICAL_ADDRESS == "Y")
.OrderByDescending(o => o.ID)
.FirstOrDefault();
resultId = (result != null ? result.ID : 0);
},
"Finding (ADDRESS) information ...",
"Finding (ADDRESS) information done",
"Finding (ADDRESS) information failed"
);
if( resultId == null || resultId == 0 )
{
return Helper.Console.Try(() => ctx.ADDRESS.Add(address),
"Adding (ADDRESS) information in local context ...",
"Adding (ADDRESS) information in local context done",
"Adding (ADDRESS) information in local context failed"
);
}
return Helper.Console.Try(() => address.ID = resultId.Value,
"Updateing (ADDRESS) information in local context ...",
"Updateing (ADDRESS) information in local context done",
"Updateing (ADDRESS) information in local context failed"
);
}
And
public static bool InsertOrUpdate(COMPANY company, Entities ctx, KnowledgeBase knowledgeBase)
{
decimal? resultId = null;
Helper.Console.Try(() =>
{
resultId = knowledgeBase.Company[company.REGISTER_CODE];
},
"Finding (COMPANY) information ...",
"Finding (COMPANY) information done",
"Finding (COMPANY) information failed"
);
if( resultId == null || resultId == 0 )
{
return Helper.Console.Try(() => ctx.COMPANY.Add(company),
"Adding (COMPANY) information in local context ...",
"Adding (COMPANY) information in local context done",
"Adding (COMPANY) information in local context failed"
);
}
return Helper.Console.Try(() => company.ID = resultId.Value,
"Updateing (COMPANY) information in local context ...",
"Updateing (COMPANY) information in local context done",
"Updateing (COMPANY) information in local context failed"
);
}
Try example:
[DebuggerStepThrough]
public static bool Try(Action a, string t1, string t2, string t3)
{
try
{
var text1 = String.Format("{0}\t{1}", DateTime.Now.ToString(Configuration.Dateformat), t1);
var text2 = String.Format("{0}\t{1}", DateTime.Now.ToString(Configuration.Dateformat), t2);
System.Console.ForegroundColor = ConsoleColor.Green;
System.Console.WriteLine(text1);
System.Console.ForegroundColor = ConsoleColor.White;
a();
System.Console.ForegroundColor = ConsoleColor.Green;
System.Console.WriteLine(text2);
System.Console.ForegroundColor = ConsoleColor.White;
return true;
}
catch (Exception ex)
{
var text3 = String.Format("{0}\t{1} {2}", DateTime.Now.ToString(Configuration.Dateformat), t3, ex.StackTrace);
System.Console.ForegroundColor = ConsoleColor.Red;
System.Console.WriteLine(text3);
System.Console.ForegroundColor = ConsoleColor.White;
SaveDataFile(text3);
return false;
}
}
Edit
After reading Eric Lippert answer to a related problem, I though that I was ignoring the KISS principle. Therefore I stopped trying to find a way to write this.
ADDRESS queryResult = null;
// ReSharper disable RedundantArgumentName
updated = (Ui.Instance.Try(
process: () => queryResult = result.ADDRESS
.Where(o => o.JURIDICAL_ADDRESS == "Y")
.OrderByDescending(o => o.ID)
.FirstOrDefault(),
onStart: Configuration.FtInfo1Address,
onAfter: Configuration.FtInfo2Address,
onError: Configuration.FtInfo3Address
) && (queryResult == null || queryResult.ID == 0)
? Ui.Instance.Try(
process: () => ctx.ADDRESS.Add(address),
onStart: Configuration.AtInfoilc1Address,
onAfter: Configuration.AtInfoilc2Address,
onError: Configuration.AtInfoilc3Address
)
: Ui.Instance.Try(
process: () => { address.ID = queryResult.ID; },
onStart: Configuration.UtInfoilc1Address,
onAfter: Configuration.UtInfoilc2Address,
onError: Configuration.UtInfoilc3Address
));
// ReSharper restore RedundantArgumentName
Just for completeness, this is what I would use in Ui class.
public class Ui
{
//default value
public static IUserInterface Instance
{
get
{
switch (Configuration.UItype)
{
case Configuration.UIconsole:
return ConsoleUi.Instance;
//...
default:
return ConsoleUi.Instance;
}
}
}
public interface IUserInterface
{
bool Try(Action process, string onStart, string onAfter, string onError, bool isCritical = Configuration.ThrowErrorsDefault);
//...
}
public sealed class ConsoleUi : IUserInterface
{
ConsoleUi() { }
public static IUserInterface Instance { get { return Nested.Instance; } }
class Nested
{
static Nested() { }
internal static readonly ConsoleUi Instance = new ConsoleUi();
}
[DebuggerStepThrough]
public bool Try(Action process, string onStart, string onAfter, string onError, bool isCritical)
{
try
{
var text1 = String.Format("{0}\t{1}", DateTime.Now.ToString(Configuration.Dateformat), onStart);
var text2 = String.Format("{0}\t{1}", DateTime.Now.ToString(Configuration.Dateformat), onAfter);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(text1);
Console.ForegroundColor = ConsoleColor.White;
process();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(text2);
Console.ForegroundColor = ConsoleColor.White;
return true;
}
catch (Exception ex)
{
var text3 = String.Format("{0}\t{1} {2}", DateTime.Now.ToString(Configuration.Dateformat), onError, ex.StackTrace);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(text3);
Console.ForegroundColor = ConsoleColor.White;
Helper.SaveDataFile(text3);
if (isCritical)
throw;
return false;
}
}
//...
}
}
Remarks:
- I can swap default UI during runtime
- I can use precompiler to throw errors when i need them, but current approach is even better. Note: interface gives optional parameter default value, that it reads from conf.
- I have ability to add single breakpoint to debug all errors (from there I can move up the call stack)
- Argument names do improve the readability a little
- Unique error codes are better then dynamically generated as devaloper can trace with ctrl+f
[DebuggerStepThrough]
will remove extra overhead calls from custom tests.