diff --git a/AsbCloud.sln b/AsbCloud.sln index 3539f040..f19612c2 100644 --- a/AsbCloud.sln +++ b/AsbCloud.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp1", "ConsoleApp1\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsbCloudDb", "AsbCloudDb\AsbCloudDb.csproj", "{40FBD29B-724B-4496-B5D9-1A5D14102456}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataTable", "DataTable\DataTable.csproj", "{28AD7CD5-17A0-448C-8C16-A34AE5DE40FB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {40FBD29B-724B-4496-B5D9-1A5D14102456}.Debug|Any CPU.Build.0 = Debug|Any CPU {40FBD29B-724B-4496-B5D9-1A5D14102456}.Release|Any CPU.ActiveCfg = Release|Any CPU {40FBD29B-724B-4496-B5D9-1A5D14102456}.Release|Any CPU.Build.0 = Release|Any CPU + {28AD7CD5-17A0-448C-8C16-A34AE5DE40FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28AD7CD5-17A0-448C-8C16-A34AE5DE40FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28AD7CD5-17A0-448C-8C16-A34AE5DE40FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28AD7CD5-17A0-448C-8C16-A34AE5DE40FB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ConsoleApp1/Header.cs b/ConsoleApp1/Header.cs deleted file mode 100644 index 805652dd..00000000 --- a/ConsoleApp1/Header.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ConsoleApp1 -{ - public class Header - { - public string Name { get; set; } - public System.Type PropertyType { get; set; } = typeof(object); - } -} \ No newline at end of file diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 8d52f1b9..34665dfc 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -20,21 +20,21 @@ namespace ConsoleApp1 { static void Main(string[] args) { - var options = new DbContextOptionsBuilder() - .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") - .Options; - var context = new AsbCloudDbContext(options); + //var options = new DbContextOptionsBuilder() + // .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") + // .Options; + //var context = new AsbCloudDbContext(options); - var e = new Event - { - IdEvent = 1, - IdTelemetry = 1, - IdCategory = 1, - MessageTemplate = "template", - }; + //var e = new Event + //{ + // IdEvent = 1, + // IdTelemetry = 1, + // IdCategory = 1, + // MessageTemplate = "template", + //}; - context.Events.Update(e).State = EntityState.Added; - context.SaveChanges(); + //context.Events.Update(e).State = EntityState.Added; + //context.SaveChanges(); //var table = new Table diff --git a/DataTable/DataTable.csproj b/DataTable/DataTable.csproj new file mode 100644 index 00000000..20827042 --- /dev/null +++ b/DataTable/DataTable.csproj @@ -0,0 +1,8 @@ + + + + Exe + net5.0 + + + diff --git a/DataTable/Header.cs b/DataTable/Header.cs new file mode 100644 index 00000000..471ce6cc --- /dev/null +++ b/DataTable/Header.cs @@ -0,0 +1,13 @@ +using System; + +namespace DataTable +{ + [Serializable] + public class Header + { + public string Name { get; set; } + public string Desctiption { get; set; } + //public System.Type ColumnType { get; set; } = typeof(object); + public string ColumnType { get; set; } + } +} \ No newline at end of file diff --git a/DataTable/Program.cs b/DataTable/Program.cs new file mode 100644 index 00000000..323f75c6 --- /dev/null +++ b/DataTable/Program.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text.Json; + +namespace DataTable +{ + class Program + { + static void Main(string[] args) + { + var listDtos = new List(100); + + /* *** Size of TestDto calc *** + * + * ** data binary size (Not implemented) ** + * Date : DateTime : 4b + * Mode : int32? : 4b + * User : string : 2-64b + * WellDepth : double? : 8b + * TOTAL data size = 18-80b + * + * ** header binary size (Not implemented) ** + * + *DateTime : len("DateTime") + 2 + len("Date") + 2 = 16b + * Mode : 14b + * User : 14b + * WellDepth : 20b + * columnsCount : 2b + * TOTAL header size = 66b + * + * ** json as tab ** + * + * header size: 137b + * json row size: 63b + * + * total for 1 record : 200b + * total for 2 records : 263b + * + * ** json as list ** + * + * Raw listItem size: 175b + * total for 2 records : 350b + * + * ** example 100 records ** + * tabJsonBytes: 6602 + * listJsonBytes: 9628 + * tabBinBytes: 7765 (by dangerous BinaryFormatter) + * listBinBytes: 4366 (by dangerous BinaryFormatter) + */ + + for (int i = 0; i < 100; i++) + listDtos.Add(new TestDto{ + Date = DateTime.Now.AddSeconds(i), + Mode = 1, + WellDepth = i * Math.PI + }); + + var tMapper = new TableMapper(); + + var tab = tMapper.MakeTable(listDtos); + + var e = tMapper.AsEnumerable(tab); + + var tabJson = JsonSerializer.Serialize(tab); + var listJson = JsonSerializer.Serialize(listDtos); + + var tabJsonBytes = JsonSerializer.SerializeToUtf8Bytes(tab); + var listJsonBytes = JsonSerializer.SerializeToUtf8Bytes(listDtos); + + var formatter = new BinaryFormatter(); + var mem = new MemoryStream(); + formatter.Serialize(mem, tab); + var tabBinBytes = new byte[mem.Length]; + Array.Copy(mem.GetBuffer(), tabBinBytes, mem.Length); + + mem = new MemoryStream(); + formatter.Serialize(mem, listDtos); + var listBinBytes = new byte[mem.Length]; + Array.Copy(mem.GetBuffer(), listBinBytes, mem.Length); + + Console.WriteLine($"tabJsonBytes:{tabJsonBytes.Length}"); + Console.WriteLine($"listJsonBytes:{listJsonBytes.Length}"); + Console.WriteLine($"tabBinBytes:{tabBinBytes.Length}"); + Console.WriteLine($"listBinBytes:{listBinBytes.Length}"); + } + } +} diff --git a/ConsoleApp1/TableMapper.cs b/DataTable/PropertyHelper.cs similarity index 61% rename from ConsoleApp1/TableMapper.cs rename to DataTable/PropertyHelper.cs index 51ee517f..2d7c563e 100644 --- a/ConsoleApp1/TableMapper.cs +++ b/DataTable/PropertyHelper.cs @@ -1,61 +1,33 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.ComponentModel; -namespace ConsoleApp1 +namespace DataTable { - // для работы с таблицами - public class TableMapper - { - private readonly Dictionary props; - - public TableMapper() - { - var props = typeof(T).GetProperties(); - this.props = new Dictionary(props.Length); - foreach (var prop in props) - { - var helper = new PropertyHelper(prop); - this.props.Add(helper.Id, helper); - } - - } - - public int UpdateObjectFromTable(ref T obj, Table table, int rowIndex) - { - var updatesCount = 0; - if (table.Rows.Count() <= rowIndex) - return 0; - - var row = table.Rows.ElementAt(rowIndex); - var headerIndex = 0; - - foreach (var header in table.Headers) - { - var headerId = PropertyHelper.MakeIdentity(header.PropertyType.Name, header.Name); - if (props.ContainsKey(headerId)) - { - props[headerId].Set(obj, row.ElementAt(headerIndex)); - updatesCount++; - } - headerIndex++; - } - return updatesCount; - } - } - /// /// Ускоренный обработчик свойства /// class PropertyHelper { + delegate void SetterDelegate(object instanse, object[] values); + + delegate object GetterDelegate(object instanse); + + public string PropertyName { get; } + public string PropertyDesctiption { get; } + public Type PropertyType { get; } + public string Id { get; } + + SetterDelegate Setter { get; } + GetterDelegate Getter { get; } public PropertyHelper(PropertyInfo property) { PropertyName = property.Name; PropertyType = property.PropertyType; Id = MakeIdentity(property.PropertyType.Name, property.Name); + PropertyDesctiption = GetDescription(property); var setter = property.SetMethod; var getter = property.GetMethod; @@ -65,21 +37,15 @@ namespace ConsoleApp1 var argumentExpressions = new List { Expression.Convert(Expression.ArrayIndex(argumentsExpression, Expression.Constant(0)), PropertyType) }; var callExpression = Expression.Call(Expression.Convert(instanceExpression, setter.ReflectedType), setter, argumentExpressions); Setter = Expression.Lambda(callExpression, instanceExpression, argumentsExpression).Compile(); - callExpression = Expression.Call(Expression.Convert(instanceExpression, getter.ReflectedType), getter); Getter = Expression.Lambda(Expression.Convert(callExpression, typeof(object)), instanceExpression).Compile(); } - public string PropertyName { get; } - public Type PropertyType { get; } - public string Id { get; } - - delegate void SetterDelegate(object instanse, object[] values); - - delegate object GetterDelegate(object instanse); - - SetterDelegate Setter { get; } - GetterDelegate Getter { get; } + private string GetDescription(PropertyInfo property) + { + var descriptionAttr = property.GetCustomAttribute(); + return descriptionAttr?.Description ?? string.Empty; + } void SetValues(object instance, params object[] values) => Setter(instance, values); @@ -90,6 +56,7 @@ namespace ConsoleApp1 public object Get(object instance) => Getter(instance); - public static string MakeIdentity(string propertyTypeName, string propertyName) => $"{propertyTypeName}_{propertyName}".ToLower(); + public static string MakeIdentity(string propertyTypeName, string propertyName) + => $"{propertyTypeName}_{propertyName}".ToLower(); } } diff --git a/ConsoleApp1/Table.cs b/DataTable/Table.cs similarity index 64% rename from ConsoleApp1/Table.cs rename to DataTable/Table.cs index 01d88410..b5f8f5ae 100644 --- a/ConsoleApp1/Table.cs +++ b/DataTable/Table.cs @@ -1,13 +1,14 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; -namespace ConsoleApp1 +namespace DataTable { + [Serializable] public class Table { public IEnumerable
Headers { get; set; } public IEnumerable> Rows { get; set; } - } } diff --git a/DataTable/TableMapper.cs b/DataTable/TableMapper.cs new file mode 100644 index 00000000..e41b5821 --- /dev/null +++ b/DataTable/TableMapper.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; + +namespace DataTable +{ + public class TableMapper + where T: new() + { + private readonly Dictionary props; + + public TableMapper() + { + var props = typeof(T).GetProperties(); + this.props = new Dictionary(props.Length); + foreach (var prop in props) + { + var helper = new PropertyHelper(prop); + this.props.Add(helper.Id, helper); + } + } + + public IEnumerable AsEnumerable(Table table) + { + if ((table?.Headers is null) || + (table?.Rows is null) || + (!table.Headers.Any()) || + (!table.Rows.Any()) || + (!props.Any()) ) + yield break; + + var headerIdToProp = new Dictionary(props.Count); + foreach(var (_, propHelper) in props) + for(var i = 0; i < table.Headers.Count(); i++) + { + var header = table.Headers.ElementAt(i); + var columnType = System.Type.GetType(header.ColumnType); + if (columnType.IsAssignableTo(propHelper.PropertyType) && + ((header.Name == propHelper.PropertyName) || + (header.Name.ToLower() == propHelper.PropertyName.ToLower()))) + headerIdToProp.Add(i, propHelper); + } + + if (!headerIdToProp.Any()) + yield break; + + foreach (var row in table.Rows) + { + var obj = new T(); + foreach (var (i, propHelper) in headerIdToProp) + propHelper.Set(obj, row.ElementAt(i)); + yield return obj; + } + + yield break; + } + + public Table MakeTable(IEnumerable data) + { + var table = new Table(); + table.Headers = props.Select(pair => new Header + { + Name = pair.Value.PropertyName, + ColumnType = pair.Value.PropertyType.Name, + }).ToArray(); + + var rows = new List>(data.Count()); + foreach(var dataItem in data) + rows.Add(MakeRow(dataItem)); + + table.Rows = rows; + + return table; + } + + private List MakeRow(T dataItem) + { + var colunms = new List(props.Count); + foreach (var (_, propHelper) in props) + { + var propValue = propHelper.Get(dataItem); + colunms.Add(propValue); + } + return colunms; + } + + } +} diff --git a/DataTable/TestDto.cs b/DataTable/TestDto.cs new file mode 100644 index 00000000..4264e0c9 --- /dev/null +++ b/DataTable/TestDto.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataTable +{ + [Serializable] + public class TestDto + { + [Description("Дата")] + public DateTime Date { get; set; } = DateTime.Now; + + public int? Mode { get; set; } + + [Description("Пользователь")] + public string User { get; set; } + + [Description("Глубина забоя")] + public double? WellDepth { get; set; } + } +}