first commit

This commit is contained in:
Thibaud Gasser 2020-06-09 00:12:01 +02:00
commit 0f335617b4
13 changed files with 542 additions and 0 deletions

16
ConsoleApp1.sln Normal file
View File

@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1\ConsoleApp1.csproj", "{C1AF7B05-8631-49ED-88F5-946446E9169F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C1AF7B05-8631-49ED-88F5-946446E9169F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C1AF7B05-8631-49ED-88F5-946446E9169F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1AF7B05-8631-49ED-88F5-946446E9169F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1AF7B05-8631-49ED-88F5-946446E9169F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,13 @@
using System;
namespace ConsoleApp1.Model
{
public class Person
{
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string GetFullName() => $"{FirstName} {LastName}";
}
}

View File

@ -0,0 +1,15 @@
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
namespace ConsoleApp1.Model
{
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
Id(x => x.Id, m => m.Generator(Generators.GuidComb));
Property(x => x.FirstName);
Property(x => x.LastName);
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Xml.Serialization;
using NHibernate.Mapping.ByCode;
using NUnit.Framework;
namespace ConsoleApp1.Model
{
[TestFixture]
public class PersonMapTest
{
[Test]
public void CanGenerateXmlMapping()
{
var mapper = new ModelMapper();
mapper.AddMapping<PersonMap>();
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
var xmlSerializer = new XmlSerializer(mapping.GetType());
xmlSerializer.Serialize(Console.Out, mapping);
}
}
}

View File

@ -0,0 +1,23 @@
using NUnit.Framework;
namespace ConsoleApp1.Model
{
[TestFixture]
public class PersonTest
{
[Test]
public void GetFullNameTest()
{
var person = new Person
{
FirstName = "Test",
LastName = "Kees"
};
Assert.AreEqual("Test", person.FirstName);
Assert.AreEqual("Kees", person.LastName);
Assert.AreEqual("Test Kees", person.GetFullName());
}
}
}

View File

@ -0,0 +1,173 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using ConsoleApp1.Model;
namespace ConsoleApp1.ORM
{
public class BulkQuery
{
public static void BulkInsert<TEntity>(IEnumerable<TEntity> entries)
{
var entityType = typeof(TEntity);
var properties = entityType.GetProperties().Select(p => p.Name);
BuildBulkInsertQuery(entityType.Name, properties, entries);
}
private static void GetParametersValues(string tableName, IEnumerable<string> propertyNames, IEnumerable entries)
{
static object GetPropertyValue(object o, string propertyName)
{
//Debug.Assert(propertyName != null, nameof(propertyName) + " != null");
var type = o.GetType();
var property = type.GetProperty(propertyName);
//Debug.Assert(property != null, nameof(property) + " != null");
var propertyValue = property?.GetValue(o);
return propertyValue;
}
var fields = propertyNames as string[] ?? propertyNames.ToArray();
var i = 0;
foreach (var entry in entries)
{
++i;
Console.WriteLine($"Entry number {i}");
foreach (var propertyName in fields)
{
var propertyValue = GetPropertyValue(entry, propertyName);
Console.WriteLine($"Property name={propertyName}, value={propertyValue}");
}
Console.WriteLine();
}
}
private static void BuildBulkInsertQuery(string tableName,
IEnumerable<string> propertyNames,
IEnumerable entries,
int maxBatchSize = 5)
{
using var session = NHibernateHelper.OpenSession();
var cmd = session.Connection.CreateCommand();
var columns = propertyNames as string[] ?? propertyNames.ToArray();
var queryString = $"INSERT INTO {tableName} ({string.Join(',', columns)}) VALUES\n";
var sb = new StringBuilder();
DbParameter CreateDbParameter(object value)
{
var pm = cmd.CreateParameter();
pm.Value = value;
return pm;
}
static object GetPropertyValue(object o, string propertyName) => o.GetType().GetProperty(propertyName)?.GetValue(o);
var batchSize = 0;
var p = 0;
foreach (var entry in entries)
{
// Build the names of the parameters
var row = new StringBuilder("(");
for (var ii = 0; ii < columns.Length; ii++)
{
row.Append($"@p{p + ii}");
if (ii < columns.Length - 1) row.Append(",");
// Set parameter value
var value = GetPropertyValue(entry, columns[ii]);
cmd.Parameters.Add(CreateDbParameter(value));
}
p += columns.Length;
row.Append(")");
// Add the row to our running SQL batch
if (batchSize > 0) sb.AppendLine(",");
sb.Append(row);
batchSize += 1;
if (batchSize >= maxBatchSize)
{
var queryStringComplete = queryString + sb + ";";
cmd.CommandText = queryStringComplete;
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
sb.Clear();
batchSize = 0;
p = 1;
}
}
// handle the last few stragglers
if (batchSize > 0)
{
var queryStringComplete = queryString + sb + ";";
cmd.CommandText = queryStringComplete;
cmd.ExecuteNonQuery();
}
}
public static void BulkInsert1(IEnumerable<Person> persons)
{
using var session = NHibernateHelper.OpenSession();
var cmd = session.Connection.CreateCommand();
var queryString = "INSERT INTO Person (Id, FirstName, LastName) VALUES\n";
var sb = new StringBuilder();
var batchSize = 0;
var p = 1; //the current paramter name (i.e. "@p1") we're going to use
DbParameter CreateDbParameter(object value)
{
var pm = cmd.CreateParameter();
pm.Value = value;
return pm;
}
foreach (var person in persons)
{
//Build the names of the parameters
var pId = $"@p{p}"; //the "Id" parameter name (i.e. "p1")
var pFirstName = $"@p{p + 1}";
var pLastName = $"@p{p + 2}";
p += 3;
//Build a single "(p1, p2)" row
var row = $"({pId}, {pFirstName}, {pLastName})"; //a single values tuple
//Add the row to our running SQL batch
if (batchSize > 0) sb.AppendLine(",");
sb.Append(row);
batchSize += 1;
// Add the parameter values for this row
cmd.Parameters.AddRange(new[] {
CreateDbParameter(person.Id),
CreateDbParameter(person.FirstName),
CreateDbParameter(person.LastName)
});
if (batchSize >= 5)
{
var queryStringComplete = queryString + sb;
cmd.CommandText = queryStringComplete;
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
sb.Clear();
batchSize = 0;
p = 1;
}
}
//handle the last few stragglers
if (batchSize > 0)
{
var queryStringComplete = queryString + sb;
cmd.CommandText = queryStringComplete;
cmd.ExecuteNonQuery();
}
}
}
}

View File

@ -0,0 +1,48 @@
using System.Collections.Generic;
using ConsoleApp1.Model;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
namespace ConsoleApp1.ORM
{
public static class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static Configuration _configuration;
private static HbmMapping _mapping;
public static ISession OpenSession()
{
//Open and return the nhibernate session
return SessionFactory.OpenSession();
}
public static ISessionFactory SessionFactory => _sessionFactory ??= Configuration.BuildSessionFactory();
public static Configuration Configuration => _configuration ??= CreateConfiguration();
public static HbmMapping Mapping => _mapping ??= CreateMapping();
private static Configuration CreateConfiguration()
{
var configuration = new Configuration();
//Loads properties from hibernate.cfg.xml
configuration.Configure();
//Loads nhibernate mappings
configuration.AddDeserializedMapping(Mapping, null);
return configuration;
}
private static HbmMapping CreateMapping()
{
var mapper = new ModelMapper();
//Add the person mapping to the model mapper
mapper.AddMappings(new List<System.Type> { typeof(PersonMap) });
//Create and return a HbmMapping of the model mapping in code
return mapper.CompileMappingForAllExplicitlyAddedEntities();
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
namespace ConsoleApp1.ORM
{
[TestFixture]
public class SchemaTest
{
[Test]
public void CanGenerateSchema()
{
var schemaUpdate = new SchemaUpdate(NHibernateHelper.Configuration);
schemaUpdate.Execute(Console.WriteLine, true);
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using ConsoleApp1.Model;
using ConsoleApp1.ORM;
namespace ConsoleApp1.Repository
{
public interface PersonRepository
{
/// <summary>
/// Get person entity by id
/// </summary>
/// <param name="id">id</param>
/// <returns>person</returns>
Person Get(Guid id);
/// <summary>
/// Save person entity
/// </summary>
/// <param name="person">person</param>
void Save(Person person);
/// <summary>
/// Update person entity
/// </summary>
/// <param name="person">person</param>
void Update(Person person);
/// <summary>
/// Delete person entity
/// </summary>
/// <param name="person">person</param>
void Delete(Person person);
/// <summary>
/// Row count person in db
/// </summary>
/// <returns>number of rows</returns>
long RowCount();
void Save(IEnumerable<Person> persons);
}
public class PersonRepositoryNHibernate : PersonRepository
{
public void Save(IEnumerable<Person> persons)
{
BulkQuery.BulkInsert(persons);
}
public void Save(Person person)
{
using var session = NHibernateHelper.OpenSession();
using var transaction = session.BeginTransaction();
session.Save(person);
transaction.Commit();
}
public Person Get(Guid id)
{
using var session = NHibernateHelper.OpenSession();
return session.Get<Person>(id);
}
public void Update(Person person)
{
using var session = NHibernateHelper.OpenSession();
using var transaction = session.BeginTransaction();
session.Update(person);
transaction.Commit();
}
public void Delete(Person person)
{
using var session = NHibernateHelper.OpenSession();
using var transaction = session.BeginTransaction();
session.Delete(person);
transaction.Commit();
}
public long RowCount()
{
using var session = NHibernateHelper.OpenSession();
return session.QueryOver<Person>().RowCountInt64();
}
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using ConsoleApp1.Model;
using ConsoleApp1.ORM;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;
namespace ConsoleApp1.Repository
{
[TestFixture]
public class NHibernatePersonRepositoryTest
{
private PersonRepository _personRepo;
[SetUp]
public void CreateSchema()
{
DeleteDatabaseIfExists();
var schemaUpdate = new SchemaUpdate(NHibernateHelper.Configuration);
schemaUpdate.Execute(false, true);
_personRepo = new PersonRepositoryNHibernate();
}
[Test]
public void CanSavePerson()
{
_personRepo.Save(new Person());
Assert.AreEqual(1, _personRepo.RowCount());
}
[Test]
public void CanGetPerson()
{
var person = new Person();
_personRepo.Save(person);
Assert.AreEqual(1, _personRepo.RowCount());
person = _personRepo.Get(person.Id);
Assert.IsNotNull(person);
}
[Test]
public void CanUpdatePerson()
{
var person = new Person();
_personRepo.Save(person);
Assert.AreEqual(1, _personRepo.RowCount());
person = _personRepo.Get(person.Id);
person.FirstName = "Test";
_personRepo.Update(person);
Assert.AreEqual(1, _personRepo.RowCount());
Assert.AreEqual("Test", _personRepo.Get(person.Id).FirstName);
}
[Test]
public void CanDeletePerson()
{
var person = new Person();
_personRepo.Save(person);
Assert.AreEqual(1, _personRepo.RowCount());
_personRepo.Delete(person);
Assert.AreEqual(0, _personRepo.RowCount());
}
[Test]
public void TestBulkInsert()
{
static IEnumerable<Person> GeneratePersons(int num = 3)
{
var ret = new Person[num];
for (var ii = 0; ii < num; ii++) ret[ii] = new Person {Id = Guid.NewGuid(), FirstName = "bac", LastName = "fif"};
return ret;
}
_personRepo.Save(GeneratePersons(10));
}
[TearDown]
public void DeleteDatabaseIfExists()
{
if (File.Exists("test.db"))
File.Delete("test.db");
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory name="NHibernate.Test">
<property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</property>
<property name="connection.connection_string">Data Source=test.db;Version=3;New=True</property>
<property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>

View File

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v3.1", FrameworkDisplayName = "")]

View File

@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("ConsoleApp1")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("ConsoleApp1")]
[assembly: System.Reflection.AssemblyTitleAttribute("ConsoleApp1")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Généré par la classe MSBuild WriteCodeFragment.