From 0e09e8f301dfc33c7d11250f47e8ad3db11fe74e Mon Sep 17 00:00:00 2001 From: Michael Hjorth Date: Tue, 5 Dec 2017 23:34:29 +0100 Subject: [PATCH 01/10] Add managed driver support --- .gitignore | 13 + OdpNetMicroMapper.sln | 15 +- OdpNetMicroMapper/DbMapper.cs | 39 ++- TestManaged/App.config | 33 ++ TestManaged/Test.cs | 589 +++++++++++++++++++++++++++++++++ TestManaged/TestBase.cs | 124 +++++++ TestManaged/TestManaged.csproj | 69 ++++ TestManaged/packages.config | 5 + 8 files changed, 878 insertions(+), 9 deletions(-) create mode 100644 TestManaged/App.config create mode 100644 TestManaged/Test.cs create mode 100644 TestManaged/TestBase.cs create mode 100644 TestManaged/TestManaged.csproj create mode 100644 TestManaged/packages.config diff --git a/.gitignore b/.gitignore index cd2946a..12c77d5 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,16 @@ $RECYCLE.BIN/ Network Trash Folder Temporary Items .apdisk + +# Build results +[Dd]ebug/ +[Rr]elease/ +[Bb]in/ +[Oo]bj/ +[Oo]utput*/ +packages/ +# Visual Studio +.vs/ +_NCrunch*/ +*.ncrunchsolution +*.user diff --git a/OdpNetMicroMapper.sln b/OdpNetMicroMapper.sln index 959407a..eb16097 100644 --- a/OdpNetMicroMapper.sln +++ b/OdpNetMicroMapper.sln @@ -1,10 +1,14 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2012 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2010 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OdpNetMicroMapper", "OdpNetMicroMapper\OdpNetMicroMapper.csproj", "{A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{CC7CDBB4-E507-4558-A7DE-9800D19624D7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestManaged", "TestManaged\TestManaged.csproj", "{E11DFADB-53F1-463A-AE18-5BC65C8EAD26}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -19,8 +23,15 @@ Global {CC7CDBB4-E507-4558-A7DE-9800D19624D7}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC7CDBB4-E507-4558-A7DE-9800D19624D7}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC7CDBB4-E507-4558-A7DE-9800D19624D7}.Release|Any CPU.Build.0 = Release|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B4FE17EF-B531-4D60-B63F-F4980F422F8D} + EndGlobalSection EndGlobal diff --git a/OdpNetMicroMapper/DbMapper.cs b/OdpNetMicroMapper/DbMapper.cs index 7fa9fce..ad12e26 100644 --- a/OdpNetMicroMapper/DbMapper.cs +++ b/OdpNetMicroMapper/DbMapper.cs @@ -4,6 +4,7 @@ using System.Data; using System.Reflection; using System.Configuration; +using System.IO; namespace OdpNetMicroMapper { @@ -18,21 +19,46 @@ public class DbMapper public static volatile bool PrintSqls = false; public string ConnectionString { get; set; } - Type oracleConnectionType, oracleDataAdapterType; + Type oracleConnectionType, oracleDataAdapterType, oracleClobType; Connection connectionWhenCreateExternal; public DbMapper() { ConnectionString = ConfigurationManager.AppSettings["ConnectionString"]; + FindOracleDataTypes(); + } + + private void FindOracleDataTypes() + { + var prefix = "Oracle.ManagedDataAccess"; + var assembly = FindDataAccessAssembly(prefix); + if (assembly == null) + { + prefix = "Oracle.DataAccess"; + assembly = FindDataAccessAssembly(prefix); + } + if (assembly == null) throw new FileNotFoundException($"Unable to load assembly {prefix}"); + + oracleDataAdapterType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleDataAdapter"); + oracleConnectionType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleConnection"); + oracleClobType = TypeFromAssembly(assembly, $"{prefix}.Types.OracleClob"); + } - oracleDataAdapterType = TypeFromAssembly("Oracle.DataAccess.Client.OracleDataAdapter"); - oracleConnectionType = TypeFromAssembly("Oracle.DataAccess.Client.OracleConnection"); + private Assembly FindDataAccessAssembly(string partialName) + { + try + { + return Assembly.Load(partialName); + } + catch + { + return null; + } } - private Type TypeFromAssembly(string typeAsString) + private Type TypeFromAssembly(Assembly assembly, string typeAsString) { - Assembly assembly = Assembly.Load("Oracle.DataAccess"); - Type type = assembly.GetType(typeAsString, false); + var type = assembly.GetType(typeAsString, false); return type; } @@ -88,7 +114,6 @@ public IDbDataAdapter CreateOracleDataAdapter() public object CreateClob(string text, IDbConnection connection) { - var oracleClobType = TypeFromAssembly("Oracle.DataAccess.Types.OracleClob"); object clob = Activator.CreateInstance(oracleClobType, new object[] { connection }); MethodInfo method = oracleClobType.GetMethod("Append", new Type[] { typeof(char[]), typeof(int), typeof(int) }); method.Invoke(clob, new object[] { text.ToCharArray(), 0, text.Length }); diff --git a/TestManaged/App.config b/TestManaged/App.config new file mode 100644 index 0000000..c17b27f --- /dev/null +++ b/TestManaged/App.config @@ -0,0 +1,33 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestManaged/Test.cs b/TestManaged/Test.cs new file mode 100644 index 0000000..3bee00f --- /dev/null +++ b/TestManaged/Test.cs @@ -0,0 +1,589 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using OdpNetMicroMapper; +using Oracle.ManagedDataAccess.Client; + +namespace TestManaged +{ + [TestFixture] + public class Test : TestBase + { + [Test] + public void InsertStatic() + { + Item item = new Item(); + item.Id = 3; + item.Name = "Third Item"; + orm.Insert(item, "onmm2.item"); + + item = orm.Query("select * from onmm2.item where id = :0", 3).SingleOrDefault(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Value.Date); + } + + [Test] + public void RawTest() + { + var guid = new Guid(); + orm.NonQuery("insert into onmm2.rawtest (bytes) values (:0)", guid.ToByteArray()); + + var bytesLoaded = orm.QueryScalar("select bytes from onmm2.rawtest where rownum = 1"); + var guidLoaded = new Guid(bytesLoaded); + Assert.AreEqual(guid, guidLoaded); + + dynamic item = orm.Query("select bytes from onmm2.rawtest where rownum = 1").Single(); + Assert.AreEqual(guid, new Guid(item.Bytes)); + } + + [TestCase("double")] + [TestCase("float")] + [TestCase("decimal")] + public void InsertDynamicTest(string type) + { + dynamic item = new Entity("onmm2.item"); + item.Id = 3; + item.Name = "Third Item"; + item.DateValue = DateTime.Today; + if (type=="double") item.DecimalValue = 3.1415d; + if (type == "float") item.DecimalValue = 3.1415f; + if (type == "decimal") item.DecimalValue = 3.1415m; + + orm.Insert(item); + + item = orm.Query("select * from onmm2.item where id = :0", 3).Single(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual(DateTime.Today, item.DateValue); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Date); + Assert.AreEqual(3.1415m, item.DecimalValue); + } + + [Test] + public void UpdateDynamicWithNull() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 1; + item.Name = null; + + orm.Update(item); + + item = orm.Query("select * from onmm2.item where id = :0", 1).Single(); + Assert.AreEqual(1, item.Id); + Assert.IsNull(item.Name); + } + + [Test] + public void InsertWithLongColumnMax4000chars() + { + dynamic item = new Entity("onmm2.item_with_long"); + item.Text = new string('X', 1000 * 4 ); //max 4k when inserting! + orm.Insert(item); + + var items = orm.Query("select * from onmm2.item_with_long"); + Assert.AreEqual(item.Text, items.Single().Text); + } + + [Test] + public void DeleteWithWhereClause() + { + dynamic item = new Entity("onmm2.item"); + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm2.item")); + + orm.Delete(item, "where id = :0", 1); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm2.item")); + + orm.Delete(item, "where id = :0", 2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm2.item")); + } + + [Test] + public void DeleteWithKeyMetadata() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 2; + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm2.item")); + + orm.Delete(item); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm2.item")); + + //check that the existing element is not the one deleted. + var items = orm.Query("select * from onmm2.item"); + Assert.AreEqual(1, items.Single().Id); + } + + [Test] + public void QueryScalarWhenNoRows() + { + Assert.AreEqual(null, orm.QueryScalar("select 1 from onmm2.item where 1=2")); + } + + [Test] + public void QueryScalarNullable() + { + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + } + + [Test] + public void QueryScalar() + { + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual("1", orm.QueryScalar("select 1 from dual")); + + Assert.AreEqual(DateTime.Today, orm.QueryScalar("select trunc(sysdate) from dual")); + Assert.AreEqual("test", orm.QueryScalar("select 'test' from dual")); + Assert.AreEqual(1.123456789m, orm.QueryScalar("select 1.123456789 from dual")); + } + + [Test] + public void UpdateViaWhereClause() + { + dynamic item = new Entity("onmm2.item"); + item.Name = "RENAMED"; + orm.Update(item, "where id = :0", 1); + + var items = orm.Query("select id, name from onmm2.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void UpdateViaMetaData() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 1; + item.Name = "RENAMED"; + orm.Update(item); + + var items = orm.Query("select id, name from onmm2.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void MergeInto() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 100; + item.Name = "Name100"; + orm.MergeInto(item); + + var itemLoaded = orm.Query("select * from onmm2.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + + item.Name = "renamed"; + orm.MergeInto(item); + + itemLoaded = orm.Query("select * from onmm2.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + } + + [Test] + public void MergeIntoWithOnlyPrimaryKey_ShouldNotTryUpdateAndFail() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 100; + orm.MergeInto(item); + orm.MergeInto(item); + } + + [Test] + public void ColumnWithUnderScoreBeforeDigit() + { + Entity item = orm.Query("select yield_2date from onmm2.item_odd where id = 1").Single(); + + //Assert.AreEqual(99m, item.Yield_2date); + + foreach (var p in item.GetDictionaryInDbStyle(true)) + Assert.AreEqual("yield_2date", p.Key); + + foreach (var p in item.GetDictionary()) + Assert.AreEqual("Yield_2date", p.Key); + } + + + [Test] + public void QueryDynamicToString() + { + var item = orm.Query("select * from onmm2.item where id = 1").Single(); + Assert.That(item.ToString(), Is.StringContaining("Id = 1")); + Assert.That(item.ToString(), Is.StringContaining("Name = First Item")); + } + + [Test] + public void QuerySingleTypeList() + { + var ints = orm.QuerySingleTypeList("select id from onmm2.item order by id").ToList(); + Assert.AreEqual(1, ints[0]); + Assert.AreEqual(2, ints[1]); + + var strings = orm.QuerySingleTypeList("select name from onmm2.item order by id").ToList(); + Assert.AreEqual("First Item", strings[0]); + Assert.AreEqual("Second Item", strings[1]); + } + + [Test] + public void QueryStaticNoWhereClause() + { + var items = orm.Query("select * from onmm2.item order by id").ToList(); + Assert.AreEqual(2, items.Count); + Assert.AreEqual(1, items[0].Id); + Assert.AreEqual(2, items[1].Id); + Assert.AreEqual("First Item", items[0].Name); + Assert.AreEqual("Second Item", items[1].Name); + Assert.AreEqual(0.321m, items[0].DecimalValue); + Assert.AreEqual(.123m, items[1].DecimalValue); + } + + [Test] + public void QueryDynamicWithWhereClause() + { + var item = orm.Query("select id, name from onmm2.item where id = :0", 1).SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryStaticWithWhereClause() + { + var item = orm.Query("select id, name from onmm2.item where id = :0", 1) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryNonExisting() + { + var item = orm.Query("select id, name from onmm2.item where id = :0", -1) + .SingleOrDefault(); + Assert.IsNull(item); + } + + + [Test] + public void ExecuteFunctionWithInt() + { + var result = orm.ExecuteFunction("onmm2.plus1function", 100); + Assert.AreEqual(101, result); + } + + [Test] + public void ExecuteFunctionWithString() + { + var result = orm.ExecuteFunction("onmm2.append1function", "100"); + Assert.AreEqual("1001", result); + } + + [Test] + public void ExecuteProcedureWithRefCursor() + { + var item = orm.ExecuteProcedure("onmm2.get_items_by_name", "First Item", DbMapperParameter.RefCursor) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void ExecuteProcedureWithRefCursorZeroElements() + { + var items = orm.ExecuteProcedure("onmm2.get_items_by_name", "non existing", DbMapperParameter.RefCursor); + Assert.AreEqual(0, items.Count()); + } + + [Test] + public void ExecuteProcedureWithoutRefCursor() + { + string newName = "RENAMED"; + object result = orm.ExecuteProcedure("onmm2.rename_item", 1, newName); + Assert.IsNull(result); + Assert.AreEqual(newName, orm.QueryScalar("select name from onmm2.item where id = :0", 1)); + } + + [Test] + public void InsertWithImplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm2.item", "id"); + item.id = 1; + + for (int i = 0; i < count; i++) + orm.Insert(item); + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm2.item")); + Console.WriteLine("InsertWithImplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm2.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm2.item")); + Console.WriteLine("InsertWithExplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnectionAndTransaction() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm2.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + using (var tx = connection.BeginTransaction()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + tx.Commit(); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm2.item")); + Console.WriteLine("InsertWithExplicitConnectionAndTransaction Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertAndSelectBigClob() + { + //this is at least 5 MB + string largeString = new string ('X', 1024*1024*5); + + //insert + dynamic item = new Entity("onmm2.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm2.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + + [Test] + public void InsertBigClobWithNonQuery() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + orm.NonQuery("insert into onmm2.bigclobtest (text) values (:0)", largeString); + + string largeStringFetched = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + [Test] + public void SelectWithExternalConnection() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + var r = orm.QueryScalar("select 1 from dual"); + Assert.AreEqual(1, r); + } + } + } + + [Test] + public void InsertWithExternalConnectionAndSelectBigClob() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 1); + + //insert + dynamic item = new Entity("onmm2.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm2.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + } + } + + [Test] + public void DeleteWithCompositeKeyt() + { + dynamic item1 = new Entity("onmm2.item_composite_key", "id, type"); + item1.Id = 1; + item1.Type = 1; + orm.Insert(item1); + + dynamic item2 = new Entity("onmm2.item_composite_key", "id,type"); + item2.Id = 1; + item2.Type = 2; + orm.Insert(item2); + + orm.Delete(item1); + var list = orm.Query("select * from onmm2.item_composite_key"); + Assert.AreEqual(2, list.Single().Type); + + orm.Delete(item2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm2.item_composite_key")); + } + + [Test] + public void OverflowDynamic() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void Overflow() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void LoadStaticShouldWarn() + { + DbMapper.PrintWarnings = true; + DbMapper.PrintSqls = true; + var item = orm.Query("select id, name, decimal_value, date_value from onmm2.item where id = :0", 1).SingleOrDefault(); + DbMapper.PrintWarnings = false; + DbMapper.PrintSqls = false; + } + + [Test] + public void CollectionShouldNotBeUsed() + { + ItemWithCollection item = new ItemWithCollection(); + item.Id = 3; + item.GroupsNotPresentInDb = new List() { 1 }; + orm.Insert(item, "onmm2.item"); + } + + [Test] + public void ConnectAsSys() + { + var orm = new DbMapper(); + orm.ConnectAsSys("tstdaily", "bi"); + orm.QueryScalar("select 1 from dual"); + } + + [TestCase("1\n2")] + [TestCase("1\r\n2")] + [TestCase("1\n\r2")] + [TestCase("1\r2")] + public void ClobTests(string testValue) + { + dynamic item = new Entity("onmm2.bigclobtest"); + item.Text = testValue; + orm.Insert(item); + + var fromDb = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(testValue, fromDb); + } + + [Test] + public void NonQueryIgnoreErrorShouldWork() + { + orm.NonQueryIgnoreError("should not work"); + } + + [Test] + public void ToEntityShouldIgnoreListProperties() + { + var entity = new SomeClass() + .ToEntity(); + + var dic = entity.GetDictionary(); + + Assert.AreEqual(4, dic.Count); + Assert.IsTrue(dic.ContainsKey("Prop1")); + Assert.IsTrue(dic.ContainsKey("Prop2")); + Assert.IsTrue(dic.ContainsKey("Prop3")); + Assert.IsTrue(dic.ContainsKey("Prop4")); + + } + } + + class ProcParameters + { + public decimal Input { get; set; } + public decimal EchoOutput { get; set; } + public decimal CountOutput { get; set; } + public object FundsOutput { get; set; } + } + + public class Item + { + public int Id { get; set; } + public string Name { get; set; } + public decimal DecimalValue { get; set; } + public DateTime? DateValue { get; set; } + } + + public class ItemWithCollection + { + public int Id { get; set; } + public List GroupsNotPresentInDb { get; set; } + } + + public class ItemWrongDefinition + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Decimalvalue { get; set; } + public DateTime? Datevalue { get; set; } + } + + class SomeClass + { + public string Prop1 { get; set; } + public decimal Prop2 { get; set; } + public DateTime Prop3 { get; set; } + public int? Prop4 { get; set; } + public List Col1 { get; set; } + public String[] Col2 { get; set; } + public Dictionary Col3 { get; set; } + } +} diff --git a/TestManaged/TestBase.cs b/TestManaged/TestBase.cs new file mode 100644 index 0000000..5631704 --- /dev/null +++ b/TestManaged/TestBase.cs @@ -0,0 +1,124 @@ +using System; +using NUnit.Framework; +using OdpNetMicroMapper; + +namespace TestManaged +{ + [TestFixture] + public class TestBase + { + static bool firstRun = true; + protected DbMapper orm = new DbMapper(); + protected DbMapper sysOrm = new DbMapper(); + + private void CreateUser() + { + if (!firstRun) + return; + + firstRun = false; + + sysOrm.NonQueryIgnoreError("drop user onmm2 cascade"); + sysOrm.NonQuery("create user onmm2 identified by onmm2"); + sysOrm.NonQuery("grant create session to onmm2"); + sysOrm.NonQuery("alter user onmm2 quota unlimited on users"); + Console.WriteLine("user onmm2 created"); + + var sql = "create table onmm2.bigclobtest (text clob)"; + sysOrm.NonQuery(sql); + + sql = "create table onmm2.rawtest (bytes raw(16))"; + sysOrm.NonQuery(sql); + + sql = "create table onmm2.item_with_long (text long)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm2.item_odd (id number(10) not null, yield_2date number)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm2.item_composite_key (id number(10) not null, type number(10) not null, text varchar2(100))"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm2.item (id number(10) not null, name varchar2(100), decimal_value number, date_value timestamp default sysdate not null)"; + sysOrm.NonQuery(sql); + + sql = @"create sequence onmm2.seq_items start with 3"; + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm2.append1function (pString varchar2) return varchar2 + as + begin + return pString || '1'; + end;"; + //todo fix elsewhere? //http://boncode.blogspot.com/2009/03/oracle-pls-00103-encountered-symbol.html + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm2.plus1function (pNumber number) return integer + as + begin + return pNumber + 1; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm2.get_items_by_name(pName in varchar2, io_cursor out SYS_REFCURSOR) is + begin + open io_cursor for + select * from onmm2.item t where t.name = pName; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm2.rename_item(pId number, pName in varchar2) is + begin + update onmm2.item t set t.name = pName where t.id = pId; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm2.proc_with_many_out_parameters(pInput number, pEcho out number, pCount out number, io_cursor out SYS_REFCURSOR) is + begin + select pInput into pEcho from dual; + select count(1) into pCount from onmm2.fund; + open io_cursor for + select * from onmm2.fund; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + } + + [TearDown] + public void DropSchema() + { + } + + [SetUp] + public void CreateSchema() + { + throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); + + sysOrm.ConnectAsSys("tns", "password"); + orm.ConnectionString = "data source=tns;user id=onmm2;password=onmm2;"; + + CreateUser(); + + sysOrm.NonQuery("delete from onmm2.item_composite_key"); + sysOrm.NonQuery("delete from onmm2.item"); + sysOrm.NonQuery("delete from onmm2.bigclobtest"); + sysOrm.NonQuery("delete from onmm2.item_with_long"); + sysOrm.NonQuery("delete from onmm2.item_odd"); + sysOrm.NonQuery("delete from onmm2.rawtest"); + + var sql = "insert into onmm2.item_odd (id, yield_2date) values (1, 99)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm2.item (id, name, decimal_value) values (1, 'First Item', 0.321)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm2.item (id, name, decimal_value) values (2, 'Second Item', 0.123)"; + sysOrm.NonQuery(sql); + + } + } +} diff --git a/TestManaged/TestManaged.csproj b/TestManaged/TestManaged.csproj new file mode 100644 index 0000000..b8f9013 --- /dev/null +++ b/TestManaged/TestManaged.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26} + Library + TestManaged + TestManaged + v4.6.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + ..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll + + + ..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll + + + ..\packages\Oracle.ManagedDataAccess.12.2.1100\lib\net40\Oracle.ManagedDataAccess.dll + + + ..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll + + + + + + + {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F} + OdpNetMicroMapper + + + + \ No newline at end of file diff --git a/TestManaged/packages.config b/TestManaged/packages.config new file mode 100644 index 0000000..d28b85f --- /dev/null +++ b/TestManaged/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From e1a0ef47016a5b943a9310dc1db9fffdce64a2cb Mon Sep 17 00:00:00 2001 From: Michael Hjorth Date: Wed, 28 Mar 2018 13:13:40 +0200 Subject: [PATCH 02/10] Support .net core 2.0 --- OdpNetMicroMapper.nuspec | 23 - OdpNetMicroMapper.sln | 12 +- OdpNetMicroMapper/DbMapper.cs | 55 +- OdpNetMicroMapper/OdpNetMicroMapper.csproj | 82 +-- OdpNetMicroMapper/Properties/AssemblyInfo.cs | 36 -- TestManaged/Test.cs | 18 +- TestManaged/TestBase.cs | 6 +- TestManaged/TestManaged.csproj | 74 +-- TestManaged/packages.config | 5 - TestManagedCore/App.config | 33 ++ TestManagedCore/Test.cs | 589 +++++++++++++++++++ TestManagedCore/TestBase.cs | 124 ++++ TestManagedCore/TestManagedCore.csproj | 26 + TestManagedCore/sqlnet.ora | 5 + TestManagedCore/tnsnames.ora | 149 +++++ Tests/App.config | 3 + Tests/Properties/AssemblyInfo.cs | 36 -- Tests/Test.cs | 18 +- Tests/TestBase.cs | 6 +- Tests/Tests.csproj | 91 +-- Tests/packages.config | 4 - packages/repositories.config | 4 - 22 files changed, 1046 insertions(+), 353 deletions(-) delete mode 100644 OdpNetMicroMapper.nuspec delete mode 100644 OdpNetMicroMapper/Properties/AssemblyInfo.cs delete mode 100644 TestManaged/packages.config create mode 100644 TestManagedCore/App.config create mode 100644 TestManagedCore/Test.cs create mode 100644 TestManagedCore/TestBase.cs create mode 100644 TestManagedCore/TestManagedCore.csproj create mode 100644 TestManagedCore/sqlnet.ora create mode 100644 TestManagedCore/tnsnames.ora delete mode 100644 Tests/Properties/AssemblyInfo.cs delete mode 100644 Tests/packages.config delete mode 100644 packages/repositories.config diff --git a/OdpNetMicroMapper.nuspec b/OdpNetMicroMapper.nuspec deleted file mode 100644 index cb38079..0000000 --- a/OdpNetMicroMapper.nuspec +++ /dev/null @@ -1,23 +0,0 @@ - - - - OdpNetMicroMapper - 1.2 - OdpNetMicroMapper - Stig Christensen - BankInvest - http://www.bankinvest.dk - https://github.com/stigc/OdpNetMicroMapper - false - Easy and simple Oracle db mapper - - Copyright 2017 - odp.net oracle connection orm micro mapper - - - - - - - - diff --git a/OdpNetMicroMapper.sln b/OdpNetMicroMapper.sln index eb16097..b75d2b1 100644 --- a/OdpNetMicroMapper.sln +++ b/OdpNetMicroMapper.sln @@ -3,11 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27004.2010 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OdpNetMicroMapper", "OdpNetMicroMapper\OdpNetMicroMapper.csproj", "{A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdpNetMicroMapper", "OdpNetMicroMapper\OdpNetMicroMapper.csproj", "{A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{CC7CDBB4-E507-4558-A7DE-9800D19624D7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{CC7CDBB4-E507-4558-A7DE-9800D19624D7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestManaged", "TestManaged\TestManaged.csproj", "{E11DFADB-53F1-463A-AE18-5BC65C8EAD26}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestManagedCore", "TestManagedCore\TestManagedCore.csproj", "{E11DFADB-53F1-463A-AE18-5BC65C8EAD26}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestManaged", "TestManaged\TestManaged.csproj", "{A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Debug|Any CPU.Build.0 = Debug|Any CPU {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Release|Any CPU.ActiveCfg = Release|Any CPU {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Release|Any CPU.Build.0 = Release|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OdpNetMicroMapper/DbMapper.cs b/OdpNetMicroMapper/DbMapper.cs index ad12e26..c689b25 100644 --- a/OdpNetMicroMapper/DbMapper.cs +++ b/OdpNetMicroMapper/DbMapper.cs @@ -19,15 +19,16 @@ public class DbMapper public static volatile bool PrintSqls = false; public string ConnectionString { get; set; } - Type oracleConnectionType, oracleDataAdapterType, oracleClobType; - Connection connectionWhenCreateExternal; - + private Type _oracleConnectionType, _oracleDataAdapterType, _oracleClobType; + private Connection _connectionWhenCreateExternal; + public DbMapper() { ConnectionString = ConfigurationManager.AppSettings["ConnectionString"]; FindOracleDataTypes(); } + private void FindOracleDataTypes() { var prefix = "Oracle.ManagedDataAccess"; @@ -39,9 +40,9 @@ private void FindOracleDataTypes() } if (assembly == null) throw new FileNotFoundException($"Unable to load assembly {prefix}"); - oracleDataAdapterType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleDataAdapter"); - oracleConnectionType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleConnection"); - oracleClobType = TypeFromAssembly(assembly, $"{prefix}.Types.OracleClob"); + _oracleDataAdapterType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleDataAdapter"); + _oracleConnectionType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleConnection"); + _oracleClobType = TypeFromAssembly(assembly, $"{prefix}.Types.OracleClob"); } private Assembly FindDataAccessAssembly(string partialName) @@ -65,37 +66,36 @@ private Type TypeFromAssembly(Assembly assembly, string typeAsString) /// /// Use this to establish a SYSDBA connection /// - /// public void ConnectAsSys(string dataSource, string password) { - ConnectionString = String.Format("data source={0}; user id=sys; password={1}; dba privilege=sysdba", dataSource, password); + ConnectionString = string.Format("data source={0}; user id=sys; password={1}; dba privilege=sysdba", dataSource, password); } public Connection SetExternalConnection(IDbConnection connection) { - connectionWhenCreateExternal = CreateOrReuseConnection(connection); - return connectionWhenCreateExternal; + _connectionWhenCreateExternal = CreateOrReuseConnection(connection); + return _connectionWhenCreateExternal; } public Connection OpenConnection() { - if (connectionWhenCreateExternal != null) + if (_connectionWhenCreateExternal != null) throw new Exception("Cannot open new Connection. Already open"); - connectionWhenCreateExternal = CreateOrReuseConnection(); - return connectionWhenCreateExternal; + _connectionWhenCreateExternal = CreateOrReuseConnection(); + return _connectionWhenCreateExternal; } private Connection CreateOrReuseConnection(IDbConnection connection = null) { - if (connectionWhenCreateExternal != null) + if (_connectionWhenCreateExternal != null) { - connectionWhenCreateExternal.NextLevel(); - return connectionWhenCreateExternal; + _connectionWhenCreateExternal.NextLevel(); + return _connectionWhenCreateExternal; } if (connection == null) { - connection = (IDbConnection)Activator.CreateInstance(oracleConnectionType); + connection = (IDbConnection)Activator.CreateInstance(_oracleConnectionType); connection.ConnectionString = ConnectionString; connection.Open(); } @@ -104,18 +104,18 @@ private Connection CreateOrReuseConnection(IDbConnection connection = null) internal void ReleaseConnection() { - connectionWhenCreateExternal = null; + _connectionWhenCreateExternal = null; } public IDbDataAdapter CreateOracleDataAdapter() { - return (IDbDataAdapter)Activator.CreateInstance(oracleDataAdapterType); + return (IDbDataAdapter)Activator.CreateInstance(_oracleDataAdapterType); } public object CreateClob(string text, IDbConnection connection) { - object clob = Activator.CreateInstance(oracleClobType, new object[] { connection }); - MethodInfo method = oracleClobType.GetMethod("Append", new Type[] { typeof(char[]), typeof(int), typeof(int) }); + object clob = Activator.CreateInstance(_oracleClobType, new object[] { connection }); + MethodInfo method = _oracleClobType.GetMethod("Append", new Type[] { typeof(char[]), typeof(int), typeof(int) }); method.Invoke(clob, new object[] { text.ToCharArray(), 0, text.Length }); return clob; } @@ -134,7 +134,7 @@ public IDbCommand CreateCommand(string sql, IDbConnection connection, bool bindB cmd.GetType().GetProperty("InitialLONGFetchSize") .SetValue(cmd, 1024 * 64, null); //reade up to 64kb with long columns - if (sql!=null) + if (sql != null) cmd.CommandText = sql; return cmd; @@ -301,7 +301,7 @@ public void Update(Entity item, string whereClause = null, params object[] args) SqlTokens setClauseTokens = new SqlTokens(nonPrimaryKeysColumns); - string sql = "update " + item.TableName + string sql = "update " + item.TableName + " " + setClauseTokens.AsSetClause(args.Length) + " " + whereClause; @@ -361,7 +361,7 @@ public void NonQueryIgnoreError(string sql, params object[] args) { command.ExecuteNonQuery(); } - catch(Exception ex) + catch (Exception ex) { Console.WriteLine("Db Exception ignored: " + ex.Message); } @@ -419,7 +419,7 @@ public IEnumerable Query(string sql, params object[] args) } } - public IEnumerable QuerySingleTypeList(string sql, params object[] args) + public IEnumerable QuerySingleTypeList(string sql, params object[] args) { using (var connection = CreateOrReuseConnection()) { @@ -483,12 +483,12 @@ public IEnumerable ExecuteProcedure(string procedureName, params object IDbDataAdapter da = CreateOracleDataAdapter(); da.SelectCommand = command; da.Fill(ds); - + if (ds.Tables.Count < 1) return null; List list = new List(); - + foreach (DataRow row in ds.Tables[0].Rows) list.Add(row.ToEntity()); return list; @@ -500,6 +500,7 @@ public IEnumerable ExecuteProcedure(string procedureName, params object /// Set OracleDbType on a OracleParameter without a reference to Oracle DataAccess /// /// Clob, Blob etc. + /// public void SetParameterOracleDbType(IDbDataParameter parameter, string type) { var pOracleDbType = parameter.GetType().GetProperty("OracleDbType"); diff --git a/OdpNetMicroMapper/OdpNetMicroMapper.csproj b/OdpNetMicroMapper/OdpNetMicroMapper.csproj index a070869..28a52ec 100644 --- a/OdpNetMicroMapper/OdpNetMicroMapper.csproj +++ b/OdpNetMicroMapper/OdpNetMicroMapper.csproj @@ -1,60 +1,34 @@ - - + + - Debug - AnyCPU - 8.0.30703 - 2.0 - {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F} - Library - Properties - OdpNetMicroMapper - OdpNetMicroMapper - v4.0 - 512 - - - true - full - false - ..\output\ - DEBUG;TRACE - prompt - 4 - AnyCPU - - - pdbonly - true - ..\output\ - TRACE - prompt - 4 + netcoreapp2.0;net461 + 2.0.0.0 + 2.0.0.0 + 2.0.0 + true + Stig Christensen, Michael Hjorth + BankInvest + Easy and simple Oracle db mapper + odp.net oracle connection orm micro mapper + BankInvest 2018 + Oracle driver must be added manually. Choose the relevant driver for your setup: +.net 4.6.1 edition supports managed driver from NuGet or unmanaged driver loaded from GAC. +.net core 2.0 edition supports managed driver only (currently only beta release available from Oracle). + + - - - - - + + - - + - - - - - - - + + + - - - \ No newline at end of file + + + + + diff --git a/OdpNetMicroMapper/Properties/AssemblyInfo.cs b/OdpNetMicroMapper/Properties/AssemblyInfo.cs deleted file mode 100644 index 8e3d98e..0000000 --- a/OdpNetMicroMapper/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OdpNetMicroMapper")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("OdpNetMicroMapper")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("566e38b8-352d-4b3d-aa32-0f67f8426e30")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0.0")] -[assembly: AssemblyFileVersion("1.2.0.0")] diff --git a/TestManaged/Test.cs b/TestManaged/Test.cs index 3bee00f..a30dbf8 100644 --- a/TestManaged/Test.cs +++ b/TestManaged/Test.cs @@ -30,7 +30,7 @@ public void RawTest() { var guid = new Guid(); orm.NonQuery("insert into onmm2.rawtest (bytes) values (:0)", guid.ToByteArray()); - + var bytesLoaded = orm.QueryScalar("select bytes from onmm2.rawtest where rownum = 1"); var guidLoaded = new Guid(bytesLoaded); Assert.AreEqual(guid, guidLoaded); @@ -48,7 +48,7 @@ public void InsertDynamicTest(string type) item.Id = 3; item.Name = "Third Item"; item.DateValue = DateTime.Today; - if (type=="double") item.DecimalValue = 3.1415d; + if (type == "double") item.DecimalValue = 3.1415d; if (type == "float") item.DecimalValue = 3.1415f; if (type == "decimal") item.DecimalValue = 3.1415m; @@ -80,7 +80,7 @@ public void UpdateDynamicWithNull() public void InsertWithLongColumnMax4000chars() { dynamic item = new Entity("onmm2.item_with_long"); - item.Text = new string('X', 1000 * 4 ); //max 4k when inserting! + item.Text = new string('X', 1000 * 4); //max 4k when inserting! orm.Insert(item); var items = orm.Query("select * from onmm2.item_with_long"); @@ -92,7 +92,7 @@ public void DeleteWithWhereClause() { dynamic item = new Entity("onmm2.item"); Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm2.item")); - + orm.Delete(item, "where id = :0", 1); Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm2.item")); @@ -216,8 +216,8 @@ public void ColumnWithUnderScoreBeforeDigit() public void QueryDynamicToString() { var item = orm.Query("select * from onmm2.item where id = 1").Single(); - Assert.That(item.ToString(), Is.StringContaining("Id = 1")); - Assert.That(item.ToString(), Is.StringContaining("Name = First Item")); + Assert.That(item.ToString(), Contains.Substring("Id = 1")); + Assert.That(item.ToString(), Contains.Substring("Name = First Item")); } [Test] @@ -280,7 +280,7 @@ public void ExecuteFunctionWithInt() [Test] public void ExecuteFunctionWithString() - { + { var result = orm.ExecuteFunction("onmm2.append1function", "100"); Assert.AreEqual("1001", result); } @@ -373,7 +373,7 @@ public void InsertWithExplicitConnectionAndTransaction() public void InsertAndSelectBigClob() { //this is at least 5 MB - string largeString = new string ('X', 1024*1024*5); + string largeString = new string('X', 1024 * 1024 * 5); //insert dynamic item = new Entity("onmm2.bigclobtest"); @@ -497,7 +497,7 @@ public void CollectionShouldNotBeUsed() { ItemWithCollection item = new ItemWithCollection(); item.Id = 3; - item.GroupsNotPresentInDb = new List() { 1 }; + item.GroupsNotPresentInDb = new List() { 1 }; orm.Insert(item, "onmm2.item"); } diff --git a/TestManaged/TestBase.cs b/TestManaged/TestBase.cs index 5631704..3332aee 100644 --- a/TestManaged/TestBase.cs +++ b/TestManaged/TestBase.cs @@ -96,10 +96,10 @@ public void DropSchema() [SetUp] public void CreateSchema() { - throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); + //throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); - sysOrm.ConnectAsSys("tns", "password"); - orm.ConnectionString = "data source=tns;user id=onmm2;password=onmm2;"; + sysOrm.ConnectAsSys("INTSHARE", "bi"); + orm.ConnectionString = "data source=INTSHARE;user id=onmm2;password=onmm2;"; CreateUser(); diff --git a/TestManaged/TestManaged.csproj b/TestManaged/TestManaged.csproj index b8f9013..e16ff03 100644 --- a/TestManaged/TestManaged.csproj +++ b/TestManaged/TestManaged.csproj @@ -1,69 +1,17 @@ - - - + + - Debug - AnyCPU - {E11DFADB-53F1-463A-AE18-5BC65C8EAD26} - Library - TestManaged - TestManaged - v4.6.1 - 512 - true + net461 - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - + - - - ..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll - - - ..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll - - - ..\packages\Oracle.ManagedDataAccess.12.2.1100\lib\net40\Oracle.ManagedDataAccess.dll - - - ..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll - - - + + + + - - {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F} - OdpNetMicroMapper - + - - \ No newline at end of file + + diff --git a/TestManaged/packages.config b/TestManaged/packages.config deleted file mode 100644 index d28b85f..0000000 --- a/TestManaged/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TestManagedCore/App.config b/TestManagedCore/App.config new file mode 100644 index 0000000..c17b27f --- /dev/null +++ b/TestManagedCore/App.config @@ -0,0 +1,33 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestManagedCore/Test.cs b/TestManagedCore/Test.cs new file mode 100644 index 0000000..179a55a --- /dev/null +++ b/TestManagedCore/Test.cs @@ -0,0 +1,589 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using OdpNetMicroMapper; +using Oracle.ManagedDataAccess.Client; + +namespace TestManagedCore +{ + [TestFixture] + public class Test : TestBase + { + [Test] + public void InsertStatic() + { + Item item = new Item(); + item.Id = 3; + item.Name = "Third Item"; + orm.Insert(item, "onmm3.item"); + + item = orm.Query("select * from onmm3.item where id = :0", 3).SingleOrDefault(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Value.Date); + } + + [Test] + public void RawTest() + { + var guid = new Guid(); + orm.NonQuery("insert into onmm3.rawtest (bytes) values (:0)", guid.ToByteArray()); + + var bytesLoaded = orm.QueryScalar("select bytes from onmm3.rawtest where rownum = 1"); + var guidLoaded = new Guid(bytesLoaded); + Assert.AreEqual(guid, guidLoaded); + + dynamic item = orm.Query("select bytes from onmm3.rawtest where rownum = 1").Single(); + Assert.AreEqual(guid, new Guid(item.Bytes)); + } + + [TestCase("double")] + [TestCase("float")] + [TestCase("decimal")] + public void InsertDynamicTest(string type) + { + dynamic item = new Entity("onmm3.item"); + item.Id = 3; + item.Name = "Third Item"; + item.DateValue = DateTime.Today; + if (type == "double") item.DecimalValue = 3.1415d; + if (type == "float") item.DecimalValue = 3.1415f; + if (type == "decimal") item.DecimalValue = 3.1415m; + + orm.Insert(item); + + item = orm.Query("select * from onmm3.item where id = :0", 3).Single(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual(DateTime.Today, item.DateValue); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Date); + Assert.AreEqual(3.1415m, item.DecimalValue); + } + + [Test] + public void UpdateDynamicWithNull() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 1; + item.Name = null; + + orm.Update(item); + + item = orm.Query("select * from onmm3.item where id = :0", 1).Single(); + Assert.AreEqual(1, item.Id); + Assert.IsNull(item.Name); + } + + [Test] + public void InsertWithLongColumnMax4000chars() + { + dynamic item = new Entity("onmm3.item_with_long"); + item.Text = new string('X', 1000 * 4); //max 4k when inserting! + orm.Insert(item); + + var items = orm.Query("select * from onmm3.item_with_long"); + Assert.AreEqual(item.Text, items.Single().Text); + } + + [Test] + public void DeleteWithWhereClause() + { + dynamic item = new Entity("onmm3.item"); + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm3.item")); + + orm.Delete(item, "where id = :0", 1); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm3.item")); + + orm.Delete(item, "where id = :0", 2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm3.item")); + } + + [Test] + public void DeleteWithKeyMetadata() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 2; + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm3.item")); + + orm.Delete(item); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm3.item")); + + //check that the existing element is not the one deleted. + var items = orm.Query("select * from onmm3.item"); + Assert.AreEqual(1, items.Single().Id); + } + + [Test] + public void QueryScalarWhenNoRows() + { + Assert.AreEqual(null, orm.QueryScalar("select 1 from onmm3.item where 1=2")); + } + + [Test] + public void QueryScalarNullable() + { + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + } + + [Test] + public void QueryScalar() + { + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual("1", orm.QueryScalar("select 1 from dual")); + + Assert.AreEqual(DateTime.Today, orm.QueryScalar("select trunc(sysdate) from dual")); + Assert.AreEqual("test", orm.QueryScalar("select 'test' from dual")); + Assert.AreEqual(1.123456789m, orm.QueryScalar("select 1.123456789 from dual")); + } + + [Test] + public void UpdateViaWhereClause() + { + dynamic item = new Entity("onmm3.item"); + item.Name = "RENAMED"; + orm.Update(item, "where id = :0", 1); + + var items = orm.Query("select id, name from onmm3.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void UpdateViaMetaData() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 1; + item.Name = "RENAMED"; + orm.Update(item); + + var items = orm.Query("select id, name from onmm3.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void MergeInto() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 100; + item.Name = "Name100"; + orm.MergeInto(item); + + var itemLoaded = orm.Query("select * from onmm3.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + + item.Name = "renamed"; + orm.MergeInto(item); + + itemLoaded = orm.Query("select * from onmm3.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + } + + [Test] + public void MergeIntoWithOnlyPrimaryKey_ShouldNotTryUpdateAndFail() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 100; + orm.MergeInto(item); + orm.MergeInto(item); + } + + [Test] + public void ColumnWithUnderScoreBeforeDigit() + { + Entity item = orm.Query("select yield_2date from onmm3.item_odd where id = 1").Single(); + + //Assert.AreEqual(99m, item.Yield_2date); + + foreach (var p in item.GetDictionaryInDbStyle(true)) + Assert.AreEqual("yield_2date", p.Key); + + foreach (var p in item.GetDictionary()) + Assert.AreEqual("Yield_2date", p.Key); + } + + + [Test] + public void QueryDynamicToString() + { + var item = orm.Query("select * from onmm3.item where id = 1").Single(); + Assert.That(item.ToString(), Contains.Substring("Id = 1")); + Assert.That(item.ToString(), Contains.Substring("Name = First Item")); + } + + [Test] + public void QuerySingleTypeList() + { + var ints = orm.QuerySingleTypeList("select id from onmm3.item order by id").ToList(); + Assert.AreEqual(1, ints[0]); + Assert.AreEqual(2, ints[1]); + + var strings = orm.QuerySingleTypeList("select name from onmm3.item order by id").ToList(); + Assert.AreEqual("First Item", strings[0]); + Assert.AreEqual("Second Item", strings[1]); + } + + [Test] + public void QueryStaticNoWhereClause() + { + var items = orm.Query("select * from onmm3.item order by id").ToList(); + Assert.AreEqual(2, items.Count); + Assert.AreEqual(1, items[0].Id); + Assert.AreEqual(2, items[1].Id); + Assert.AreEqual("First Item", items[0].Name); + Assert.AreEqual("Second Item", items[1].Name); + Assert.AreEqual(0.321m, items[0].DecimalValue); + Assert.AreEqual(.123m, items[1].DecimalValue); + } + + [Test] + public void QueryDynamicWithWhereClause() + { + var item = orm.Query("select id, name from onmm3.item where id = :0", 1).SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryStaticWithWhereClause() + { + var item = orm.Query("select id, name from onmm3.item where id = :0", 1) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryNonExisting() + { + var item = orm.Query("select id, name from onmm3.item where id = :0", -1) + .SingleOrDefault(); + Assert.IsNull(item); + } + + + [Test] + public void ExecuteFunctionWithInt() + { + var result = orm.ExecuteFunction("onmm3.plus1function", 100); + Assert.AreEqual(101, result); + } + + [Test] + public void ExecuteFunctionWithString() + { + var result = orm.ExecuteFunction("onmm3.append1function", "100"); + Assert.AreEqual("1001", result); + } + + [Test] + public void ExecuteProcedureWithRefCursor() + { + var item = orm.ExecuteProcedure("onmm3.get_items_by_name", "First Item", DbMapperParameter.RefCursor) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void ExecuteProcedureWithRefCursorZeroElements() + { + var items = orm.ExecuteProcedure("onmm3.get_items_by_name", "non existing", DbMapperParameter.RefCursor); + Assert.AreEqual(0, items.Count()); + } + + [Test] + public void ExecuteProcedureWithoutRefCursor() + { + string newName = "RENAMED"; + object result = orm.ExecuteProcedure("onmm3.rename_item", 1, newName); + Assert.IsNull(result); + Assert.AreEqual(newName, orm.QueryScalar("select name from onmm3.item where id = :0", 1)); + } + + [Test] + public void InsertWithImplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm3.item", "id"); + item.id = 1; + + for (int i = 0; i < count; i++) + orm.Insert(item); + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm3.item")); + Console.WriteLine("InsertWithImplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm3.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm3.item")); + Console.WriteLine("InsertWithExplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnectionAndTransaction() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm3.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + using (var tx = connection.BeginTransaction()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + tx.Commit(); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm3.item")); + Console.WriteLine("InsertWithExplicitConnectionAndTransaction Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertAndSelectBigClob() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + //insert + dynamic item = new Entity("onmm3.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm3.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + + [Test] + public void InsertBigClobWithNonQuery() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + orm.NonQuery("insert into onmm3.bigclobtest (text) values (:0)", largeString); + + string largeStringFetched = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + [Test] + public void SelectWithExternalConnection() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + var r = orm.QueryScalar("select 1 from dual"); + Assert.AreEqual(1, r); + } + } + } + + [Test] + public void InsertWithExternalConnectionAndSelectBigClob() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 1); + + //insert + dynamic item = new Entity("onmm3.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm3.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + } + } + + [Test] + public void DeleteWithCompositeKeyt() + { + dynamic item1 = new Entity("onmm3.item_composite_key", "id, type"); + item1.Id = 1; + item1.Type = 1; + orm.Insert(item1); + + dynamic item2 = new Entity("onmm3.item_composite_key", "id,type"); + item2.Id = 1; + item2.Type = 2; + orm.Insert(item2); + + orm.Delete(item1); + var list = orm.Query("select * from onmm3.item_composite_key"); + Assert.AreEqual(2, list.Single().Type); + + orm.Delete(item2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm3.item_composite_key")); + } + + [Test] + public void OverflowDynamic() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void Overflow() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void LoadStaticShouldWarn() + { + DbMapper.PrintWarnings = true; + DbMapper.PrintSqls = true; + var item = orm.Query("select id, name, decimal_value, date_value from onmm3.item where id = :0", 1).SingleOrDefault(); + DbMapper.PrintWarnings = false; + DbMapper.PrintSqls = false; + } + + [Test] + public void CollectionShouldNotBeUsed() + { + ItemWithCollection item = new ItemWithCollection(); + item.Id = 3; + item.GroupsNotPresentInDb = new List() { 1 }; + orm.Insert(item, "onmm3.item"); + } + + [Test] + public void ConnectAsSys() + { + var orm = new DbMapper(); + orm.ConnectAsSys("tstdaily", "bi"); + orm.QueryScalar("select 1 from dual"); + } + + [TestCase("1\n2")] + [TestCase("1\r\n2")] + [TestCase("1\n\r2")] + [TestCase("1\r2")] + public void ClobTests(string testValue) + { + dynamic item = new Entity("onmm3.bigclobtest"); + item.Text = testValue; + orm.Insert(item); + + var fromDb = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(testValue, fromDb); + } + + [Test] + public void NonQueryIgnoreErrorShouldWork() + { + orm.NonQueryIgnoreError("should not work"); + } + + [Test] + public void ToEntityShouldIgnoreListProperties() + { + var entity = new SomeClass() + .ToEntity(); + + var dic = entity.GetDictionary(); + + Assert.AreEqual(4, dic.Count); + Assert.IsTrue(dic.ContainsKey("Prop1")); + Assert.IsTrue(dic.ContainsKey("Prop2")); + Assert.IsTrue(dic.ContainsKey("Prop3")); + Assert.IsTrue(dic.ContainsKey("Prop4")); + + } + } + + class ProcParameters + { + public decimal Input { get; set; } + public decimal EchoOutput { get; set; } + public decimal CountOutput { get; set; } + public object FundsOutput { get; set; } + } + + public class Item + { + public int Id { get; set; } + public string Name { get; set; } + public decimal DecimalValue { get; set; } + public DateTime? DateValue { get; set; } + } + + public class ItemWithCollection + { + public int Id { get; set; } + public List GroupsNotPresentInDb { get; set; } + } + + public class ItemWrongDefinition + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Decimalvalue { get; set; } + public DateTime? Datevalue { get; set; } + } + + class SomeClass + { + public string Prop1 { get; set; } + public decimal Prop2 { get; set; } + public DateTime Prop3 { get; set; } + public int? Prop4 { get; set; } + public List Col1 { get; set; } + public String[] Col2 { get; set; } + public Dictionary Col3 { get; set; } + } +} diff --git a/TestManagedCore/TestBase.cs b/TestManagedCore/TestBase.cs new file mode 100644 index 0000000..5cec4fc --- /dev/null +++ b/TestManagedCore/TestBase.cs @@ -0,0 +1,124 @@ +using System; +using NUnit.Framework; +using OdpNetMicroMapper; + +namespace TestManagedCore +{ + [TestFixture] + public class TestBase + { + static bool firstRun = true; + protected DbMapper orm = new DbMapper(); + protected DbMapper sysOrm = new DbMapper(); + + private void CreateUser() + { + if (!firstRun) + return; + + firstRun = false; + + sysOrm.NonQueryIgnoreError("drop user onmm3 cascade"); + sysOrm.NonQuery("create user onmm3 identified by onmm3"); + sysOrm.NonQuery("grant create session to onmm3"); + sysOrm.NonQuery("alter user onmm3 quota unlimited on users"); + Console.WriteLine("user onmm3 created"); + + var sql = "create table onmm3.bigclobtest (text clob)"; + sysOrm.NonQuery(sql); + + sql = "create table onmm3.rawtest (bytes raw(16))"; + sysOrm.NonQuery(sql); + + sql = "create table onmm3.item_with_long (text long)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm3.item_odd (id number(10) not null, yield_2date number)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm3.item_composite_key (id number(10) not null, type number(10) not null, text varchar2(100))"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm3.item (id number(10) not null, name varchar2(100), decimal_value number, date_value timestamp default sysdate not null)"; + sysOrm.NonQuery(sql); + + sql = @"create sequence onmm3.seq_items start with 3"; + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm3.append1function (pString varchar2) return varchar2 + as + begin + return pString || '1'; + end;"; + //todo fix elsewhere? //http://boncode.blogspot.com/2009/03/oracle-pls-00103-encountered-symbol.html + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm3.plus1function (pNumber number) return integer + as + begin + return pNumber + 1; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm3.get_items_by_name(pName in varchar2, io_cursor out SYS_REFCURSOR) is + begin + open io_cursor for + select * from onmm3.item t where t.name = pName; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm3.rename_item(pId number, pName in varchar2) is + begin + update onmm3.item t set t.name = pName where t.id = pId; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm3.proc_with_many_out_parameters(pInput number, pEcho out number, pCount out number, io_cursor out SYS_REFCURSOR) is + begin + select pInput into pEcho from dual; + select count(1) into pCount from onmm3.fund; + open io_cursor for + select * from onmm3.fund; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + } + + [TearDown] + public void DropSchema() + { + } + + [SetUp] + public void CreateSchema() + { + //throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); + + sysOrm.ConnectAsSys("INTSHARE", "bi"); + orm.ConnectionString = "data source=INTSHARE;user id=onmm3;password=onmm3;"; + + CreateUser(); + + sysOrm.NonQuery("delete from onmm3.item_composite_key"); + sysOrm.NonQuery("delete from onmm3.item"); + sysOrm.NonQuery("delete from onmm3.bigclobtest"); + sysOrm.NonQuery("delete from onmm3.item_with_long"); + sysOrm.NonQuery("delete from onmm3.item_odd"); + sysOrm.NonQuery("delete from onmm3.rawtest"); + + var sql = "insert into onmm3.item_odd (id, yield_2date) values (1, 99)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm3.item (id, name, decimal_value) values (1, 'First Item', 0.321)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm3.item (id, name, decimal_value) values (2, 'Second Item', 0.123)"; + sysOrm.NonQuery(sql); + + } + } +} diff --git a/TestManagedCore/TestManagedCore.csproj b/TestManagedCore/TestManagedCore.csproj new file mode 100644 index 0000000..97e9e26 --- /dev/null +++ b/TestManagedCore/TestManagedCore.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp2.0 + + + + + + + + + + + + + + + Always + + + Always + + + + diff --git a/TestManagedCore/sqlnet.ora b/TestManagedCore/sqlnet.ora new file mode 100644 index 0000000..858fe0d --- /dev/null +++ b/TestManagedCore/sqlnet.ora @@ -0,0 +1,5 @@ +# This file is actually generated by netca. But if customers choose to +# install "Software Only", this file wont exist and without the native +# authentication, they will not be able to connect to the database on NT. + +SQLNET.AUTHENTICATION_SERVICES = (NTS) diff --git a/TestManagedCore/tnsnames.ora b/TestManagedCore/tnsnames.ora new file mode 100644 index 0000000..a3d1a93 --- /dev/null +++ b/TestManagedCore/tnsnames.ora @@ -0,0 +1,149 @@ +# ____ ____ ___ ____ _ _ _ __ _____ ___ ___ _ _ ____ _ __ _ ___ _____ _ _ _____ _____ ____ +# | _ \ | _ \ / _ \ | _ \ | | | || |/ /|_ _||_ _|/ _ \ | \ | |/ ___| | |/ /| | |_ _|| ____|| \ | ||_ _|| ____|| _ \ +# | |_) || |_) || | | || | | || | | || ' / | | | || | | || \| |\___ \ | ' / | | | | | _| | \| | | | | _| | |_) | +# | __/ | _ < | |_| || |_| || |_| || . \ | | | || |_| || |\ | ___) | | . \ | |___ | | | |___ | |\ | | | | |___ | _ < +# |_| |_| \_\ \___/ |____/ \___/ |_|\_\ |_| |___|\___/ |_| \_||____/ |_|\_\|_____||___||_____||_| \_| |_| |_____||_| \_\ +# + + +AUDDB = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = AUDDB)) + ) + +CI = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA01.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = ORCL)) + ) + +INV = + (DESCRIPTION = + (ADDRESS = (PROTOCOL=TCP) (HOST=bidbs2003) (PORT=1521)) + (CONNECT_DATA = (SERVICE_NAME=invision)) + ) + +INVISION = + (DESCRIPTION = + (ADDRESS = (PROTOCOL=TCP) (HOST=bidbs2003) (PORT=1521)) + (CONNECT_DATA = (SERVICE_NAME=invision)) + ) + +PMFORUM = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA01.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = ORCL)) + ) + +ORA_ALIAS = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA01.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = ORCL)) + ) + +#Herunder er der kun testinstanser + +INV_TEST = (DESCRIPTION = + (ADDRESS_LIST = + (ADDRESS = (PROTOCOL = TCP)(HOST = bidbs2004.bi.local)(PORT = 1521)) + ) + (CONNECT_DATA = + (SERVICE_NAME = Invision) + ) + ) + +CIUDV = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = CIUDV)) + ) + +TEST = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TEST)) + ) + +TSTDAILY = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TSTDAILY)) + ) + +FPDTEST = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = FPDTEST)) + ) + +SUPDAILY = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = SUPDAILY)) + ) + +INTFID = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = INTFID)) + ) + +INTSHARE = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = INTSHARE)) + ) + +DEVSHARE = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = CIUDV)) + ) + +INTFMS = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = FMSREF)) + ) + +TSTWEEK = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TEST)) + ) + +BIZTEST = + (DESCRIPTION = + (ADDRESS_LIST = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + ) + (CONNECT_DATA = + (SERVER = DEDICATED) + (SERVICE_NAME = BIZTEST)) + ) + +INVWEEK = + (DESCRIPTION= + (ADDRESS= (PROTOCOL=TCP) (HOST=bidbs2004) (PORT=1521)) + (CONNECT_DATA =(SERVICE_NAME=week)) + ) + +INVUDV = + (DESCRIPTION= + (ADDRESS= (PROTOCOL=TCP) (HOST=bidbs2004) (PORT=1521)) + (CONNECT_DATA =(SERVICE_NAME=udv)) + ) + +INVPRJKT = + (DESCRIPTION= + (ADDRESS = (PROTOCOL = TCP)(HOST=bidbs2004.bi.local)(PORT=1521)) + (CONNECT_DATA= + (SERVICE_NAME=INVPRJKT)) + ) + +TSTPRJKT = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TSTPRJKT)) + ) \ No newline at end of file diff --git a/Tests/App.config b/Tests/App.config index 05a3c21..9a12e04 100644 --- a/Tests/App.config +++ b/Tests/App.config @@ -1,5 +1,8 @@  + + + diff --git a/Tests/Properties/AssemblyInfo.cs b/Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index ad159e9..0000000 --- a/Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Test")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Test")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("039db2c1-89f6-47d8-855d-a472a29ddf76")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/Test.cs b/Tests/Test.cs index acfdfb9..64f889b 100644 --- a/Tests/Test.cs +++ b/Tests/Test.cs @@ -30,7 +30,7 @@ public void RawTest() { var guid = new Guid(); orm.NonQuery("insert into onmm.rawtest (bytes) values (:0)", guid.ToByteArray()); - + var bytesLoaded = orm.QueryScalar("select bytes from onmm.rawtest where rownum = 1"); var guidLoaded = new Guid(bytesLoaded); Assert.AreEqual(guid, guidLoaded); @@ -48,7 +48,7 @@ public void InsertDynamicTest(string type) item.Id = 3; item.Name = "Third Item"; item.DateValue = DateTime.Today; - if (type=="double") item.DecimalValue = 3.1415d; + if (type == "double") item.DecimalValue = 3.1415d; if (type == "float") item.DecimalValue = 3.1415f; if (type == "decimal") item.DecimalValue = 3.1415m; @@ -80,7 +80,7 @@ public void UpdateDynamicWithNull() public void InsertWithLongColumnMax4000chars() { dynamic item = new Entity("onmm.item_with_long"); - item.Text = new string('X', 1000 * 4 ); //max 4k when inserting! + item.Text = new string('X', 1000 * 4); //max 4k when inserting! orm.Insert(item); var items = orm.Query("select * from onmm.item_with_long"); @@ -92,7 +92,7 @@ public void DeleteWithWhereClause() { dynamic item = new Entity("onmm.item"); Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm.item")); - + orm.Delete(item, "where id = :0", 1); Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm.item")); @@ -216,8 +216,8 @@ public void ColumnWithUnderScoreBeforeDigit() public void QueryDynamicToString() { var item = orm.Query("select * from onmm.item where id = 1").Single(); - Assert.That(item.ToString(), Is.StringContaining("Id = 1")); - Assert.That(item.ToString(), Is.StringContaining("Name = First Item")); + Assert.That(item.ToString(), Contains.Substring("Id = 1")); + Assert.That(item.ToString(), Contains.Substring("Name = First Item")); } [Test] @@ -280,7 +280,7 @@ public void ExecuteFunctionWithInt() [Test] public void ExecuteFunctionWithString() - { + { var result = orm.ExecuteFunction("onmm.append1function", "100"); Assert.AreEqual("1001", result); } @@ -373,7 +373,7 @@ public void InsertWithExplicitConnectionAndTransaction() public void InsertAndSelectBigClob() { //this is at least 5 MB - string largeString = new string ('X', 1024*1024*5); + string largeString = new string('X', 1024 * 1024 * 5); //insert dynamic item = new Entity("onmm.bigclobtest"); @@ -497,7 +497,7 @@ public void CollectionShouldNotBeUsed() { ItemWithCollection item = new ItemWithCollection(); item.Id = 3; - item.GroupsNotPresentInDb = new List() { 1 }; + item.GroupsNotPresentInDb = new List() { 1 }; orm.Insert(item, "onmm.item"); } diff --git a/Tests/TestBase.cs b/Tests/TestBase.cs index 3ffe646..66ea049 100644 --- a/Tests/TestBase.cs +++ b/Tests/TestBase.cs @@ -96,10 +96,10 @@ public void DropSchema() [SetUp] public void CreateSchema() { - throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); +// throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); - sysOrm.ConnectAsSys("tns", "password"); - orm.ConnectionString = "data source=tns;user id=onmm;password=onmm;"; + sysOrm.ConnectAsSys("intshare", "bi"); + orm.ConnectionString = "data source=intshare;user id=onmm;password=onmm;"; CreateUser(); diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 82910ec..8a39e2b 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,84 +1,27 @@ - - + + - Debug - AnyCPU - 8.0.30703 - 2.0 - {CC7CDBB4-E507-4558-A7DE-9800D19624D7} - Library - Properties - Test - Test - v4.0 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + net461 + - - ..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll - - - ..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll - - - False - False - - - ..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll - - - - - - - - + + + - - - - - + + - - Designer - - - Designer - + + ..\..\..\Oracle\Product\11.2.0.3.0_32\odp.net\bin\2.x\Oracle.DataAccess.dll + + - - {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F} - OdpNetMicroMapper - + - - - \ No newline at end of file + + + diff --git a/Tests/packages.config b/Tests/packages.config deleted file mode 100644 index 0c82178..0000000 --- a/Tests/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/repositories.config b/packages/repositories.config deleted file mode 100644 index fa38fcf..0000000 --- a/packages/repositories.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From 3df9964d81e56cbc9eab5febbf50d5390b3cee84 Mon Sep 17 00:00:00 2001 From: Michael Hjorth Date: Wed, 28 Mar 2018 13:36:42 +0200 Subject: [PATCH 03/10] Code standards --- OdpNetMicroMapper/Connection.cs | 25 +++++------ OdpNetMicroMapper/DbMapper.cs | 63 +++++++++++++-------------- OdpNetMicroMapper/Entity.cs | 38 ++++++++-------- OdpNetMicroMapper/ObjectExtensions.cs | 27 ++++++------ OdpNetMicroMapper/SqlTokens.cs | 22 +++++----- 5 files changed, 85 insertions(+), 90 deletions(-) diff --git a/OdpNetMicroMapper/Connection.cs b/OdpNetMicroMapper/Connection.cs index 45ae4b7..79f1cb8 100644 --- a/OdpNetMicroMapper/Connection.cs +++ b/OdpNetMicroMapper/Connection.cs @@ -5,41 +5,40 @@ namespace OdpNetMicroMapper { public class Connection : IDisposable { - int level = 0; - IDbConnection connection; - DbMapper orm; + private int _level; + private readonly IDbConnection _connection; + private readonly DbMapper _orm; public IDbConnection GetAdoConnection() { - return connection; + return _connection; } public Connection(DbMapper orm, IDbConnection connection) { - this.orm = orm; - this.connection = connection; + _orm = orm; + _connection = connection; } public void NextLevel() { - level++; + _level++; } public IDbTransaction BeginTransaction() { - return connection.BeginTransaction(); + return _connection.BeginTransaction(); } - public void Dispose() { - if (level == 0) + if (_level == 0) { - connection.Dispose(); - orm.ReleaseConnection(); + _connection.Dispose(); + _orm.ReleaseConnection(); } else { - level--; + _level--; } } } diff --git a/OdpNetMicroMapper/DbMapper.cs b/OdpNetMicroMapper/DbMapper.cs index c689b25..9cb3c02 100644 --- a/OdpNetMicroMapper/DbMapper.cs +++ b/OdpNetMicroMapper/DbMapper.cs @@ -114,8 +114,8 @@ public IDbDataAdapter CreateOracleDataAdapter() public object CreateClob(string text, IDbConnection connection) { - object clob = Activator.CreateInstance(_oracleClobType, new object[] { connection }); - MethodInfo method = _oracleClobType.GetMethod("Append", new Type[] { typeof(char[]), typeof(int), typeof(int) }); + var clob = Activator.CreateInstance(_oracleClobType, connection); + var method = _oracleClobType.GetMethod("Append", new[] { typeof(char[]), typeof(int), typeof(int) }); method.Invoke(clob, new object[] { text.ToCharArray(), 0, text.Length }); return clob; } @@ -140,7 +140,7 @@ public IDbCommand CreateCommand(string sql, IDbConnection connection, bool bindB return cmd; } - private void SetParameter(IDbDataParameter parameter, object value, string columnName, IDbCommand cmd, Type type = null) + private void SetParameter(IDbDataParameter parameter, object value, IDbCommand cmd, Type type = null) { if (value == null) { @@ -201,22 +201,22 @@ private void SetParameter(IDbDataParameter parameter, object value, string colum public void Insert(object item, string dbName) { - Entity entity = item.ToEntity(); + var entity = item.ToEntity(); entity.TableName = dbName; Insert(entity); } public void Insert(Entity item) { - SqlTokens sqlTokens = new SqlTokens(item.GetDictionaryInDbStyle(true)); - string sql = "insert into " + item.TableName + var sqlTokens = new SqlTokens(item.GetDictionaryInDbStyle(true)); + var sql = "insert into " + item.TableName + " (" + sqlTokens.AsColumnNames(false) + ") select " + sqlTokens.AsIndcies(false) + " from dual"; using (var connection = CreateOrReuseConnection()) { using (var command = CreateCommand(sql, connection.GetAdoConnection())) { - AddParameters(command, sqlTokens.GetNonNullableFieldsAndValues(), connection.GetAdoConnection(), 0); + AddParameters(command, sqlTokens.GetNonNullableFieldsAndValues(), 0); command.ExecuteNonQuery(); } } @@ -226,11 +226,11 @@ public void MergeInto(Entity item) { var dic = item.GetWhereClauseOnPrimaryKeyDbStyle(); var args = dic.Values.ToArray(); - string whereClause = new SqlTokens(dic).AsWhereClause(); + var whereClause = new SqlTokens(dic).AsWhereClause(); - string sql = "select count(1) from " + item.TableName + " " + whereClause; + var sql = "select count(1) from " + item.TableName + " " + whereClause; - int count = QueryScalar(sql, args); + var count = QueryScalar(sql, args); if (count == 0) Insert(item); @@ -240,13 +240,13 @@ public void MergeInto(Entity item) private void AddParameters(IDbCommand cmd, object[] args) { - AddParameters(cmd, args.ToDictionary(x => Guid.NewGuid().ToString(), x => x), cmd.Connection, 0); + AddParameters(cmd, args.ToDictionary(x => Guid.NewGuid().ToString(), x => x), 0); } - private void AddParameters(IDbCommand cmd, IDictionary columnsAndValues, IDbConnection connection, int offset, bool useKeyNames = false) + private void AddParameters(IDbCommand cmd, IDictionary columnsAndValues, int offset, bool useKeyNames = false) { - int index = offset; - foreach (KeyValuePair o in columnsAndValues) + var index = offset; + foreach (var o in columnsAndValues) { var parameter = cmd.CreateParameter(); if (useKeyNames) @@ -255,7 +255,7 @@ private void AddParameters(IDbCommand cmd, IDictionary columnsAn parameter.ParameterName = index.ToString(); if (o.Key.EndsWith("Output")) parameter.Direction = ParameterDirection.Output; - SetParameter(parameter, o.Value, o.Key, cmd); + SetParameter(parameter, o.Value, cmd); cmd.Parameters.Add(parameter); index++; } @@ -267,12 +267,12 @@ public void Delete(Entity item, string whereClause = null, params object[] args) if (whereClause == null) { var dic = item.GetWhereClauseOnPrimaryKeyDbStyle(); - SqlTokens sqlTokens = new SqlTokens(dic); + var sqlTokens = new SqlTokens(dic); whereClause = sqlTokens.AsWhereClause(); args = dic.Values.ToArray(); } - string sql = "delete from " + item.TableName + " " + whereClause; + var sql = "delete from " + item.TableName + " " + whereClause; using (var connection = CreateOrReuseConnection()) { @@ -299,9 +299,9 @@ public void Update(Entity item, string whereClause = null, params object[] args) if (nonPrimaryKeysColumns.Count == 0) return; - SqlTokens setClauseTokens = new SqlTokens(nonPrimaryKeysColumns); + var setClauseTokens = new SqlTokens(nonPrimaryKeysColumns); - string sql = "update " + item.TableName + var sql = "update " + item.TableName + " " + setClauseTokens.AsSetClause(args.Length) + " " + whereClause; @@ -310,7 +310,7 @@ public void Update(Entity item, string whereClause = null, params object[] args) using (var command = CreateCommand(sql, connection.GetAdoConnection())) { AddParameters(command, args); - AddParameters(command, item.GetDictionaryInDbStyle(false), null, args.Length); + AddParameters(command, item.GetDictionaryInDbStyle(false), args.Length); command.ExecuteNonQuery(); } } @@ -323,11 +323,11 @@ public T QueryScalar(string sql, params object[] args) using (var command = CreateCommand(sql, connection.GetAdoConnection())) { AddParameters(command, args); - object result = command.ExecuteScalar(); + var result = command.ExecuteScalar(); - bool isString = typeof(T) == typeof(string); - bool isNullableType = typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition(); + var isString = typeof(T) == typeof(string); + var isNullableType = typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition(); //NULL handle if (isNullableType || isString) @@ -340,7 +340,7 @@ public T QueryScalar(string sql, params object[] args) if (isNullableType) { - Type type = Nullable.GetUnderlyingType(typeof(T)); + var type = Nullable.GetUnderlyingType(typeof(T)); return (T)Convert.ChangeType(result, type); } @@ -389,7 +389,7 @@ public void NonQuery(string sql, params object[] args) { AddParameters(command, args); - using (IDataReader reader = command.ExecuteReader()) + using (var reader = command.ExecuteReader()) { while (reader.Read()) { @@ -408,7 +408,7 @@ public IEnumerable Query(string sql, params object[] args) { AddParameters(command, args); - using (IDataReader reader = command.ExecuteReader()) + using (var reader = command.ExecuteReader()) { while (reader.Read()) { @@ -427,7 +427,7 @@ public IEnumerable QuerySingleTypeList(string sql, params object[] args) { AddParameters(command, args); - using (IDataReader reader = command.ExecuteReader()) + using (var reader = command.ExecuteReader()) { while (reader.Read()) { @@ -450,13 +450,12 @@ public T ExecuteFunction(string functionName, params object[] args) { using (var cmd = CreateCommand(null, connection.GetAdoConnection(), false)) { - DataSet ds = new DataSet(); cmd.CommandText = functionName; cmd.CommandType = CommandType.StoredProcedure; //return value var parameter = cmd.CreateParameter(); - SetParameter(parameter, default(T), "dummy", cmd, typeof(T)); + SetParameter(parameter, default(T), cmd, typeof(T)); parameter.Direction = ParameterDirection.ReturnValue; cmd.Parameters.Add(parameter); @@ -474,20 +473,20 @@ public IEnumerable ExecuteProcedure(string procedureName, params object { using (var command = CreateCommand(null, connection.GetAdoConnection(), false)) { - DataSet ds = new DataSet(); + var ds = new DataSet(); command.CommandText = procedureName; command.CommandType = CommandType.StoredProcedure; AddParameters(command, args); //Execute - IDbDataAdapter da = CreateOracleDataAdapter(); + var da = CreateOracleDataAdapter(); da.SelectCommand = command; da.Fill(ds); if (ds.Tables.Count < 1) return null; - List list = new List(); + var list = new List(); foreach (DataRow row in ds.Tables[0].Rows) list.Add(row.ToEntity()); diff --git a/OdpNetMicroMapper/Entity.cs b/OdpNetMicroMapper/Entity.cs index b4d9c31..b923b80 100644 --- a/OdpNetMicroMapper/Entity.cs +++ b/OdpNetMicroMapper/Entity.cs @@ -7,40 +7,40 @@ namespace OdpNetMicroMapper { public class Entity : DynamicObject { - string dbPrimaryKey; + private readonly string _dbPrimaryKey; public string TableName { get; set; } - Dictionary dictionary = new Dictionary(); + private readonly Dictionary _dictionary = new Dictionary(); public Entity(Dictionary dictionary) { - this.dictionary = dictionary; + _dictionary = dictionary; } public Entity(string tableName, string dbPrimaryKey = null) { TableName = tableName; - this.dbPrimaryKey = dbPrimaryKey; + _dbPrimaryKey = dbPrimaryKey; } private bool IsPrimaryKey(string propertyName) { - if (dbPrimaryKey == null) + if (_dbPrimaryKey == null) return false; - return dbPrimaryKey.Split(new char[] { ',' }) + return _dbPrimaryKey.Split(new[] { ',' }) .Select(x => x.Trim()) .Contains(propertyName); } public IDictionary GetDictionary() { - return dictionary; + return _dictionary; } public Dictionary GetDictionaryInDbStyle(bool includePrimaryKey) { - Dictionary dic = new Dictionary(); - foreach (KeyValuePair element in dictionary) + var dic = new Dictionary(); + foreach (var element in _dictionary) if (includePrimaryKey || !IsPrimaryKey(element.Key.UnCammelCase())) dic.Add(element.Key.UnCammelCase(), element.Value); return dic; @@ -48,38 +48,38 @@ public Dictionary GetDictionaryInDbStyle(bool includePrimaryKey) public Dictionary GetWhereClauseOnPrimaryKeyDbStyle() { - if (dbPrimaryKey == null) + if (_dbPrimaryKey == null) throw new ApplicationException("Primary key is not set"); var dic = new Dictionary(); - foreach (string column in dbPrimaryKey.Split(new char[] { ',' })) + foreach (var column in _dbPrimaryKey.Split(new[] { ',' })) { - string domainProperty = column.Trim().CammelCase(); - if (!dictionary.ContainsKey(domainProperty)) + var domainProperty = column.Trim().CammelCase(); + if (!_dictionary.ContainsKey(domainProperty)) throw new ApplicationException("Property '" + domainProperty + "' was not found"); - dic.Add(column, dictionary[domainProperty]); + dic.Add(column, _dictionary[domainProperty]); } return dic; } public override bool TrySetMember(SetMemberBinder binder, object value) { - dictionary[binder.Name] = value; + _dictionary[binder.Name] = value; return true; } public override bool TryGetMember(GetMemberBinder binder, out object result) { - return dictionary.TryGetValue(binder.Name, out result); + return _dictionary.TryGetValue(binder.Name, out result); } public override string ToString() { - string s = ""; + var s = ""; - foreach (KeyValuePair element in dictionary) - s += element.Key + " = "+ element.Value + "\n"; + foreach (var element in _dictionary) + s += element.Key + " = " + element.Value + "\n"; return s; } diff --git a/OdpNetMicroMapper/ObjectExtensions.cs b/OdpNetMicroMapper/ObjectExtensions.cs index 40ee67f..aefd6e2 100644 --- a/OdpNetMicroMapper/ObjectExtensions.cs +++ b/OdpNetMicroMapper/ObjectExtensions.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Data; -using System.Reflection; using System.Collections; namespace OdpNetMicroMapper @@ -18,13 +17,13 @@ public static string RemoveInEnd(this string text, string search) public static string UnCammelCase(this string name) { - string s = ""; + var s = ""; - foreach (char c in name) + foreach (var c in name) { if (char.IsUpper(c) && s.Length>0) s += "_"; - s += Char.ToLower(c); + s += char.ToLower(c); } return s; @@ -44,12 +43,12 @@ private static bool IsUnderscoreAndIsReverseable(string columnName, int index) public static string CammelCase(this string name) { - string s = ""; - bool nextIsToUpper = true; + var s = ""; + var nextIsToUpper = true; - for (int i = 0; i < name.Length; i++) + for (var i = 0; i < name.Length; i++) { - char c = name[i]; + var c = name[i]; if (IsUnderscoreAndIsReverseable(name, i)) { @@ -97,7 +96,7 @@ public static Entity ToEntity(this object o) public static Entity ToEntity(this IDataReader rdr) { var dic = new Dictionary(); - for (int i = 0; i < rdr.FieldCount; i++) + for (var i = 0; i < rdr.FieldCount; i++) { try { @@ -121,19 +120,19 @@ public static Entity ToEntity(this DataRow row) public static T ToObject(this IDataReader rdr) where T : new() { - T o = new T(); - for (int i = 0; i < rdr.FieldCount; i++) + var o = new T(); + for (var i = 0; i < rdr.FieldCount; i++) { var name = rdr.GetName(i).CammelCase(); - PropertyInfo pi = o.GetType().GetProperty(name); + var pi = o.GetType().GetProperty(name); if (pi != null) { try { - Type type = Nullable.GetUnderlyingType(pi.PropertyType) + var type = Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType; - object value = DBNull.Value.Equals(rdr[i]) ? null : + var value = DBNull.Value.Equals(rdr[i]) ? null : Convert.ChangeType(rdr[i], type); pi.SetValue(o, value, null); diff --git a/OdpNetMicroMapper/SqlTokens.cs b/OdpNetMicroMapper/SqlTokens.cs index 5b19c6f..576b23f 100644 --- a/OdpNetMicroMapper/SqlTokens.cs +++ b/OdpNetMicroMapper/SqlTokens.cs @@ -3,25 +3,25 @@ namespace OdpNetMicroMapper { - class SqlTokens + public class SqlTokens { - IDictionary dic = new Dictionary(); + private readonly IDictionary _dic; public SqlTokens(IDictionary dic) { - this.dic = dic; + _dic = dic; } public IDictionary GetNonNullableFieldsAndValues() { - return dic.Where(x => x.Value != null) + return _dic.Where(x => x.Value != null) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } public string AsWhereClause(int index = 0) { string sql = ""; - foreach (string token in dic.Keys) + foreach (string token in _dic.Keys) { sql += token + "=:" + index + " and "; index++; @@ -32,14 +32,12 @@ public string AsWhereClause(int index = 0) public string AsColumnNames(bool includeNullFields = true) { - int index = 0; string sql = ""; - foreach (string token in dic.Keys) + foreach (string token in _dic.Keys) { - if (dic[token] != null || includeNullFields) + if (_dic[token] != null || includeNullFields) { sql += token.UnCammelCase() + ", "; - index++; } } sql = sql.RemoveInEnd(", "); @@ -50,9 +48,9 @@ public string AsIndcies(bool includeNullFields = true) { int index = 0; string sql = ""; - foreach (string token in dic.Keys) + foreach (string token in _dic.Keys) { - if (dic[token] != null || includeNullFields) + if (_dic[token] != null || includeNullFields) { sql += ":" + index + ", "; index++; @@ -65,7 +63,7 @@ public string AsIndcies(bool includeNullFields = true) public string AsSetClause(int index = 0) { string sql = "set "; - foreach (string token in dic.Keys) + foreach (string token in _dic.Keys) { sql += token + "=:" + index + ", "; index++; From 80393cc2e6a97a760dc24ec1de6edbdbcdb7f0f9 Mon Sep 17 00:00:00 2001 From: MHJ Date: Wed, 28 Mar 2018 13:57:04 +0000 Subject: [PATCH 04/10] Version 2.0 git-svn-id: http://svn/svn/IT_Udvikling/Tools/OdpNetMicroMapper/trunc@77635 9010fbff-3bac-c549-a0fa-7ef7ff2f0957 --- OdpNetMicroMapper.sln | 43 ++ OdpNetMicroMapper/Connection.cs | 45 ++ OdpNetMicroMapper/Entity.cs | 87 +++ OdpNetMicroMapper/ObjectExtensions.cs | 159 ++++++ OdpNetMicroMapper/OdpNetMicroMapper.csproj | 34 ++ OdpNetMicroMapper/SqlTokens.cs | 75 +++ OdpNetMicroMapper/readme.txt | 5 + TestManaged/App.config | 33 ++ TestManaged/Test.cs | 589 +++++++++++++++++++++ TestManaged/TestBase.cs | 124 +++++ TestManaged/TestManaged.csproj | 17 + TestManagedCore/App.config | 33 ++ TestManagedCore/Test.cs | 589 +++++++++++++++++++++ TestManagedCore/TestBase.cs | 124 +++++ TestManagedCore/TestManagedCore.csproj | 26 + TestManagedCore/sqlnet.ora | 5 + TestManagedCore/tnsnames.ora | 149 ++++++ Tests/App.config | 14 + Tests/CammelCaseTest.cs | 36 ++ Tests/ReferencesTest.cs | 27 + Tests/Test.cs | 589 +++++++++++++++++++++ Tests/TestBase.cs | 124 +++++ Tests/Tests.csproj | 27 + changeLog.txt | 0 24 files changed, 2954 insertions(+) create mode 100644 OdpNetMicroMapper.sln create mode 100644 OdpNetMicroMapper/Connection.cs create mode 100644 OdpNetMicroMapper/Entity.cs create mode 100644 OdpNetMicroMapper/ObjectExtensions.cs create mode 100644 OdpNetMicroMapper/OdpNetMicroMapper.csproj create mode 100644 OdpNetMicroMapper/SqlTokens.cs create mode 100644 OdpNetMicroMapper/readme.txt create mode 100644 TestManaged/App.config create mode 100644 TestManaged/Test.cs create mode 100644 TestManaged/TestBase.cs create mode 100644 TestManaged/TestManaged.csproj create mode 100644 TestManagedCore/App.config create mode 100644 TestManagedCore/Test.cs create mode 100644 TestManagedCore/TestBase.cs create mode 100644 TestManagedCore/TestManagedCore.csproj create mode 100644 TestManagedCore/sqlnet.ora create mode 100644 TestManagedCore/tnsnames.ora create mode 100644 Tests/App.config create mode 100644 Tests/CammelCaseTest.cs create mode 100644 Tests/ReferencesTest.cs create mode 100644 Tests/Test.cs create mode 100644 Tests/TestBase.cs create mode 100644 Tests/Tests.csproj create mode 100644 changeLog.txt diff --git a/OdpNetMicroMapper.sln b/OdpNetMicroMapper.sln new file mode 100644 index 0000000..99699dc --- /dev/null +++ b/OdpNetMicroMapper.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2010 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdpNetMicroMapper", "OdpNetMicroMapper\OdpNetMicroMapper.csproj", "{A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{CC7CDBB4-E507-4558-A7DE-9800D19624D7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestManagedCore", "TestManagedCore\TestManagedCore.csproj", "{E11DFADB-53F1-463A-AE18-5BC65C8EAD26}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestManaged", "TestManaged\TestManaged.csproj", "{A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A18AC73A-2B4B-4D66-BF15-1094CBEDCC1F}.Release|Any CPU.Build.0 = Release|Any CPU + {CC7CDBB4-E507-4558-A7DE-9800D19624D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC7CDBB4-E507-4558-A7DE-9800D19624D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC7CDBB4-E507-4558-A7DE-9800D19624D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC7CDBB4-E507-4558-A7DE-9800D19624D7}.Release|Any CPU.Build.0 = Release|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E11DFADB-53F1-463A-AE18-5BC65C8EAD26}.Release|Any CPU.Build.0 = Release|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7BDAEAC-FB26-4BCA-AF89-A4C14C0E3A9B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B4FE17EF-B531-4D60-B63F-F4980F422F8D} + EndGlobalSection +EndGlobal diff --git a/OdpNetMicroMapper/Connection.cs b/OdpNetMicroMapper/Connection.cs new file mode 100644 index 0000000..06ac464 --- /dev/null +++ b/OdpNetMicroMapper/Connection.cs @@ -0,0 +1,45 @@ +using System; +using System.Data; + +namespace OdpNetMicroMapper +{ + public class Connection : IDisposable + { + private int _level; + private readonly IDbConnection _connection; + private readonly DbMapper _orm; + public IDbConnection GetAdoConnection() + { + return _connection; + } + + public Connection(DbMapper orm, IDbConnection connection) + { + _orm = orm; + _connection = connection; + } + + public void NextLevel() + { + _level++; + } + + public IDbTransaction BeginTransaction() + { + return _connection.BeginTransaction(); + } + + public void Dispose() + { + if (_level == 0) + { + _connection.Dispose(); + _orm.ReleaseConnection(); + } + else + { + _level--; + } + } + } +} diff --git a/OdpNetMicroMapper/Entity.cs b/OdpNetMicroMapper/Entity.cs new file mode 100644 index 0000000..e13bcc7 --- /dev/null +++ b/OdpNetMicroMapper/Entity.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Dynamic; + +namespace OdpNetMicroMapper +{ + public class Entity : DynamicObject + { + private readonly string _dbPrimaryKey; + public string TableName { get; set; } + private readonly Dictionary _dictionary = new Dictionary(); + + public Entity(Dictionary dictionary) + { + _dictionary = dictionary; + } + + public Entity(string tableName, string dbPrimaryKey = null) + { + TableName = tableName; + _dbPrimaryKey = dbPrimaryKey; + } + + private bool IsPrimaryKey(string propertyName) + { + if (_dbPrimaryKey == null) + return false; + + return _dbPrimaryKey.Split(new[] { ',' }) + .Select(x => x.Trim()) + .Contains(propertyName); + } + + public IDictionary GetDictionary() + { + return _dictionary; + } + + public Dictionary GetDictionaryInDbStyle(bool includePrimaryKey) + { + var dic = new Dictionary(); + foreach (var element in _dictionary) + if (includePrimaryKey || !IsPrimaryKey(element.Key.UnCammelCase())) + dic.Add(element.Key.UnCammelCase(), element.Value); + return dic; + } + + public Dictionary GetWhereClauseOnPrimaryKeyDbStyle() + { + if (_dbPrimaryKey == null) + throw new ApplicationException("Primary key is not set"); + + var dic = new Dictionary(); + + foreach (var column in _dbPrimaryKey.Split(new[] { ',' })) + { + var domainProperty = column.Trim().CammelCase(); + if (!_dictionary.ContainsKey(domainProperty)) + throw new ApplicationException("Property '" + domainProperty + "' was not found"); + dic.Add(column, _dictionary[domainProperty]); + } + return dic; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + _dictionary[binder.Name] = value; + return true; + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + return _dictionary.TryGetValue(binder.Name, out result); + } + + public override string ToString() + { + var s = ""; + + foreach (var element in _dictionary) + s += element.Key + " = " + element.Value + "\n"; + + return s; + } + } +} diff --git a/OdpNetMicroMapper/ObjectExtensions.cs b/OdpNetMicroMapper/ObjectExtensions.cs new file mode 100644 index 0000000..93d6352 --- /dev/null +++ b/OdpNetMicroMapper/ObjectExtensions.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Data; +using System.Collections; + +namespace OdpNetMicroMapper +{ + public static class ObjectExtensions + { + public static string RemoveInEnd(this string text, string search) + { + if (text.EndsWith(search)) + text = text.Substring(0, text.Length - search.Length); + return text; + } + + public static string UnCammelCase(this string name) + { + var s = ""; + + foreach (var c in name) + { + if (char.IsUpper(c) && s.Length>0) + s += "_"; + s += char.ToLower(c); + } + + return s; + } + + private static bool IsUnderscoreAndIsReverseable(string columnName, int index) + { + //Last and first char are not reversable + if (index == columnName.Length - 1 || index == 0) + return false; + + if (columnName[index] == '_' && Char.ToUpper(columnName[index + 1]) != Char.ToLower(columnName[index + 1])) + return true; + + return false; + } + + public static string CammelCase(this string name) + { + var s = ""; + var nextIsToUpper = true; + + for (var i = 0; i < name.Length; i++) + { + var c = name[i]; + + if (IsUnderscoreAndIsReverseable(name, i)) + { + nextIsToUpper = true; + } + else + { + s += nextIsToUpper ? Char.ToUpper(c) : Char.ToLower(c); + nextIsToUpper = false; + } + } + return s; + } + + public static void SetPropertyValue(this object o, string name, object value) + { + var pi = o.GetType().GetProperty(name); + pi.SetValue(o, value, null); + } + + public static IDictionary ToDictionary(this object o) + { + var dic = new Dictionary(); + var props = o.GetType().GetProperties().OrderBy(x => x.MetadataToken); + foreach (var item in props) + dic.Add(item.Name, item.GetValue(o, null)); + return dic; + } + + public static Entity ToEntity(this object o) + { + var dic = new Dictionary(); + + var props = o.GetType().GetProperties() + //ignore collections + .Where(x => x.PropertyType == typeof(string) || typeof(IEnumerable).IsAssignableFrom(x.PropertyType) == false) + .ToList(); + + foreach (var item in props) + dic.Add(item.Name, item.GetValue(o, null)); + + return new Entity(dic); + } + + public static Entity ToEntity(this IDataReader rdr) + { + var dic = new Dictionary(); + for (var i = 0; i < rdr.FieldCount; i++) + { + try + { + dic.Add(rdr.GetName(i).CammelCase(), DBNull.Value.Equals(rdr[i]) ? null : rdr[i]); + } + catch (Exception ex) + { + throw new ApplicationException("Error reading column " + rdr.GetName(i), ex); + } + } + return new Entity(dic); + } + + public static Entity ToEntity(this DataRow row) + { + var dic = new Dictionary(); + foreach (DataColumn c in row.Table.Columns) + dic.Add(c.ColumnName.CammelCase(), DBNull.Value.Equals(row[c]) ? null : row[c]); + return new Entity(dic); + } + + public static T ToObject(this IDataReader rdr) where T : new() + { + var o = new T(); + for (var i = 0; i < rdr.FieldCount; i++) + { + var name = rdr.GetName(i).CammelCase(); + var pi = o.GetType().GetProperty(name); + if (pi != null) + { + try + { + var type = Nullable.GetUnderlyingType(pi.PropertyType) + ?? pi.PropertyType; + + var value = DBNull.Value.Equals(rdr[i]) ? null : + Convert.ChangeType(rdr[i], type); + + pi.SetValue(o, value, null); + } + catch (Exception ex) + { + throw new ApplicationException("Error reading column " + rdr.GetName(i), ex); + } + } + else + { + if (DbMapper.PrintWarnings) + { + Console.WriteLine("Warning: could not find property " + name); + } + } + } + + return o; + } + + + } +} diff --git a/OdpNetMicroMapper/OdpNetMicroMapper.csproj b/OdpNetMicroMapper/OdpNetMicroMapper.csproj new file mode 100644 index 0000000..604697d --- /dev/null +++ b/OdpNetMicroMapper/OdpNetMicroMapper.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp2.0;net461 + 2.0.0.0 + 2.0.0.0 + 2.0.0 + true + Stig Christensen, Michael Hjorth + BankInvest + Easy and simple Oracle db mapper + odp.net oracle connection orm micro mapper + BankInvest 2018 + Oracle driver must be added manually. Choose the relevant driver for your setup: +.net 4.6.1 edition supports managed driver from NuGet or unmanaged driver loaded from GAC. +.net core 2.0 edition supports managed driver only (currently only beta release available from Oracle). + + + + + + + + + + + + + + + + + + diff --git a/OdpNetMicroMapper/SqlTokens.cs b/OdpNetMicroMapper/SqlTokens.cs new file mode 100644 index 0000000..4fc62d4 --- /dev/null +++ b/OdpNetMicroMapper/SqlTokens.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OdpNetMicroMapper +{ + public class SqlTokens + { + private readonly IDictionary _dic; + + public SqlTokens(IDictionary dic) + { + _dic = dic; + } + + public IDictionary GetNonNullableFieldsAndValues() + { + return _dic.Where(x => x.Value != null) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + + public string AsWhereClause(int index = 0) + { + string sql = ""; + foreach (string token in _dic.Keys) + { + sql += token + "=:" + index + " and "; + index++; + } + sql = "where " + sql.RemoveInEnd("and "); + return sql; + } + + public string AsColumnNames(bool includeNullFields = true) + { + string sql = ""; + foreach (string token in _dic.Keys) + { + if (_dic[token] != null || includeNullFields) + { + sql += token.UnCammelCase() + ", "; + } + } + sql = sql.RemoveInEnd(", "); + return sql; + } + + public string AsIndcies(bool includeNullFields = true) + { + int index = 0; + string sql = ""; + foreach (string token in _dic.Keys) + { + if (_dic[token] != null || includeNullFields) + { + sql += ":" + index + ", "; + index++; + } + } + sql = sql.RemoveInEnd(", "); + return sql; + } + + public string AsSetClause(int index = 0) + { + string sql = "set "; + foreach (string token in _dic.Keys) + { + sql += token + "=:" + index + ", "; + index++; + } + sql = sql.RemoveInEnd(", "); + return sql; + } + } +} diff --git a/OdpNetMicroMapper/readme.txt b/OdpNetMicroMapper/readme.txt new file mode 100644 index 0000000..0208fad --- /dev/null +++ b/OdpNetMicroMapper/readme.txt @@ -0,0 +1,5 @@ +- No odp.net binary reference +- Supports select/insert/update/delete/procedures +- Supports Oracle procedures with RefCursor +- Supports dynamic Domain Model / static Domain Model +- Everything is one line of code. Even calls to Oracle procedures diff --git a/TestManaged/App.config b/TestManaged/App.config new file mode 100644 index 0000000..addc074 --- /dev/null +++ b/TestManaged/App.config @@ -0,0 +1,33 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestManaged/Test.cs b/TestManaged/Test.cs new file mode 100644 index 0000000..9e2706c --- /dev/null +++ b/TestManaged/Test.cs @@ -0,0 +1,589 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using OdpNetMicroMapper; +using Oracle.ManagedDataAccess.Client; + +namespace TestManaged +{ + [TestFixture] + public class Test : TestBase + { + [Test] + public void InsertStatic() + { + Item item = new Item(); + item.Id = 3; + item.Name = "Third Item"; + orm.Insert(item, "onmm2.item"); + + item = orm.Query("select * from onmm2.item where id = :0", 3).SingleOrDefault(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Value.Date); + } + + [Test] + public void RawTest() + { + var guid = new Guid(); + orm.NonQuery("insert into onmm2.rawtest (bytes) values (:0)", guid.ToByteArray()); + + var bytesLoaded = orm.QueryScalar("select bytes from onmm2.rawtest where rownum = 1"); + var guidLoaded = new Guid(bytesLoaded); + Assert.AreEqual(guid, guidLoaded); + + dynamic item = orm.Query("select bytes from onmm2.rawtest where rownum = 1").Single(); + Assert.AreEqual(guid, new Guid(item.Bytes)); + } + + [TestCase("double")] + [TestCase("float")] + [TestCase("decimal")] + public void InsertDynamicTest(string type) + { + dynamic item = new Entity("onmm2.item"); + item.Id = 3; + item.Name = "Third Item"; + item.DateValue = DateTime.Today; + if (type == "double") item.DecimalValue = 3.1415d; + if (type == "float") item.DecimalValue = 3.1415f; + if (type == "decimal") item.DecimalValue = 3.1415m; + + orm.Insert(item); + + item = orm.Query("select * from onmm2.item where id = :0", 3).Single(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual(DateTime.Today, item.DateValue); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Date); + Assert.AreEqual(3.1415m, item.DecimalValue); + } + + [Test] + public void UpdateDynamicWithNull() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 1; + item.Name = null; + + orm.Update(item); + + item = orm.Query("select * from onmm2.item where id = :0", 1).Single(); + Assert.AreEqual(1, item.Id); + Assert.IsNull(item.Name); + } + + [Test] + public void InsertWithLongColumnMax4000chars() + { + dynamic item = new Entity("onmm2.item_with_long"); + item.Text = new string('X', 1000 * 4); //max 4k when inserting! + orm.Insert(item); + + var items = orm.Query("select * from onmm2.item_with_long"); + Assert.AreEqual(item.Text, items.Single().Text); + } + + [Test] + public void DeleteWithWhereClause() + { + dynamic item = new Entity("onmm2.item"); + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm2.item")); + + orm.Delete(item, "where id = :0", 1); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm2.item")); + + orm.Delete(item, "where id = :0", 2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm2.item")); + } + + [Test] + public void DeleteWithKeyMetadata() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 2; + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm2.item")); + + orm.Delete(item); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm2.item")); + + //check that the existing element is not the one deleted. + var items = orm.Query("select * from onmm2.item"); + Assert.AreEqual(1, items.Single().Id); + } + + [Test] + public void QueryScalarWhenNoRows() + { + Assert.AreEqual(null, orm.QueryScalar("select 1 from onmm2.item where 1=2")); + } + + [Test] + public void QueryScalarNullable() + { + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + } + + [Test] + public void QueryScalar() + { + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual("1", orm.QueryScalar("select 1 from dual")); + + Assert.AreEqual(DateTime.Today, orm.QueryScalar("select trunc(sysdate) from dual")); + Assert.AreEqual("test", orm.QueryScalar("select 'test' from dual")); + Assert.AreEqual(1.123456789m, orm.QueryScalar("select 1.123456789 from dual")); + } + + [Test] + public void UpdateViaWhereClause() + { + dynamic item = new Entity("onmm2.item"); + item.Name = "RENAMED"; + orm.Update(item, "where id = :0", 1); + + var items = orm.Query("select id, name from onmm2.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void UpdateViaMetaData() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 1; + item.Name = "RENAMED"; + orm.Update(item); + + var items = orm.Query("select id, name from onmm2.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void MergeInto() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 100; + item.Name = "Name100"; + orm.MergeInto(item); + + var itemLoaded = orm.Query("select * from onmm2.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + + item.Name = "renamed"; + orm.MergeInto(item); + + itemLoaded = orm.Query("select * from onmm2.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + } + + [Test] + public void MergeIntoWithOnlyPrimaryKey_ShouldNotTryUpdateAndFail() + { + dynamic item = new Entity("onmm2.item", "id"); + item.Id = 100; + orm.MergeInto(item); + orm.MergeInto(item); + } + + [Test] + public void ColumnWithUnderScoreBeforeDigit() + { + Entity item = orm.Query("select yield_2date from onmm2.item_odd where id = 1").Single(); + + //Assert.AreEqual(99m, item.Yield_2date); + + foreach (var p in item.GetDictionaryInDbStyle(true)) + Assert.AreEqual("yield_2date", p.Key); + + foreach (var p in item.GetDictionary()) + Assert.AreEqual("Yield_2date", p.Key); + } + + + [Test] + public void QueryDynamicToString() + { + var item = orm.Query("select * from onmm2.item where id = 1").Single(); + Assert.That(item.ToString(), Contains.Substring("Id = 1")); + Assert.That(item.ToString(), Contains.Substring("Name = First Item")); + } + + [Test] + public void QuerySingleTypeList() + { + var ints = orm.QuerySingleTypeList("select id from onmm2.item order by id").ToList(); + Assert.AreEqual(1, ints[0]); + Assert.AreEqual(2, ints[1]); + + var strings = orm.QuerySingleTypeList("select name from onmm2.item order by id").ToList(); + Assert.AreEqual("First Item", strings[0]); + Assert.AreEqual("Second Item", strings[1]); + } + + [Test] + public void QueryStaticNoWhereClause() + { + var items = orm.Query("select * from onmm2.item order by id").ToList(); + Assert.AreEqual(2, items.Count); + Assert.AreEqual(1, items[0].Id); + Assert.AreEqual(2, items[1].Id); + Assert.AreEqual("First Item", items[0].Name); + Assert.AreEqual("Second Item", items[1].Name); + Assert.AreEqual(0.321m, items[0].DecimalValue); + Assert.AreEqual(.123m, items[1].DecimalValue); + } + + [Test] + public void QueryDynamicWithWhereClause() + { + var item = orm.Query("select id, name from onmm2.item where id = :0", 1).SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryStaticWithWhereClause() + { + var item = orm.Query("select id, name from onmm2.item where id = :0", 1) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryNonExisting() + { + var item = orm.Query("select id, name from onmm2.item where id = :0", -1) + .SingleOrDefault(); + Assert.IsNull(item); + } + + + [Test] + public void ExecuteFunctionWithInt() + { + var result = orm.ExecuteFunction("onmm2.plus1function", 100); + Assert.AreEqual(101, result); + } + + [Test] + public void ExecuteFunctionWithString() + { + var result = orm.ExecuteFunction("onmm2.append1function", "100"); + Assert.AreEqual("1001", result); + } + + [Test] + public void ExecuteProcedureWithRefCursor() + { + var item = orm.ExecuteProcedure("onmm2.get_items_by_name", "First Item", DbMapperParameter.RefCursor) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void ExecuteProcedureWithRefCursorZeroElements() + { + var items = orm.ExecuteProcedure("onmm2.get_items_by_name", "non existing", DbMapperParameter.RefCursor); + Assert.AreEqual(0, items.Count()); + } + + [Test] + public void ExecuteProcedureWithoutRefCursor() + { + string newName = "RENAMED"; + object result = orm.ExecuteProcedure("onmm2.rename_item", 1, newName); + Assert.IsNull(result); + Assert.AreEqual(newName, orm.QueryScalar("select name from onmm2.item where id = :0", 1)); + } + + [Test] + public void InsertWithImplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm2.item", "id"); + item.id = 1; + + for (int i = 0; i < count; i++) + orm.Insert(item); + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm2.item")); + Console.WriteLine("InsertWithImplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm2.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm2.item")); + Console.WriteLine("InsertWithExplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnectionAndTransaction() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm2.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + using (var tx = connection.BeginTransaction()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + tx.Commit(); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm2.item")); + Console.WriteLine("InsertWithExplicitConnectionAndTransaction Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertAndSelectBigClob() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + //insert + dynamic item = new Entity("onmm2.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm2.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + + [Test] + public void InsertBigClobWithNonQuery() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + orm.NonQuery("insert into onmm2.bigclobtest (text) values (:0)", largeString); + + string largeStringFetched = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + [Test] + public void SelectWithExternalConnection() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + var r = orm.QueryScalar("select 1 from dual"); + Assert.AreEqual(1, r); + } + } + } + + [Test] + public void InsertWithExternalConnectionAndSelectBigClob() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 1); + + //insert + dynamic item = new Entity("onmm2.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm2.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + } + } + + [Test] + public void DeleteWithCompositeKeyt() + { + dynamic item1 = new Entity("onmm2.item_composite_key", "id, type"); + item1.Id = 1; + item1.Type = 1; + orm.Insert(item1); + + dynamic item2 = new Entity("onmm2.item_composite_key", "id,type"); + item2.Id = 1; + item2.Type = 2; + orm.Insert(item2); + + orm.Delete(item1); + var list = orm.Query("select * from onmm2.item_composite_key"); + Assert.AreEqual(2, list.Single().Type); + + orm.Delete(item2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm2.item_composite_key")); + } + + [Test] + public void OverflowDynamic() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void Overflow() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void LoadStaticShouldWarn() + { + DbMapper.PrintWarnings = true; + DbMapper.PrintSqls = true; + var item = orm.Query("select id, name, decimal_value, date_value from onmm2.item where id = :0", 1).SingleOrDefault(); + DbMapper.PrintWarnings = false; + DbMapper.PrintSqls = false; + } + + [Test] + public void CollectionShouldNotBeUsed() + { + ItemWithCollection item = new ItemWithCollection(); + item.Id = 3; + item.GroupsNotPresentInDb = new List() { 1 }; + orm.Insert(item, "onmm2.item"); + } + + [Test] + public void ConnectAsSys() + { + var orm = new DbMapper(); + orm.ConnectAsSys("tstdaily", "bi"); + orm.QueryScalar("select 1 from dual"); + } + + [TestCase("1\n2")] + [TestCase("1\r\n2")] + [TestCase("1\n\r2")] + [TestCase("1\r2")] + public void ClobTests(string testValue) + { + dynamic item = new Entity("onmm2.bigclobtest"); + item.Text = testValue; + orm.Insert(item); + + var fromDb = orm.QueryScalar("select text from onmm2.bigclobtest"); + Assert.AreEqual(testValue, fromDb); + } + + [Test] + public void NonQueryIgnoreErrorShouldWork() + { + orm.NonQueryIgnoreError("should not work"); + } + + [Test] + public void ToEntityShouldIgnoreListProperties() + { + var entity = new SomeClass() + .ToEntity(); + + var dic = entity.GetDictionary(); + + Assert.AreEqual(4, dic.Count); + Assert.IsTrue(dic.ContainsKey("Prop1")); + Assert.IsTrue(dic.ContainsKey("Prop2")); + Assert.IsTrue(dic.ContainsKey("Prop3")); + Assert.IsTrue(dic.ContainsKey("Prop4")); + + } + } + + class ProcParameters + { + public decimal Input { get; set; } + public decimal EchoOutput { get; set; } + public decimal CountOutput { get; set; } + public object FundsOutput { get; set; } + } + + public class Item + { + public int Id { get; set; } + public string Name { get; set; } + public decimal DecimalValue { get; set; } + public DateTime? DateValue { get; set; } + } + + public class ItemWithCollection + { + public int Id { get; set; } + public List GroupsNotPresentInDb { get; set; } + } + + public class ItemWrongDefinition + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Decimalvalue { get; set; } + public DateTime? Datevalue { get; set; } + } + + class SomeClass + { + public string Prop1 { get; set; } + public decimal Prop2 { get; set; } + public DateTime Prop3 { get; set; } + public int? Prop4 { get; set; } + public List Col1 { get; set; } + public String[] Col2 { get; set; } + public Dictionary Col3 { get; set; } + } +} diff --git a/TestManaged/TestBase.cs b/TestManaged/TestBase.cs new file mode 100644 index 0000000..a342167 --- /dev/null +++ b/TestManaged/TestBase.cs @@ -0,0 +1,124 @@ +using System; +using NUnit.Framework; +using OdpNetMicroMapper; + +namespace TestManaged +{ + [TestFixture] + public class TestBase + { + static bool firstRun = true; + protected DbMapper orm = new DbMapper(); + protected DbMapper sysOrm = new DbMapper(); + + private void CreateUser() + { + if (!firstRun) + return; + + firstRun = false; + + sysOrm.NonQueryIgnoreError("drop user onmm2 cascade"); + sysOrm.NonQuery("create user onmm2 identified by onmm2"); + sysOrm.NonQuery("grant create session to onmm2"); + sysOrm.NonQuery("alter user onmm2 quota unlimited on users"); + Console.WriteLine("user onmm2 created"); + + var sql = "create table onmm2.bigclobtest (text clob)"; + sysOrm.NonQuery(sql); + + sql = "create table onmm2.rawtest (bytes raw(16))"; + sysOrm.NonQuery(sql); + + sql = "create table onmm2.item_with_long (text long)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm2.item_odd (id number(10) not null, yield_2date number)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm2.item_composite_key (id number(10) not null, type number(10) not null, text varchar2(100))"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm2.item (id number(10) not null, name varchar2(100), decimal_value number, date_value timestamp default sysdate not null)"; + sysOrm.NonQuery(sql); + + sql = @"create sequence onmm2.seq_items start with 3"; + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm2.append1function (pString varchar2) return varchar2 + as + begin + return pString || '1'; + end;"; + //todo fix elsewhere? //http://boncode.blogspot.com/2009/03/oracle-pls-00103-encountered-symbol.html + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm2.plus1function (pNumber number) return integer + as + begin + return pNumber + 1; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm2.get_items_by_name(pName in varchar2, io_cursor out SYS_REFCURSOR) is + begin + open io_cursor for + select * from onmm2.item t where t.name = pName; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm2.rename_item(pId number, pName in varchar2) is + begin + update onmm2.item t set t.name = pName where t.id = pId; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm2.proc_with_many_out_parameters(pInput number, pEcho out number, pCount out number, io_cursor out SYS_REFCURSOR) is + begin + select pInput into pEcho from dual; + select count(1) into pCount from onmm2.fund; + open io_cursor for + select * from onmm2.fund; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + } + + [TearDown] + public void DropSchema() + { + } + + [SetUp] + public void CreateSchema() + { + //throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); + + sysOrm.ConnectAsSys("INTSHARE", "bi"); + orm.ConnectionString = "data source=INTSHARE;user id=onmm2;password=onmm2;"; + + CreateUser(); + + sysOrm.NonQuery("delete from onmm2.item_composite_key"); + sysOrm.NonQuery("delete from onmm2.item"); + sysOrm.NonQuery("delete from onmm2.bigclobtest"); + sysOrm.NonQuery("delete from onmm2.item_with_long"); + sysOrm.NonQuery("delete from onmm2.item_odd"); + sysOrm.NonQuery("delete from onmm2.rawtest"); + + var sql = "insert into onmm2.item_odd (id, yield_2date) values (1, 99)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm2.item (id, name, decimal_value) values (1, 'First Item', 0.321)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm2.item (id, name, decimal_value) values (2, 'Second Item', 0.123)"; + sysOrm.NonQuery(sql); + + } + } +} diff --git a/TestManaged/TestManaged.csproj b/TestManaged/TestManaged.csproj new file mode 100644 index 0000000..0c5d9fb --- /dev/null +++ b/TestManaged/TestManaged.csproj @@ -0,0 +1,17 @@ + + + + net461 + + + + + + + + + + + + + diff --git a/TestManagedCore/App.config b/TestManagedCore/App.config new file mode 100644 index 0000000..addc074 --- /dev/null +++ b/TestManagedCore/App.config @@ -0,0 +1,33 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestManagedCore/Test.cs b/TestManagedCore/Test.cs new file mode 100644 index 0000000..289caa9 --- /dev/null +++ b/TestManagedCore/Test.cs @@ -0,0 +1,589 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using OdpNetMicroMapper; +using Oracle.ManagedDataAccess.Client; + +namespace TestManagedCore +{ + [TestFixture] + public class Test : TestBase + { + [Test] + public void InsertStatic() + { + Item item = new Item(); + item.Id = 3; + item.Name = "Third Item"; + orm.Insert(item, "onmm3.item"); + + item = orm.Query("select * from onmm3.item where id = :0", 3).SingleOrDefault(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Value.Date); + } + + [Test] + public void RawTest() + { + var guid = new Guid(); + orm.NonQuery("insert into onmm3.rawtest (bytes) values (:0)", guid.ToByteArray()); + + var bytesLoaded = orm.QueryScalar("select bytes from onmm3.rawtest where rownum = 1"); + var guidLoaded = new Guid(bytesLoaded); + Assert.AreEqual(guid, guidLoaded); + + dynamic item = orm.Query("select bytes from onmm3.rawtest where rownum = 1").Single(); + Assert.AreEqual(guid, new Guid(item.Bytes)); + } + + [TestCase("double")] + [TestCase("float")] + [TestCase("decimal")] + public void InsertDynamicTest(string type) + { + dynamic item = new Entity("onmm3.item"); + item.Id = 3; + item.Name = "Third Item"; + item.DateValue = DateTime.Today; + if (type == "double") item.DecimalValue = 3.1415d; + if (type == "float") item.DecimalValue = 3.1415f; + if (type == "decimal") item.DecimalValue = 3.1415m; + + orm.Insert(item); + + item = orm.Query("select * from onmm3.item where id = :0", 3).Single(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual(DateTime.Today, item.DateValue); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Date); + Assert.AreEqual(3.1415m, item.DecimalValue); + } + + [Test] + public void UpdateDynamicWithNull() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 1; + item.Name = null; + + orm.Update(item); + + item = orm.Query("select * from onmm3.item where id = :0", 1).Single(); + Assert.AreEqual(1, item.Id); + Assert.IsNull(item.Name); + } + + [Test] + public void InsertWithLongColumnMax4000chars() + { + dynamic item = new Entity("onmm3.item_with_long"); + item.Text = new string('X', 1000 * 4); //max 4k when inserting! + orm.Insert(item); + + var items = orm.Query("select * from onmm3.item_with_long"); + Assert.AreEqual(item.Text, items.Single().Text); + } + + [Test] + public void DeleteWithWhereClause() + { + dynamic item = new Entity("onmm3.item"); + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm3.item")); + + orm.Delete(item, "where id = :0", 1); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm3.item")); + + orm.Delete(item, "where id = :0", 2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm3.item")); + } + + [Test] + public void DeleteWithKeyMetadata() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 2; + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm3.item")); + + orm.Delete(item); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm3.item")); + + //check that the existing element is not the one deleted. + var items = orm.Query("select * from onmm3.item"); + Assert.AreEqual(1, items.Single().Id); + } + + [Test] + public void QueryScalarWhenNoRows() + { + Assert.AreEqual(null, orm.QueryScalar("select 1 from onmm3.item where 1=2")); + } + + [Test] + public void QueryScalarNullable() + { + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + } + + [Test] + public void QueryScalar() + { + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual("1", orm.QueryScalar("select 1 from dual")); + + Assert.AreEqual(DateTime.Today, orm.QueryScalar("select trunc(sysdate) from dual")); + Assert.AreEqual("test", orm.QueryScalar("select 'test' from dual")); + Assert.AreEqual(1.123456789m, orm.QueryScalar("select 1.123456789 from dual")); + } + + [Test] + public void UpdateViaWhereClause() + { + dynamic item = new Entity("onmm3.item"); + item.Name = "RENAMED"; + orm.Update(item, "where id = :0", 1); + + var items = orm.Query("select id, name from onmm3.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void UpdateViaMetaData() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 1; + item.Name = "RENAMED"; + orm.Update(item); + + var items = orm.Query("select id, name from onmm3.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void MergeInto() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 100; + item.Name = "Name100"; + orm.MergeInto(item); + + var itemLoaded = orm.Query("select * from onmm3.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + + item.Name = "renamed"; + orm.MergeInto(item); + + itemLoaded = orm.Query("select * from onmm3.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + } + + [Test] + public void MergeIntoWithOnlyPrimaryKey_ShouldNotTryUpdateAndFail() + { + dynamic item = new Entity("onmm3.item", "id"); + item.Id = 100; + orm.MergeInto(item); + orm.MergeInto(item); + } + + [Test] + public void ColumnWithUnderScoreBeforeDigit() + { + Entity item = orm.Query("select yield_2date from onmm3.item_odd where id = 1").Single(); + + //Assert.AreEqual(99m, item.Yield_2date); + + foreach (var p in item.GetDictionaryInDbStyle(true)) + Assert.AreEqual("yield_2date", p.Key); + + foreach (var p in item.GetDictionary()) + Assert.AreEqual("Yield_2date", p.Key); + } + + + [Test] + public void QueryDynamicToString() + { + var item = orm.Query("select * from onmm3.item where id = 1").Single(); + Assert.That(item.ToString(), Contains.Substring("Id = 1")); + Assert.That(item.ToString(), Contains.Substring("Name = First Item")); + } + + [Test] + public void QuerySingleTypeList() + { + var ints = orm.QuerySingleTypeList("select id from onmm3.item order by id").ToList(); + Assert.AreEqual(1, ints[0]); + Assert.AreEqual(2, ints[1]); + + var strings = orm.QuerySingleTypeList("select name from onmm3.item order by id").ToList(); + Assert.AreEqual("First Item", strings[0]); + Assert.AreEqual("Second Item", strings[1]); + } + + [Test] + public void QueryStaticNoWhereClause() + { + var items = orm.Query("select * from onmm3.item order by id").ToList(); + Assert.AreEqual(2, items.Count); + Assert.AreEqual(1, items[0].Id); + Assert.AreEqual(2, items[1].Id); + Assert.AreEqual("First Item", items[0].Name); + Assert.AreEqual("Second Item", items[1].Name); + Assert.AreEqual(0.321m, items[0].DecimalValue); + Assert.AreEqual(.123m, items[1].DecimalValue); + } + + [Test] + public void QueryDynamicWithWhereClause() + { + var item = orm.Query("select id, name from onmm3.item where id = :0", 1).SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryStaticWithWhereClause() + { + var item = orm.Query("select id, name from onmm3.item where id = :0", 1) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryNonExisting() + { + var item = orm.Query("select id, name from onmm3.item where id = :0", -1) + .SingleOrDefault(); + Assert.IsNull(item); + } + + + [Test] + public void ExecuteFunctionWithInt() + { + var result = orm.ExecuteFunction("onmm3.plus1function", 100); + Assert.AreEqual(101, result); + } + + [Test] + public void ExecuteFunctionWithString() + { + var result = orm.ExecuteFunction("onmm3.append1function", "100"); + Assert.AreEqual("1001", result); + } + + [Test] + public void ExecuteProcedureWithRefCursor() + { + var item = orm.ExecuteProcedure("onmm3.get_items_by_name", "First Item", DbMapperParameter.RefCursor) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void ExecuteProcedureWithRefCursorZeroElements() + { + var items = orm.ExecuteProcedure("onmm3.get_items_by_name", "non existing", DbMapperParameter.RefCursor); + Assert.AreEqual(0, items.Count()); + } + + [Test] + public void ExecuteProcedureWithoutRefCursor() + { + string newName = "RENAMED"; + object result = orm.ExecuteProcedure("onmm3.rename_item", 1, newName); + Assert.IsNull(result); + Assert.AreEqual(newName, orm.QueryScalar("select name from onmm3.item where id = :0", 1)); + } + + [Test] + public void InsertWithImplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm3.item", "id"); + item.id = 1; + + for (int i = 0; i < count; i++) + orm.Insert(item); + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm3.item")); + Console.WriteLine("InsertWithImplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm3.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm3.item")); + Console.WriteLine("InsertWithExplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnectionAndTransaction() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm3.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + using (var tx = connection.BeginTransaction()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + tx.Commit(); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm3.item")); + Console.WriteLine("InsertWithExplicitConnectionAndTransaction Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertAndSelectBigClob() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + //insert + dynamic item = new Entity("onmm3.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm3.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + + [Test] + public void InsertBigClobWithNonQuery() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + orm.NonQuery("insert into onmm3.bigclobtest (text) values (:0)", largeString); + + string largeStringFetched = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + [Test] + public void SelectWithExternalConnection() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + var r = orm.QueryScalar("select 1 from dual"); + Assert.AreEqual(1, r); + } + } + } + + [Test] + public void InsertWithExternalConnectionAndSelectBigClob() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 1); + + //insert + dynamic item = new Entity("onmm3.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm3.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + } + } + + [Test] + public void DeleteWithCompositeKeyt() + { + dynamic item1 = new Entity("onmm3.item_composite_key", "id, type"); + item1.Id = 1; + item1.Type = 1; + orm.Insert(item1); + + dynamic item2 = new Entity("onmm3.item_composite_key", "id,type"); + item2.Id = 1; + item2.Type = 2; + orm.Insert(item2); + + orm.Delete(item1); + var list = orm.Query("select * from onmm3.item_composite_key"); + Assert.AreEqual(2, list.Single().Type); + + orm.Delete(item2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm3.item_composite_key")); + } + + [Test] + public void OverflowDynamic() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void Overflow() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void LoadStaticShouldWarn() + { + DbMapper.PrintWarnings = true; + DbMapper.PrintSqls = true; + var item = orm.Query("select id, name, decimal_value, date_value from onmm3.item where id = :0", 1).SingleOrDefault(); + DbMapper.PrintWarnings = false; + DbMapper.PrintSqls = false; + } + + [Test] + public void CollectionShouldNotBeUsed() + { + ItemWithCollection item = new ItemWithCollection(); + item.Id = 3; + item.GroupsNotPresentInDb = new List() { 1 }; + orm.Insert(item, "onmm3.item"); + } + + [Test] + public void ConnectAsSys() + { + var orm = new DbMapper(); + orm.ConnectAsSys("tstdaily", "bi"); + orm.QueryScalar("select 1 from dual"); + } + + [TestCase("1\n2")] + [TestCase("1\r\n2")] + [TestCase("1\n\r2")] + [TestCase("1\r2")] + public void ClobTests(string testValue) + { + dynamic item = new Entity("onmm3.bigclobtest"); + item.Text = testValue; + orm.Insert(item); + + var fromDb = orm.QueryScalar("select text from onmm3.bigclobtest"); + Assert.AreEqual(testValue, fromDb); + } + + [Test] + public void NonQueryIgnoreErrorShouldWork() + { + orm.NonQueryIgnoreError("should not work"); + } + + [Test] + public void ToEntityShouldIgnoreListProperties() + { + var entity = new SomeClass() + .ToEntity(); + + var dic = entity.GetDictionary(); + + Assert.AreEqual(4, dic.Count); + Assert.IsTrue(dic.ContainsKey("Prop1")); + Assert.IsTrue(dic.ContainsKey("Prop2")); + Assert.IsTrue(dic.ContainsKey("Prop3")); + Assert.IsTrue(dic.ContainsKey("Prop4")); + + } + } + + class ProcParameters + { + public decimal Input { get; set; } + public decimal EchoOutput { get; set; } + public decimal CountOutput { get; set; } + public object FundsOutput { get; set; } + } + + public class Item + { + public int Id { get; set; } + public string Name { get; set; } + public decimal DecimalValue { get; set; } + public DateTime? DateValue { get; set; } + } + + public class ItemWithCollection + { + public int Id { get; set; } + public List GroupsNotPresentInDb { get; set; } + } + + public class ItemWrongDefinition + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Decimalvalue { get; set; } + public DateTime? Datevalue { get; set; } + } + + class SomeClass + { + public string Prop1 { get; set; } + public decimal Prop2 { get; set; } + public DateTime Prop3 { get; set; } + public int? Prop4 { get; set; } + public List Col1 { get; set; } + public String[] Col2 { get; set; } + public Dictionary Col3 { get; set; } + } +} diff --git a/TestManagedCore/TestBase.cs b/TestManagedCore/TestBase.cs new file mode 100644 index 0000000..9e785a4 --- /dev/null +++ b/TestManagedCore/TestBase.cs @@ -0,0 +1,124 @@ +using System; +using NUnit.Framework; +using OdpNetMicroMapper; + +namespace TestManagedCore +{ + [TestFixture] + public class TestBase + { + static bool firstRun = true; + protected DbMapper orm = new DbMapper(); + protected DbMapper sysOrm = new DbMapper(); + + private void CreateUser() + { + if (!firstRun) + return; + + firstRun = false; + + sysOrm.NonQueryIgnoreError("drop user onmm3 cascade"); + sysOrm.NonQuery("create user onmm3 identified by onmm3"); + sysOrm.NonQuery("grant create session to onmm3"); + sysOrm.NonQuery("alter user onmm3 quota unlimited on users"); + Console.WriteLine("user onmm3 created"); + + var sql = "create table onmm3.bigclobtest (text clob)"; + sysOrm.NonQuery(sql); + + sql = "create table onmm3.rawtest (bytes raw(16))"; + sysOrm.NonQuery(sql); + + sql = "create table onmm3.item_with_long (text long)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm3.item_odd (id number(10) not null, yield_2date number)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm3.item_composite_key (id number(10) not null, type number(10) not null, text varchar2(100))"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm3.item (id number(10) not null, name varchar2(100), decimal_value number, date_value timestamp default sysdate not null)"; + sysOrm.NonQuery(sql); + + sql = @"create sequence onmm3.seq_items start with 3"; + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm3.append1function (pString varchar2) return varchar2 + as + begin + return pString || '1'; + end;"; + //todo fix elsewhere? //http://boncode.blogspot.com/2009/03/oracle-pls-00103-encountered-symbol.html + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm3.plus1function (pNumber number) return integer + as + begin + return pNumber + 1; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm3.get_items_by_name(pName in varchar2, io_cursor out SYS_REFCURSOR) is + begin + open io_cursor for + select * from onmm3.item t where t.name = pName; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm3.rename_item(pId number, pName in varchar2) is + begin + update onmm3.item t set t.name = pName where t.id = pId; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm3.proc_with_many_out_parameters(pInput number, pEcho out number, pCount out number, io_cursor out SYS_REFCURSOR) is + begin + select pInput into pEcho from dual; + select count(1) into pCount from onmm3.fund; + open io_cursor for + select * from onmm3.fund; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + } + + [TearDown] + public void DropSchema() + { + } + + [SetUp] + public void CreateSchema() + { + //throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); + + sysOrm.ConnectAsSys("INTSHARE", "bi"); + orm.ConnectionString = "data source=INTSHARE;user id=onmm3;password=onmm3;"; + + CreateUser(); + + sysOrm.NonQuery("delete from onmm3.item_composite_key"); + sysOrm.NonQuery("delete from onmm3.item"); + sysOrm.NonQuery("delete from onmm3.bigclobtest"); + sysOrm.NonQuery("delete from onmm3.item_with_long"); + sysOrm.NonQuery("delete from onmm3.item_odd"); + sysOrm.NonQuery("delete from onmm3.rawtest"); + + var sql = "insert into onmm3.item_odd (id, yield_2date) values (1, 99)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm3.item (id, name, decimal_value) values (1, 'First Item', 0.321)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm3.item (id, name, decimal_value) values (2, 'Second Item', 0.123)"; + sysOrm.NonQuery(sql); + + } + } +} diff --git a/TestManagedCore/TestManagedCore.csproj b/TestManagedCore/TestManagedCore.csproj new file mode 100644 index 0000000..a9b9446 --- /dev/null +++ b/TestManagedCore/TestManagedCore.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp2.0 + + + + + + + + + + + + + + + Always + + + Always + + + + diff --git a/TestManagedCore/sqlnet.ora b/TestManagedCore/sqlnet.ora new file mode 100644 index 0000000..e763a8e --- /dev/null +++ b/TestManagedCore/sqlnet.ora @@ -0,0 +1,5 @@ +# This file is actually generated by netca. But if customers choose to +# install "Software Only", this file wont exist and without the native +# authentication, they will not be able to connect to the database on NT. + +SQLNET.AUTHENTICATION_SERVICES = (NTS) diff --git a/TestManagedCore/tnsnames.ora b/TestManagedCore/tnsnames.ora new file mode 100644 index 0000000..37b52fe --- /dev/null +++ b/TestManagedCore/tnsnames.ora @@ -0,0 +1,149 @@ +# ____ ____ ___ ____ _ _ _ __ _____ ___ ___ _ _ ____ _ __ _ ___ _____ _ _ _____ _____ ____ +# | _ \ | _ \ / _ \ | _ \ | | | || |/ /|_ _||_ _|/ _ \ | \ | |/ ___| | |/ /| | |_ _|| ____|| \ | ||_ _|| ____|| _ \ +# | |_) || |_) || | | || | | || | | || ' / | | | || | | || \| |\___ \ | ' / | | | | | _| | \| | | | | _| | |_) | +# | __/ | _ < | |_| || |_| || |_| || . \ | | | || |_| || |\ | ___) | | . \ | |___ | | | |___ | |\ | | | | |___ | _ < +# |_| |_| \_\ \___/ |____/ \___/ |_|\_\ |_| |___|\___/ |_| \_||____/ |_|\_\|_____||___||_____||_| \_| |_| |_____||_| \_\ +# + + +AUDDB = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = AUDDB)) + ) + +CI = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA01.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = ORCL)) + ) + +INV = + (DESCRIPTION = + (ADDRESS = (PROTOCOL=TCP) (HOST=bidbs2003) (PORT=1521)) + (CONNECT_DATA = (SERVICE_NAME=invision)) + ) + +INVISION = + (DESCRIPTION = + (ADDRESS = (PROTOCOL=TCP) (HOST=bidbs2003) (PORT=1521)) + (CONNECT_DATA = (SERVICE_NAME=invision)) + ) + +PMFORUM = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA01.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = ORCL)) + ) + +ORA_ALIAS = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA01.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = ORCL)) + ) + +#Herunder er der kun testinstanser + +INV_TEST = (DESCRIPTION = + (ADDRESS_LIST = + (ADDRESS = (PROTOCOL = TCP)(HOST = bidbs2004.bi.local)(PORT = 1521)) + ) + (CONNECT_DATA = + (SERVICE_NAME = Invision) + ) + ) + +CIUDV = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = CIUDV)) + ) + +TEST = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TEST)) + ) + +TSTDAILY = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TSTDAILY)) + ) + +FPDTEST = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = FPDTEST)) + ) + +SUPDAILY = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = SUPDAILY)) + ) + +INTFID = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = INTFID)) + ) + +INTSHARE = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = INTSHARE)) + ) + +DEVSHARE = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = CIUDV)) + ) + +INTFMS = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = FMSREF)) + ) + +TSTWEEK = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TEST)) + ) + +BIZTEST = + (DESCRIPTION = + (ADDRESS_LIST = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + ) + (CONNECT_DATA = + (SERVER = DEDICATED) + (SERVICE_NAME = BIZTEST)) + ) + +INVWEEK = + (DESCRIPTION= + (ADDRESS= (PROTOCOL=TCP) (HOST=bidbs2004) (PORT=1521)) + (CONNECT_DATA =(SERVICE_NAME=week)) + ) + +INVUDV = + (DESCRIPTION= + (ADDRESS= (PROTOCOL=TCP) (HOST=bidbs2004) (PORT=1521)) + (CONNECT_DATA =(SERVICE_NAME=udv)) + ) + +INVPRJKT = + (DESCRIPTION= + (ADDRESS = (PROTOCOL = TCP)(HOST=bidbs2004.bi.local)(PORT=1521)) + (CONNECT_DATA= + (SERVICE_NAME=INVPRJKT)) + ) + +TSTPRJKT = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = BICLUORA03.bi.local)(PORT = 1521)) + (CONNECT_DATA = (SID = TSTPRJKT)) + ) \ No newline at end of file diff --git a/Tests/App.config b/Tests/App.config new file mode 100644 index 0000000..0e83688 --- /dev/null +++ b/Tests/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/CammelCaseTest.cs b/Tests/CammelCaseTest.cs new file mode 100644 index 0000000..d492420 --- /dev/null +++ b/Tests/CammelCaseTest.cs @@ -0,0 +1,36 @@ +using NUnit.Framework; +using OdpNetMicroMapper; + +namespace Test +{ + class CammelCaseTest + { + [Test] + public void CammelCaseRules() + { + Assert.AreEqual("ID".CammelCase(), "Id"); + Assert.AreEqual("FUND".CammelCase(), "Fund"); + Assert.AreEqual("id".CammelCase(), "Id"); + Assert.AreEqual("fund_id".CammelCase(), "FundId"); + Assert.AreEqual("yield_2date".CammelCase(), "Yield_2date"); + Assert.AreEqual("_test".CammelCase(), "_test"); + Assert.AreEqual("test_".CammelCase(), "Test_"); + } + + [TestCase("id")] + [TestCase("fund_id")] + [TestCase("yield_2date")] + [TestCase("yield__2date")] + [TestCase("yield_2_date")] + [TestCase("test")] + [TestCase("test_")] + [TestCase("_test")] + [TestCase("_test_")] + [TestCase("1_2_3_name")] + [TestCase("_")] + public void IsReverseableTest(string column) + { + Assert.AreEqual(column, column.CammelCase().UnCammelCase(), "CammelCase is " + column.CammelCase()); + } + } +} diff --git a/Tests/ReferencesTest.cs b/Tests/ReferencesTest.cs new file mode 100644 index 0000000..e01c45a --- /dev/null +++ b/Tests/ReferencesTest.cs @@ -0,0 +1,27 @@ +using System; +using System.Reflection; +using NUnit.Framework; +using OdpNetMicroMapper; + +namespace Tests +{ + [TestFixture] + class ReferencesTest + { + [Test] + public void PrintMyReferences() + { + var orm = new DbMapper(); + Console.WriteLine("Runtime Assemblies"); + + var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var a in loadedAssemblies) + { + PortableExecutableKinds peKind; + ImageFileMachine imageFileMachine; + a.ManifestModule.GetPEKind(out peKind, out imageFileMachine); + Console.WriteLine(a + " / " + a.ImageRuntimeVersion + " / " + peKind + " / " + imageFileMachine); + } + } + } +} diff --git a/Tests/Test.cs b/Tests/Test.cs new file mode 100644 index 0000000..cf41cbb --- /dev/null +++ b/Tests/Test.cs @@ -0,0 +1,589 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OdpNetMicroMapper; +using NUnit.Framework; +using System.Diagnostics; +using Oracle.DataAccess.Client; + +namespace Tests +{ + [TestFixture] + public class Test : TestBase + { + [Test] + public void InsertStatic() + { + Item item = new Item(); + item.Id = 3; + item.Name = "Third Item"; + orm.Insert(item, "onmm.item"); + + item = orm.Query("select * from onmm.item where id = :0", 3).SingleOrDefault(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Value.Date); + } + + [Test] + public void RawTest() + { + var guid = new Guid(); + orm.NonQuery("insert into onmm.rawtest (bytes) values (:0)", guid.ToByteArray()); + + var bytesLoaded = orm.QueryScalar("select bytes from onmm.rawtest where rownum = 1"); + var guidLoaded = new Guid(bytesLoaded); + Assert.AreEqual(guid, guidLoaded); + + dynamic item = orm.Query("select bytes from onmm.rawtest where rownum = 1").Single(); + Assert.AreEqual(guid, new Guid(item.Bytes)); + } + + [TestCase("double")] + [TestCase("float")] + [TestCase("decimal")] + public void InsertDynamicTest(string type) + { + dynamic item = new Entity("onmm.item"); + item.Id = 3; + item.Name = "Third Item"; + item.DateValue = DateTime.Today; + if (type == "double") item.DecimalValue = 3.1415d; + if (type == "float") item.DecimalValue = 3.1415f; + if (type == "decimal") item.DecimalValue = 3.1415m; + + orm.Insert(item); + + item = orm.Query("select * from onmm.item where id = :0", 3).Single(); + Assert.AreEqual(3, item.Id); + Assert.AreEqual(DateTime.Today, item.DateValue); + Assert.AreEqual("Third Item", item.Name); + Assert.AreEqual(DateTime.Today, item.DateValue.Date); + Assert.AreEqual(3.1415m, item.DecimalValue); + } + + [Test] + public void UpdateDynamicWithNull() + { + dynamic item = new Entity("onmm.item", "id"); + item.Id = 1; + item.Name = null; + + orm.Update(item); + + item = orm.Query("select * from onmm.item where id = :0", 1).Single(); + Assert.AreEqual(1, item.Id); + Assert.IsNull(item.Name); + } + + [Test] + public void InsertWithLongColumnMax4000chars() + { + dynamic item = new Entity("onmm.item_with_long"); + item.Text = new string('X', 1000 * 4); //max 4k when inserting! + orm.Insert(item); + + var items = orm.Query("select * from onmm.item_with_long"); + Assert.AreEqual(item.Text, items.Single().Text); + } + + [Test] + public void DeleteWithWhereClause() + { + dynamic item = new Entity("onmm.item"); + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm.item")); + + orm.Delete(item, "where id = :0", 1); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm.item")); + + orm.Delete(item, "where id = :0", 2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm.item")); + } + + [Test] + public void DeleteWithKeyMetadata() + { + dynamic item = new Entity("onmm.item", "id"); + item.Id = 2; + Assert.AreEqual(2, orm.QueryScalar("select count(1) from onmm.item")); + + orm.Delete(item); + Assert.AreEqual(1, orm.QueryScalar("select count(1) from onmm.item")); + + //check that the existing element is not the one deleted. + var items = orm.Query("select * from onmm.item"); + Assert.AreEqual(1, items.Single().Id); + } + + [Test] + public void QueryScalarWhenNoRows() + { + Assert.AreEqual(null, orm.QueryScalar("select 1 from onmm.item where 1=2")); + } + + [Test] + public void QueryScalarNullable() + { + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + Assert.AreEqual(null, orm.QueryScalar("select null from dual")); + } + + [Test] + public void QueryScalar() + { + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual(1, orm.QueryScalar("select 1 from dual")); + Assert.AreEqual("1", orm.QueryScalar("select 1 from dual")); + + Assert.AreEqual(DateTime.Today, orm.QueryScalar("select trunc(sysdate) from dual")); + Assert.AreEqual("test", orm.QueryScalar("select 'test' from dual")); + Assert.AreEqual(1.123456789m, orm.QueryScalar("select 1.123456789 from dual")); + } + + [Test] + public void UpdateViaWhereClause() + { + dynamic item = new Entity("onmm.item"); + item.Name = "RENAMED"; + orm.Update(item, "where id = :0", 1); + + var items = orm.Query("select id, name from onmm.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void UpdateViaMetaData() + { + dynamic item = new Entity("onmm.item", "id"); + item.Id = 1; + item.Name = "RENAMED"; + orm.Update(item); + + var items = orm.Query("select id, name from onmm.item order by id"); + Assert.AreEqual(items.First().Name, item.Name); + Assert.AreNotEqual(items.Last().Name, item.Name); + } + + [Test] + public void MergeInto() + { + dynamic item = new Entity("onmm.item", "id"); + item.Id = 100; + item.Name = "Name100"; + orm.MergeInto(item); + + var itemLoaded = orm.Query("select * from onmm.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + + item.Name = "renamed"; + orm.MergeInto(item); + + itemLoaded = orm.Query("select * from onmm.item where id = :0", 100).SingleOrDefault(); + Assert.AreEqual(item.Name, itemLoaded.Name); + } + + [Test] + public void MergeIntoWithOnlyPrimaryKey_ShouldNotTryUpdateAndFail() + { + dynamic item = new Entity("onmm.item", "id"); + item.Id = 100; + orm.MergeInto(item); + orm.MergeInto(item); + } + + [Test] + public void ColumnWithUnderScoreBeforeDigit() + { + Entity item = orm.Query("select yield_2date from onmm.item_odd where id = 1").Single(); + + //Assert.AreEqual(99m, item.Yield_2date); + + foreach (var p in item.GetDictionaryInDbStyle(true)) + Assert.AreEqual("yield_2date", p.Key); + + foreach (var p in item.GetDictionary()) + Assert.AreEqual("Yield_2date", p.Key); + } + + + [Test] + public void QueryDynamicToString() + { + var item = orm.Query("select * from onmm.item where id = 1").Single(); + Assert.That(item.ToString(), Contains.Substring("Id = 1")); + Assert.That(item.ToString(), Contains.Substring("Name = First Item")); + } + + [Test] + public void QuerySingleTypeList() + { + var ints = orm.QuerySingleTypeList("select id from onmm.item order by id").ToList(); + Assert.AreEqual(1, ints[0]); + Assert.AreEqual(2, ints[1]); + + var strings = orm.QuerySingleTypeList("select name from onmm.item order by id").ToList(); + Assert.AreEqual("First Item", strings[0]); + Assert.AreEqual("Second Item", strings[1]); + } + + [Test] + public void QueryStaticNoWhereClause() + { + var items = orm.Query("select * from onmm.item order by id").ToList(); + Assert.AreEqual(2, items.Count); + Assert.AreEqual(1, items[0].Id); + Assert.AreEqual(2, items[1].Id); + Assert.AreEqual("First Item", items[0].Name); + Assert.AreEqual("Second Item", items[1].Name); + Assert.AreEqual(0.321m, items[0].DecimalValue); + Assert.AreEqual(.123m, items[1].DecimalValue); + } + + [Test] + public void QueryDynamicWithWhereClause() + { + var item = orm.Query("select id, name from onmm.item where id = :0", 1).SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryStaticWithWhereClause() + { + var item = orm.Query("select id, name from onmm.item where id = :0", 1) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void QueryNonExisting() + { + var item = orm.Query("select id, name from onmm.item where id = :0", -1) + .SingleOrDefault(); + Assert.IsNull(item); + } + + + [Test] + public void ExecuteFunctionWithInt() + { + var result = orm.ExecuteFunction("onmm.plus1function", 100); + Assert.AreEqual(101, result); + } + + [Test] + public void ExecuteFunctionWithString() + { + var result = orm.ExecuteFunction("onmm.append1function", "100"); + Assert.AreEqual("1001", result); + } + + [Test] + public void ExecuteProcedureWithRefCursor() + { + var item = orm.ExecuteProcedure("onmm.get_items_by_name", "First Item", DbMapperParameter.RefCursor) + .SingleOrDefault(); + Assert.AreEqual(1, item.Id); + Assert.AreEqual("First Item", item.Name); + } + + [Test] + public void ExecuteProcedureWithRefCursorZeroElements() + { + var items = orm.ExecuteProcedure("onmm.get_items_by_name", "non existing", DbMapperParameter.RefCursor); + Assert.AreEqual(0, items.Count()); + } + + [Test] + public void ExecuteProcedureWithoutRefCursor() + { + string newName = "RENAMED"; + object result = orm.ExecuteProcedure("onmm.rename_item", 1, newName); + Assert.IsNull(result); + Assert.AreEqual(newName, orm.QueryScalar("select name from onmm.item where id = :0", 1)); + } + + [Test] + public void InsertWithImplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm.item", "id"); + item.id = 1; + + for (int i = 0; i < count; i++) + orm.Insert(item); + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm.item")); + Console.WriteLine("InsertWithImplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnection() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm.item")); + Console.WriteLine("InsertWithExplicitConnection Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertWithExplicitConnectionAndTransaction() + { + var sw = new Stopwatch(); + sw.Start(); + + int count = 1000; + dynamic item = new Entity("onmm.item", "id"); + item.id = 1; + + using (var connection = orm.OpenConnection()) + using (var tx = connection.BeginTransaction()) + { + for (int i = 0; i < count; i++) + orm.Insert(item); + tx.Commit(); + } + + Assert.AreEqual(count + 2, orm.QueryScalar("select count(1) from onmm.item")); + Console.WriteLine("InsertWithExplicitConnectionAndTransaction Ms used: " + sw.ElapsedMilliseconds); + } + + [Test] + public void InsertAndSelectBigClob() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + //insert + dynamic item = new Entity("onmm.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + + [Test] + public void InsertBigClobWithNonQuery() + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 5); + + orm.NonQuery("insert into onmm.bigclobtest (text) values (:0)", largeString); + + string largeStringFetched = orm.QueryScalar("select text from onmm.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + + [Test] + public void SelectWithExternalConnection() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + var r = orm.QueryScalar("select 1 from dual"); + Assert.AreEqual(1, r); + } + } + } + + [Test] + public void InsertWithExternalConnectionAndSelectBigClob() + { + Console.WriteLine("InsertWithExternalConnectionAndSelectBigClob"); + + using (OracleConnection oracleConnection = new OracleConnection(orm.ConnectionString)) + { + oracleConnection.Open(); + using (var conncetion = orm.SetExternalConnection(oracleConnection)) + { + //this is at least 5 MB + string largeString = new string('X', 1024 * 1024 * 1); + + //insert + dynamic item = new Entity("onmm.bigclobtest"); + item.Text = largeString; + orm.Insert(item); + + //select + item = orm.Query("select * from onmm.bigclobtest").SingleOrDefault(); + Assert.AreEqual(largeString, item.Text); + + //scalar + string largeStringFetched = orm.QueryScalar("select text from onmm.bigclobtest"); + Assert.AreEqual(largeString, largeStringFetched); + } + } + } + + [Test] + public void DeleteWithCompositeKeyt() + { + dynamic item1 = new Entity("onmm.item_composite_key", "id, type"); + item1.Id = 1; + item1.Type = 1; + orm.Insert(item1); + + dynamic item2 = new Entity("onmm.item_composite_key", "id,type"); + item2.Id = 1; + item2.Type = 2; + orm.Insert(item2); + + orm.Delete(item1); + var list = orm.Query("select * from onmm.item_composite_key"); + Assert.AreEqual(2, list.Single().Type); + + orm.Delete(item2); + Assert.AreEqual(0, orm.QueryScalar("select count(1) from onmm.item_composite_key")); + } + + [Test] + public void OverflowDynamic() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void Overflow() + { + Assert.Throws(() => orm.Query("select 1/3 decimal_value from dual").Single()); + Assert.Throws(() => orm.Query("select 999999999999999999999999999999 decimal_value from dual").Single()); + } + + [Test] + public void LoadStaticShouldWarn() + { + DbMapper.PrintWarnings = true; + DbMapper.PrintSqls = true; + var item = orm.Query("select id, name, decimal_value, date_value from onmm.item where id = :0", 1).SingleOrDefault(); + DbMapper.PrintWarnings = false; + DbMapper.PrintSqls = false; + } + + [Test] + public void CollectionShouldNotBeUsed() + { + ItemWithCollection item = new ItemWithCollection(); + item.Id = 3; + item.GroupsNotPresentInDb = new List() { 1 }; + orm.Insert(item, "onmm.item"); + } + + [Test] + public void ConnectAsSys() + { + var orm = new DbMapper(); + orm.ConnectAsSys("tstdaily", "bi"); + orm.QueryScalar("select 1 from dual"); + } + + [TestCase("1\n2")] + [TestCase("1\r\n2")] + [TestCase("1\n\r2")] + [TestCase("1\r2")] + public void ClobTests(string testValue) + { + dynamic item = new Entity("onmm.bigclobtest"); + item.Text = testValue; + orm.Insert(item); + + var fromDb = orm.QueryScalar("select text from onmm.bigclobtest"); + Assert.AreEqual(testValue, fromDb); + } + + [Test] + public void NonQueryIgnoreErrorShouldWork() + { + orm.NonQueryIgnoreError("should not work"); + } + + [Test] + public void ToEntityShouldIgnoreListProperties() + { + var entity = new SomeClass() + .ToEntity(); + + var dic = entity.GetDictionary(); + + Assert.AreEqual(4, dic.Count); + Assert.IsTrue(dic.ContainsKey("Prop1")); + Assert.IsTrue(dic.ContainsKey("Prop2")); + Assert.IsTrue(dic.ContainsKey("Prop3")); + Assert.IsTrue(dic.ContainsKey("Prop4")); + + } + } + + class ProcParameters + { + public decimal Input { get; set; } + public decimal EchoOutput { get; set; } + public decimal CountOutput { get; set; } + public object FundsOutput { get; set; } + } + + public class Item + { + public int Id { get; set; } + public string Name { get; set; } + public decimal DecimalValue { get; set; } + public DateTime? DateValue { get; set; } + } + + public class ItemWithCollection + { + public int Id { get; set; } + public List GroupsNotPresentInDb { get; set; } + } + + public class ItemWrongDefinition + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Decimalvalue { get; set; } + public DateTime? Datevalue { get; set; } + } + + class SomeClass + { + public string Prop1 { get; set; } + public decimal Prop2 { get; set; } + public DateTime Prop3 { get; set; } + public int? Prop4 { get; set; } + public List Col1 { get; set; } + public String[] Col2 { get; set; } + public Dictionary Col3 { get; set; } + } +} diff --git a/Tests/TestBase.cs b/Tests/TestBase.cs new file mode 100644 index 0000000..14d4362 --- /dev/null +++ b/Tests/TestBase.cs @@ -0,0 +1,124 @@ +using System; +using OdpNetMicroMapper; +using NUnit.Framework; + +namespace Tests +{ + [TestFixture] + public class TestBase + { + static bool firstRun = true; + protected DbMapper orm = new DbMapper(); + protected DbMapper sysOrm = new DbMapper(); + + private void CreateUser() + { + if (!firstRun) + return; + + firstRun = false; + + sysOrm.NonQueryIgnoreError("drop user onmm cascade"); + sysOrm.NonQuery("create user onmm identified by onmm"); + sysOrm.NonQuery("grant create session to onmm"); + sysOrm.NonQuery("alter user onmm quota unlimited on users"); + Console.WriteLine("user onmm created"); + + var sql = "create table onmm.bigclobtest (text clob)"; + sysOrm.NonQuery(sql); + + sql = "create table onmm.rawtest (bytes raw(16))"; + sysOrm.NonQuery(sql); + + sql = "create table onmm.item_with_long (text long)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm.item_odd (id number(10) not null, yield_2date number)"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm.item_composite_key (id number(10) not null, type number(10) not null, text varchar2(100))"; + sysOrm.NonQuery(sql); + + sql = @"create table onmm.item (id number(10) not null, name varchar2(100), decimal_value number, date_value timestamp default sysdate not null)"; + sysOrm.NonQuery(sql); + + sql = @"create sequence onmm.seq_items start with 3"; + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm.append1function (pString varchar2) return varchar2 + as + begin + return pString || '1'; + end;"; + //todo fix elsewhere? //http://boncode.blogspot.com/2009/03/oracle-pls-00103-encountered-symbol.html + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace function onmm.plus1function (pNumber number) return integer + as + begin + return pNumber + 1; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm.get_items_by_name(pName in varchar2, io_cursor out SYS_REFCURSOR) is + begin + open io_cursor for + select * from onmm.item t where t.name = pName; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm.rename_item(pId number, pName in varchar2) is + begin + update onmm.item t set t.name = pName where t.id = pId; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + + sql = @"create or replace procedure onmm.proc_with_many_out_parameters(pInput number, pEcho out number, pCount out number, io_cursor out SYS_REFCURSOR) is + begin + select pInput into pEcho from dual; + select count(1) into pCount from onmm.fund; + open io_cursor for + select * from onmm.fund; + end;"; + sql = sql.Replace(Environment.NewLine, "\n"); + sysOrm.NonQuery(sql); + } + + [TearDown] + public void DropSchema() + { + } + + [SetUp] + public void CreateSchema() + { +// throw new Exception("Please set 'data source' and 'password' in function CreateSchema()"); + + sysOrm.ConnectAsSys("intshare", "bi"); + orm.ConnectionString = "data source=intshare;user id=onmm;password=onmm;"; + + CreateUser(); + + sysOrm.NonQuery("delete from onmm.item_composite_key"); + sysOrm.NonQuery("delete from onmm.item"); + sysOrm.NonQuery("delete from onmm.bigclobtest"); + sysOrm.NonQuery("delete from onmm.item_with_long"); + sysOrm.NonQuery("delete from onmm.item_odd"); + sysOrm.NonQuery("delete from onmm.rawtest"); + + var sql = "insert into onmm.item_odd (id, yield_2date) values (1, 99)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm.item (id, name, decimal_value) values (1, 'First Item', 0.321)"; + sysOrm.NonQuery(sql); + + sql = "insert into onmm.item (id, name, decimal_value) values (2, 'Second Item', 0.123)"; + sysOrm.NonQuery(sql); + + } + } +} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj new file mode 100644 index 0000000..a970dfe --- /dev/null +++ b/Tests/Tests.csproj @@ -0,0 +1,27 @@ + + + + net461 + + + + + + + + + + + + + + ..\..\..\Oracle\Product\11.2.0.3.0_32\odp.net\bin\2.x\Oracle.DataAccess.dll + + + + + + + + + diff --git a/changeLog.txt b/changeLog.txt new file mode 100644 index 0000000..e69de29 From a25e83098875d701da1d8f0d0cc4898669540e0e Mon Sep 17 00:00:00 2001 From: MHJ Date: Wed, 28 Mar 2018 20:52:27 +0000 Subject: [PATCH 05/10] Fix reference git-svn-id: http://svn/svn/IT_Udvikling/Tools/OdpNetMicroMapper/trunc@77636 9010fbff-3bac-c549-a0fa-7ef7ff2f0957 --- Tests/Tests.csproj | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index a970dfe..90dd3db 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -6,19 +6,16 @@ - + + + ..\..\..\..\Oracle\Product\11.2.0.3.0_32\odp.net\bin\2.x\Oracle.DataAccess.dll + - - - ..\..\..\Oracle\Product\11.2.0.3.0_32\odp.net\bin\2.x\Oracle.DataAccess.dll - - - From 167fc752375ff26ef35eaadd6e6d78a3cba0b7c6 Mon Sep 17 00:00:00 2001 From: MHJ Date: Sat, 31 Mar 2018 12:40:42 +0000 Subject: [PATCH 06/10] Add missing file git-svn-id: http://svn/svn/IT_Udvikling/Tools/OdpNetMicroMapper/trunc@77637 9010fbff-3bac-c549-a0fa-7ef7ff2f0957 --- OdpNetMicroMapper/DbMapper.cs | 512 ++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 OdpNetMicroMapper/DbMapper.cs diff --git a/OdpNetMicroMapper/DbMapper.cs b/OdpNetMicroMapper/DbMapper.cs new file mode 100644 index 0000000..e41bf67 --- /dev/null +++ b/OdpNetMicroMapper/DbMapper.cs @@ -0,0 +1,512 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Data; +using System.Reflection; +using System.Configuration; +using System.IO; + +namespace OdpNetMicroMapper +{ + public enum DbMapperParameter + { + RefCursor + } + + public class DbMapper + { + public static volatile bool PrintWarnings = false; + public static volatile bool PrintSqls = false; + + public string ConnectionString { get; set; } + private Type _oracleConnectionType, _oracleDataAdapterType, _oracleClobType; + private Connection _connectionWhenCreateExternal; + + public DbMapper() + { + ConnectionString = ConfigurationManager.AppSettings["ConnectionString"]; + FindOracleDataTypes(); + } + + + private void FindOracleDataTypes() + { + var prefix = "Oracle.ManagedDataAccess"; + var assembly = FindDataAccessAssembly(prefix); + if (assembly == null) + { + prefix = "Oracle.DataAccess"; + assembly = FindDataAccessAssembly(prefix); + } + if (assembly == null) throw new FileNotFoundException($"Unable to load assembly {prefix}"); + + _oracleDataAdapterType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleDataAdapter"); + _oracleConnectionType = TypeFromAssembly(assembly, $"{prefix}.Client.OracleConnection"); + _oracleClobType = TypeFromAssembly(assembly, $"{prefix}.Types.OracleClob"); + } + + private Assembly FindDataAccessAssembly(string partialName) + { + try + { + return Assembly.Load(partialName); + } + catch + { + return null; + } + } + + private Type TypeFromAssembly(Assembly assembly, string typeAsString) + { + var type = assembly.GetType(typeAsString, false); + return type; + } + + /// + /// Use this to establish a SYSDBA connection + /// + public void ConnectAsSys(string dataSource, string password) + { + ConnectionString = string.Format("data source={0}; user id=sys; password={1}; dba privilege=sysdba", dataSource, password); + } + + public Connection SetExternalConnection(IDbConnection connection) + { + _connectionWhenCreateExternal = CreateOrReuseConnection(connection); + return _connectionWhenCreateExternal; + } + + public Connection OpenConnection() + { + if (_connectionWhenCreateExternal != null) + throw new Exception("Cannot open new Connection. Already open"); + _connectionWhenCreateExternal = CreateOrReuseConnection(); + return _connectionWhenCreateExternal; + } + + private Connection CreateOrReuseConnection(IDbConnection connection = null) + { + if (_connectionWhenCreateExternal != null) + { + _connectionWhenCreateExternal.NextLevel(); + return _connectionWhenCreateExternal; + } + + if (connection == null) + { + connection = (IDbConnection)Activator.CreateInstance(_oracleConnectionType); + connection.ConnectionString = ConnectionString; + connection.Open(); + } + return new Connection(this, connection); + } + + internal void ReleaseConnection() + { + _connectionWhenCreateExternal = null; + } + + public IDbDataAdapter CreateOracleDataAdapter() + { + return (IDbDataAdapter)Activator.CreateInstance(_oracleDataAdapterType); + } + + public object CreateClob(string text, IDbConnection connection) + { + var clob = Activator.CreateInstance(_oracleClobType, connection); + var method = _oracleClobType.GetMethod("Append", new[] { typeof(char[]), typeof(int), typeof(int) }); + method.Invoke(clob, new object[] { text.ToCharArray(), 0, text.Length }); + return clob; + } + + public IDbCommand CreateCommand(string sql, IDbConnection connection, bool bindByName = true) + { + if (PrintSqls) + Console.WriteLine(sql); + + var cmd = connection.CreateCommand(); + + if (bindByName) + cmd.GetType().GetProperty("BindByName") + .SetValue(cmd, true, null); + + cmd.GetType().GetProperty("InitialLONGFetchSize") + .SetValue(cmd, 1024 * 64, null); //reade up to 64kb with long columns + + if (sql != null) + cmd.CommandText = sql; + + return cmd; + } + + private void SetParameter(IDbDataParameter parameter, object value, IDbCommand cmd, Type type = null) + { + if (value == null) + { + parameter.Value = DBNull.Value; + if (type != null) + { + if (type == typeof(string)) + { + parameter.DbType = DbType.String; + parameter.Size = 4096; + } + } + } + else + { + if (value is float || value is double || value is Decimal) + { + parameter.DbType = DbType.Decimal; + parameter.Value = Convert.ToDecimal(value); + } + else if (value is int || value is bool) + { + parameter.DbType = DbType.Int32; //ok? + parameter.Value = Convert.ToInt32(value); + } + else if (value is DateTime) + { + parameter.DbType = DbType.DateTime; + parameter.Value = Convert.ToDateTime(value); + } + //todo: check for real enum value + else if (value is DbMapperParameter) + { + parameter.Direction = ParameterDirection.Output; + SetParameterOracleDbType(parameter, "RefCursor"); + } + else if (value is byte[]) + { + SetParameterOracleDbType(parameter, "Raw"); + parameter.Value = value; + } + //strings + else + { + if (((string)value).Length > 4000) + { + SetParameterOracleDbType(parameter, "Clob"); + parameter.Value = CreateClob(value.ToString(), cmd.Connection); + } + else + { + parameter.DbType = DbType.String; + parameter.Value = Convert.ToString(value); + } + } + } + } + + public void Insert(object item, string dbName) + { + var entity = item.ToEntity(); + entity.TableName = dbName; + Insert(entity); + } + + public void Insert(Entity item) + { + var sqlTokens = new SqlTokens(item.GetDictionaryInDbStyle(true)); + var sql = "insert into " + item.TableName + + " (" + sqlTokens.AsColumnNames(false) + ") select " + sqlTokens.AsIndcies(false) + " from dual"; + + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, sqlTokens.GetNonNullableFieldsAndValues(), 0); + command.ExecuteNonQuery(); + } + } + } + + public void MergeInto(Entity item) + { + var dic = item.GetWhereClauseOnPrimaryKeyDbStyle(); + var args = dic.Values.ToArray(); + var whereClause = new SqlTokens(dic).AsWhereClause(); + + var sql = "select count(1) from " + item.TableName + " " + whereClause; + + var count = QueryScalar(sql, args); + + if (count == 0) + Insert(item); + else + Update(item); + } + + private void AddParameters(IDbCommand cmd, object[] args) + { + AddParameters(cmd, args.ToDictionary(x => Guid.NewGuid().ToString(), x => x), 0); + } + + private void AddParameters(IDbCommand cmd, IDictionary columnsAndValues, int offset, bool useKeyNames = false) + { + var index = offset; + foreach (var o in columnsAndValues) + { + var parameter = cmd.CreateParameter(); + if (useKeyNames) + parameter.ParameterName = o.Key; + else + parameter.ParameterName = index.ToString(); + if (o.Key.EndsWith("Output")) + parameter.Direction = ParameterDirection.Output; + SetParameter(parameter, o.Value, cmd); + cmd.Parameters.Add(parameter); + index++; + } + } + + public void Delete(Entity item, string whereClause = null, params object[] args) + { + //build where clause from metadata + if (whereClause == null) + { + var dic = item.GetWhereClauseOnPrimaryKeyDbStyle(); + var sqlTokens = new SqlTokens(dic); + whereClause = sqlTokens.AsWhereClause(); + args = dic.Values.ToArray(); + } + + var sql = "delete from " + item.TableName + " " + whereClause; + + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + command.ExecuteNonQuery(); + } + } + } + + public void Update(Entity item, string whereClause = null, params object[] args) + { + //build where/set clause from metadata + if (whereClause == null) + { + var dic = item.GetWhereClauseOnPrimaryKeyDbStyle(); + args = dic.Values.ToArray(); + whereClause = new SqlTokens(dic).AsWhereClause(); + } + + var nonPrimaryKeysColumns = item.GetDictionaryInDbStyle(false); + + if (nonPrimaryKeysColumns.Count == 0) + return; + + var setClauseTokens = new SqlTokens(nonPrimaryKeysColumns); + + var sql = "update " + item.TableName + + " " + setClauseTokens.AsSetClause(args.Length) + + " " + whereClause; + + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + AddParameters(command, item.GetDictionaryInDbStyle(false), args.Length); + command.ExecuteNonQuery(); + } + } + } + + public T QueryScalar(string sql, params object[] args) + { + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + var result = command.ExecuteScalar(); + + + var isString = typeof(T) == typeof(string); + var isNullableType = typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition(); + + //NULL handle + if (isNullableType || isString) + { + if (result == DBNull.Value || result == null) + { + return default(T); //this is mostly null :) + } + } + + if (isNullableType) + { + var type = Nullable.GetUnderlyingType(typeof(T)); + return (T)Convert.ChangeType(result, type); + } + + return (T)Convert.ChangeType(result, typeof(T)); + } + } + } + + public void NonQueryIgnoreError(string sql, params object[] args) + { + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + + try + { + command.ExecuteNonQuery(); + } + catch (Exception ex) + { + Console.WriteLine("Db Exception ignored: " + ex.Message); + } + } + } + } + + public void NonQuery(string sql, params object[] args) + { + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + command.ExecuteNonQuery(); + } + } + } + + public IEnumerable Query(string sql, params object[] args) where T : new() + { + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + yield return reader.ToObject(); + } + } + } + } + } + + public IEnumerable Query(string sql, params object[] args) + { + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + yield return reader.ToEntity(); + } + } + } + } + } + + public IEnumerable QuerySingleTypeList(string sql, params object[] args) + { + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(sql, connection.GetAdoConnection())) + { + AddParameters(command, args); + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + yield return (T)Convert.ChangeType(reader.GetValue(0), typeof(T)); + } + } + } + } + } + + public IEnumerable ExecuteProcedure(string procedureName, params object[] args) where T : new() + { + dynamic d = ExecuteProcedure(procedureName, args); + return d.RecordToObject(); + } + + public T ExecuteFunction(string functionName, params object[] args) + { + using (var connection = CreateOrReuseConnection()) + { + using (var cmd = CreateCommand(null, connection.GetAdoConnection(), false)) + { + cmd.CommandText = functionName; + cmd.CommandType = CommandType.StoredProcedure; + + //return value + var parameter = cmd.CreateParameter(); + SetParameter(parameter, default(T), cmd, typeof(T)); + parameter.Direction = ParameterDirection.ReturnValue; + cmd.Parameters.Add(parameter); + + AddParameters(cmd, args); + cmd.ExecuteNonQuery(); + + return (T)Convert.ChangeType(parameter.Value, typeof(T)); + } + } + } + + public IEnumerable ExecuteProcedure(string procedureName, params object[] args) + { + using (var connection = CreateOrReuseConnection()) + { + using (var command = CreateCommand(null, connection.GetAdoConnection(), false)) + { + var ds = new DataSet(); + command.CommandText = procedureName; + command.CommandType = CommandType.StoredProcedure; + AddParameters(command, args); + + //Execute + var da = CreateOracleDataAdapter(); + da.SelectCommand = command; + da.Fill(ds); + + if (ds.Tables.Count < 1) + return null; + + var list = new List(); + + foreach (DataRow row in ds.Tables[0].Rows) + list.Add(row.ToEntity()); + return list; + } + } + } + + /// + /// Set OracleDbType on a OracleParameter without a reference to Oracle DataAccess + /// + /// Clob, Blob etc. + /// + public void SetParameterOracleDbType(IDbDataParameter parameter, string type) + { + var pOracleDbType = parameter.GetType().GetProperty("OracleDbType"); + + var enums = Enum.Parse(pOracleDbType.PropertyType, type); + + pOracleDbType.SetValue(parameter, enums, null); + } + } +} \ No newline at end of file From bd0bf570e8e98e086afdc38193477f566c5cb3e9 Mon Sep 17 00:00:00 2001 From: MHJ Date: Sat, 31 Mar 2018 13:02:17 +0000 Subject: [PATCH 07/10] Reference git-svn-id: http://svn/svn/IT_Udvikling/Tools/OdpNetMicroMapper/trunc@77638 9010fbff-3bac-c549-a0fa-7ef7ff2f0957 --- Tests/Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 90dd3db..e8eb7e3 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -9,6 +9,7 @@ ..\..\..\..\Oracle\Product\11.2.0.3.0_32\odp.net\bin\2.x\Oracle.DataAccess.dll + true From ba64f95ebae71603afa5a6aa2154db3b4deeb6f4 Mon Sep 17 00:00:00 2001 From: MHJ Date: Sat, 31 Mar 2018 13:09:25 +0000 Subject: [PATCH 08/10] Reference git-svn-id: http://svn/svn/IT_Udvikling/Tools/OdpNetMicroMapper/trunc@77639 9010fbff-3bac-c549-a0fa-7ef7ff2f0957 --- Tests/Tests.csproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index e8eb7e3..39e2036 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -7,9 +7,8 @@ - - ..\..\..\..\Oracle\Product\11.2.0.3.0_32\odp.net\bin\2.x\Oracle.DataAccess.dll - true + + False From cf426bc16e5f9b10f12b227cb20d001a04ae174d Mon Sep 17 00:00:00 2001 From: MHJ Date: Sat, 31 Mar 2018 13:18:10 +0000 Subject: [PATCH 09/10] Reference git-svn-id: http://svn/svn/IT_Udvikling/Tools/OdpNetMicroMapper/trunc@77640 9010fbff-3bac-c549-a0fa-7ef7ff2f0957 --- Tests/App.config | 16 +++++++++------- Tests/Tests.csproj | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Tests/App.config b/Tests/App.config index 0e83688..6d74e72 100644 --- a/Tests/App.config +++ b/Tests/App.config @@ -4,11 +4,13 @@ - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 39e2036..46e7b74 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -8,6 +8,7 @@ + C:\Oracle\Product\11.2.0.3.0_32\odp.net\bin\2.x\Oracle.DataAccess.dll False From 4d7786bf0fd996af31d974070f8b74a08c2eceea Mon Sep 17 00:00:00 2001 From: Michael Hjorth Date: Tue, 3 Apr 2018 13:00:37 +0200 Subject: [PATCH 10/10] Add test adapter --- TestManagedCore/TestManagedCore.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TestManagedCore/TestManagedCore.csproj b/TestManagedCore/TestManagedCore.csproj index 97e9e26..d52e569 100644 --- a/TestManagedCore/TestManagedCore.csproj +++ b/TestManagedCore/TestManagedCore.csproj @@ -5,7 +5,9 @@ + +