From 39be65f3545d64a3bd3601ad94a5506de3024925 Mon Sep 17 00:00:00 2001 From: "MIDDLEEAST\\muleithy" Date: Wed, 7 Feb 2018 13:57:21 +0200 Subject: [PATCH] Performance fixes: * Compile Automapper mappings * Geofence intersections checks in DB * Minor SQL query optmiziations * Update instead of append GeofenceUpdates Bug Fixes: * Fix bug where Geofence geometry was inverted if user specified geofence points right-handedly --- .../GeoFenceUpdateData.cs | 9 +- ...ofenceUpdates-UniqueGeofenceId.Designer.cs | 29 ++++ ...124064_GeofenceUpdates-UniqueGeofenceId.cs | 34 +++++ ...4064_GeofenceUpdates-UniqueGeofenceId.resx | 126 ++++++++++++++++++ .../Trackable.EntityFramework.csproj | 7 + .../TrackableDbContext.cs | 2 +- .../src/Trackable.Models/GeoFenceUpdate.cs | 2 + .../Helpers/GeoFenceExtensions.cs | 21 --- .../Helpers/GeographyHelper.cs | 59 ++++---- .../IGeoFenceRepository.cs | 2 +- ...sitory.cs => IGeoFenceUpdateRepository.cs} | 4 +- .../Repositories/DbRepositoryBase.cs | 1 - .../Repositories/GeoFenceRepository.cs | 23 ++-- .../Repositories/GeoFenceUpdateRepository.cs | 28 ++-- .../Repositories/TrackingPointRepository.cs | 39 +++--- .../RepositoriesExtensions.cs | 5 +- .../Trackable.Repositories.csproj | 8 +- .../Services/GeoFenceService.cs | 74 ++++++---- .../Services/TrackingPointService.cs | 4 - 19 files changed, 350 insertions(+), 127 deletions(-) create mode 100644 Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.Designer.cs create mode 100644 Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.cs create mode 100644 Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.resx delete mode 100644 Backend/src/Trackable.Repositories/Helpers/GeoFenceExtensions.cs rename Backend/src/Trackable.Repositories/{INotificationUpdateRepository.cs => IGeoFenceUpdateRepository.cs} (64%) diff --git a/Backend/src/Trackable.EntityFramework/GeoFenceUpdateData.cs b/Backend/src/Trackable.EntityFramework/GeoFenceUpdateData.cs index 8ef1577..30d1139 100644 --- a/Backend/src/Trackable.EntityFramework/GeoFenceUpdateData.cs +++ b/Backend/src/Trackable.EntityFramework/GeoFenceUpdateData.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Data.Entity.Spatial; namespace Trackable.EntityFramework { @@ -10,9 +7,13 @@ namespace Trackable.EntityFramework public class GeoFenceUpdateData : EntityBase { [Required] + [Index("IX_GeoFenceDataId")] + [Index("GeoFenceAssetUnique", IsUnique = true, Order = 0)] public int GeoFenceDataId { get; set; } [Required] + [Index("IX_AssetDataId")] + [Index("GeoFenceAssetUnique", IsUnique = true, Order = 1)] public string AssetDataId { get; set; } [Required] diff --git a/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.Designer.cs b/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.Designer.cs new file mode 100644 index 0000000..13bf70b --- /dev/null +++ b/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.Designer.cs @@ -0,0 +1,29 @@ +// +namespace Trackable.EntityFramework.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.1.3-40302")] + public sealed partial class GeofenceUpdatesUniqueGeofenceId : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(GeofenceUpdatesUniqueGeofenceId)); + + string IMigrationMetadata.Id + { + get { return "201802071124064_GeofenceUpdates-UniqueGeofenceId"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.cs b/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.cs new file mode 100644 index 0000000..f5cce1c --- /dev/null +++ b/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.cs @@ -0,0 +1,34 @@ +namespace Trackable.EntityFramework.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class GeofenceUpdatesUniqueGeofenceId : DbMigration + { + public override void Up() + { + DropIndex("dbo.GeoFenceUpdates", new[] { "GeoFenceDataId" }); + DropIndex("dbo.GeoFenceUpdates", new[] { "AssetDataId" }); + + //For uniqueness constraint to pass + Sql(@" + DELETE FROM GeoFenceUpdates + WHERE Id <> (SELECT max(Id) + FROM GeofenceUpdates g2 + WHERE g2.GeoFenceDataId = GeoFenceUpdates.GeoFenceDataId and g2.AssetDataId = GeoFenceUpdates.AssetDataId)"); + + CreateIndex("dbo.GeoFenceUpdates", new[] { "GeoFenceDataId", "AssetDataId" }, unique: true, name: "GeoFenceAssetUnique"); + CreateIndex("dbo.GeoFenceUpdates", "GeoFenceDataId"); + CreateIndex("dbo.GeoFenceUpdates", "AssetDataId"); + } + + public override void Down() + { + DropIndex("dbo.GeoFenceUpdates", new[] { "AssetDataId" }); + DropIndex("dbo.GeoFenceUpdates", new[] { "GeoFenceDataId" }); + DropIndex("dbo.GeoFenceUpdates", "GeoFenceAssetUnique"); + CreateIndex("dbo.GeoFenceUpdates", "AssetDataId"); + CreateIndex("dbo.GeoFenceUpdates", "GeoFenceDataId"); + } + } +} diff --git a/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.resx b/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.resx new file mode 100644 index 0000000..222f9b5 --- /dev/null +++ b/Backend/src/Trackable.EntityFramework/Migrations/201802071124064_GeofenceUpdates-UniqueGeofenceId.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + H4sIAAAAAAAEAO1dW2/cOJZ+X2D/Q6GedhsZl53uWfQE9gzcTjptTBIbKWezb4FcostCq6QaXdI2FvPL9mF/0v6FJXXl5fAmUVJVuRAgcInkIXn48fCQPOfw//7nf8//9rQJZ99RkgZxdDE/Ozmdz1C0iv0gWl/M8+zhTz/P//bXf/2X83f+5mn2n3W+H0k+XDJKL+aPWbZ9s1ikq0e08dKTTbBK4jR+yE5W8Wbh+fHi9enpXxZnZwuEScwxrdns/HMeZcEGFT/wz6s4WqFtlnvhx9hHYVp9xynLgursk7dB6dZboYv5XeKtfvfuQ3TyDtPInn9NcNofcfL7fHYZBh5u0BKFD/OZF0Vx5mW4uW++pGiZJXG0Xm7xBy+8e94inO/BC1NUdeNNm920R6evSY8WbcGa1CpPs3hjSfDsx4pFC754J0bPGxZiJpaMIr0uGHkxv0xTlN0m8RYlWYDSt17mzWd8vW+uwoSUUbD8BCD0aibN/qoBEMYZ+fdqdpWHWZ6giwjlWeKFr2a3+X0YrP6Onu/i31F0EeVhSPcF9wanMR/wp6oFz5/RQ9XDa38+W7DlFnzBphhVpuzwdZT9+Ho++4QrJ/1ooEIxZ5nFCXqPIpR4GfJvvSxDSURooKLPQu1cXQXjfkPB+jGrK30b464jo4JfAz977FDuA4rWnQp+7drSyyc8nTm2GhTDEApCjBX7kku0CepSv8RxiLzIqNxH7+l94vkBHr4O3fwYRHd5En3GBPLUrvxbFCKMIKHNAvjUZK4SRIB4md1hufolWzWNwF/Jp05ovoo327xoHFf5J+97sC6KQtyYzz6jsEhNH4NtKZBLUUEExDdOaMxnvybx5nMc1sWhTN/uvGRNKN/FupzLOE9WHL/OF60M1EvGnvLwUKQgXjOxFjCf4alRCg6sH7z+GQ9X8IT8+kuFpS9RgJUGXChLcghrJnMeV6oTwC9uKtHTxP2kqqeKflLV08+08e9R/CvWIxEhBjedzvGtqZhpuCSLIAtk+SBJoGr0BzxiaXYbp0GpdykZzmeG+M3mUbCby2jL7U9xFjwEqyLptwCLreRZyfMvWx9X2LILYjqfR8p1IaMt2wtJiSXNW/Q9IOWUbOczQ2xn8yjYzmWE2G68ctAg7LZ40BQOYf0YXosm/ytWK/znEKvVuw1WTtPx6y3A0X+VvMLLoh//EV1HWGvFSxKtZP/HT9bkbuPweU0EYEkEg3ideNtH7dgd4GI9yGLHSy/dojjCmlF/0iwb9SeTlaPJ20t7Fwn3k8QtnaM8NpHHNDb19RrsCVhC421IlpgfOX/6cNyNuFcqZdJNqn3abkCGkmkGDRfkXyeZVuuot3EQ9TiZEMgcJZqJRCt3BmSepZm32VLzr5u29CFeVYf6luoS/vk98FHiQO00ayFfYVcheJ+v+4rAy9Uqx1h9tjtYXW5RK30Nz3JDjIncR3alfkFeORY2he6SYCsA2WBFnGI1ZLfJU7TgpaylgpQWl1JJFmFBkuWzXUgNTmfEquSnNLq8Bv0wOLVRdyjYSroRbIsKiqpSttVMkrCj4dN77WLY/vVf8ls6h7Dmjyd0HJ0uqSspDB4Gr+X2MY6G78vNlohGTHv5nGZoM3h9FWIHr+elrD69TtnlF7QSAe/wVkaUdIrrGW1m6RIkL9Fzh1cuHl2lfFn6EGT78Pu5ZeYlFvNPcxEQ+a5IvWDdvhiSetfZ99QQD4krUi9F6je6K7DVoFMAscgk2+rh1FCp28VkBFpHpcvbSGeybWm9H+i3Z5A2jdtTmLaKmTfqxnFZgTYyOeRNZbMNspWsalLtIMEs8kY72C9+QGv55R6V4Vu5uWQbS6eBO0cmQ6+tYz003dUJmsJRpRjRCEGjH/h+glIXRgcDnU9/DKJgk2++ekFWrmoWB5s4E8K9yz6g7+1e2Kjk/q3RVruCSjB03xhUBI4TeZ/3Bp9jDCPrCXn5HbNhjcBbEOs9QrmS9tWoj5r+GJq+iV5daxwK1RrIAqpYUL5hVNm6JqU2C2ZSNryvTqu4TTDWClXtY9TGTkvJVRw9BOs86akaCmR2Y1nBmc748ZJlFBUK7QpUkh/7UKRs6/jXrOkKo41WAIc7vPfCfHi9ef9Eu/Gsfou2Yfy8wbrNtd99UvNUdmNO91MV3+eB/wKUAGOkkDWlO0Lq0i8WGaNs8PcPfiqt6EtaOO8CahFJKdQaAitaJ2IShGMyNrXXGVlNqttsqEu/2NlQeL8opsNPfz7dW2OMq9ALNqnyEuw428XZXk7l7pOd3/3AoqDbKRqZXj3O0OriL3a6j6OmX6eXqyz4jvpOiMOaVyb3ZTVAVRdmkjziqYMso+2hCJnAmuaWWaBGkhRF04pkG5lwmabxKihawrsWCV7zbP/eRf7M2IW+nCCMGRWeKFgQBFs89XG7LuanJyfCSYVRJY02xFXSZjGr7nxBMUPNI5nHoqzxWvfFtumspzTb5h8U/NGGAzAYgh96M0Rwc9I1V+7zJLKEdly0Z4zcZd+AM2dzfv25iUpxOiOymYSxuvLSleeL0hNPPJ/9gpcslJBbFy+8wgIAL4JBlInrWxCtgq0XGnaDK2+4QpL2NTXxKW/RFkXkeshwzEyawDguim1pquR4qGNZP6g27m0WGBJ93QYErOgsbCYwdhK2fGemQS4/fiat4D14J8GvzAtGBietS0wLJcCb0hy2OpcaMzHL0+8EOk1TRgCchunGknJylMmNxnU40NuD2yBPox1amK2LldKuRA6VRK3jl/lMkm1NBpu6MvcFG+YNNpnhxk0yq+GBMWmKaFo+0Txn7JvlCIGNnWk81H4aNrgDbaT3QyGHGz8KCqGx2IclBTBZ10IDtLBxBDvIKoeizdoLDyTc5E0ZE0oAl02q58zMpoVV5WSgHXXe40APJq3yIfF/7rU898MT28kxocQywWwpLKM9TAsf1nBOO9YSEzpHkgk2u5tINoGNGRNSIK9NGiDY1E4LMWP9X+PD4whkbpT8ndHDJtwWKMdrz3YDkPWzCkpKU2gWqY3Hhh1YVUbU44tERWtGwpqC4/uktMEW6yZAMFp7e4JtV1ZgZXtGBtwBrMOM04EJDlgPBKcQY11e9Yv6bqy1UOtHxiE9JqYbjEnvbKSB8WUw0Qa/d3h2ro/I38l6oxN7DDVkfTR7lwYnLvTkXvxhLQVlDZaYDbatbG17zeWVxPC4Jdqaz7u8OpEZbknFqtaKixqs1urRQm7rrL9GvFdizcT0LWZtxpwxgrE1M4CZRadLWzUsdTMsc1FCb5KIXeHbe5KEnjLAbhXXXpmuppV1Jd8TQnqJMlr2UEZus9ZOTmVMJvAHoColZkSisDSASLAGHxoqrOWDihxtrKIhyqw4EE1gSTIkWc4ZFU16VmmJBluYVK1gaQjUKitEhNX/DVqCtRdZYxpFUkOGcTKFiAHOrBqStIsbQFD0o9PQI1ICalm7TmgIVN4xAoFWrOh4TSQUyOlW3nEkKDHEzeH2oo2WEVR+7VtTvL5pYzfbdK2VKIL+amMiy9Oj+8TvGFieGPBL+oyHyC0j+1mmbzoLWqpnteBUcEpnLKvlew/2iC8ByPmjNqcFuyQ1qKX6JCwKBqySms+OxKv2CQIjdsEmnbqeCUad7pgmmHBqEduBb9II2SLPjMwIuaMLtSEh1SFeQVCwSmc2OAS8DCKiyhlmaBEHdlJvE9ePiXrzN4B+o3INAD/e78YEh6otnwY6ks2fK2RKtn4D85QNQAlxUG7CBV/8KeZtoTArmQKabA0zS6EQl4r+S2+s4B5AF1bdeAFdTFGUqF2EO6bUAFbwAzKIgTvAmcR04wJn/WI86fpwgYt2pGCG4pYJ7g58ydSNNfBl0tAQMRG+BpYJcJdMRK0xf6YSrGCkL5hN2mtxq4txjk3l0YCGU6pr8AGhJAkspuaSzWxT3+r24dTo044NaaZmkXgDaXYH2YchzI2jbqp24IH0nkt5YmKsRmtvxbopetoLsCG0G+mdl5JVxnJae0PW9WhpClHNRcoQGaS4IDO4IqPaXh0+KjgBX4hRJKoD0P7yRBq8AJArRvdldjdm9IhWJ6oqKaO7Hxt2LeciJ6g4JF6amVybdeUGc0mmA5qi53X8huZOrEk7XyxXj2jjVR/OFzjLCm2z3AuL55PSOuGjt91irqdtyerLbLn1VuR24U/L+expE0bpxfwxy7ZvFou0IJ2ebIJVEqfxQ3ayijcLz48Xr09P/7I4O1tsShqLFbPR4G/wmpqymMTd5VLJxYaPfg2StJAx9x6JGXLlb4RswA2g5C6grhC+5BNHrr4qqMuRv7lbRyH+DXQxKJqjVBRxsTW5RilYgOQH8TAVTGe58kIvAeLXXMVhvonkRjby0kXVv6Fg/ZixZJgES3pfAz97BMhV3y2plXFzAHJ1gm3rZL392q23l0+IvHYvkKu+W1LDWAvCQjAIBNskS5pLtAkAeuVnS1ofvaf3iecHGMcASSbVlnIQ3eVJ9BkXz6Huc+nm1JugRzTJ5qM5HTHqEU1QTBUpny84UcBLoYUghrjVgZdu5rLPqcjrI+hGF2+kV9B0Kj4fYWQMo+qOyg2MGBMWeySpiw8DJvI/W778Yk6hiA3Jybb6mzmVouMiqKnPFmCM49CP/4iuIyxe8+JSk0GjmGxO+zYOn9dk509TbD4eJ57xxOPvnd3OQMrqq/s8VBEZZjbykXhoSrooPXKqzQrFk2QSzOktMy/jtZn623EGGM8A7ijPzQQQTRTt8W9AYxj4lycnZKTSzNtshcGE0s2pt4f8NM32q8UikMTfA5+cyDCrQPPVvk0wRTHVhpf3+ZpnX/HJQnCsVjnGwjMnNZqvFiKjfJGIkRjlJ4vWhBihuc+rvc1Xc0q/IK+MaUsTaj6a06k9/GkyMq9/Rb/q8CWCaLajIzrbsu3SueKqsHQU1czRrltZTZl+dxfWKiK7unUoDnJZEtUnC1lcvnLPCOLykzmNmy33hj1NTUg0p9u8VU/Taz4e55/F/CMXua5mXeUl0WWuyYoOM8PYxwk5dTtRjZN0s069UMhs2Knvh7RyCR7aAhNV7ttKPsqoauIQyGm++Fnemq24memMK5P9bFcX39U1tXk0mJmT9ccpdkvCK8HMes8nmtPlXhBmmM4mHeeg1Upb2F65W2xrL8Bu66209Mtccqt3gWlC1ScLEcG8EszICSbFbiMMHTCqIlRoxuC4aO+HwOB8d92IDdHf1154GNCQDUb5/Cs9APB7s0oKrwUKwkO0amBRz7Ky4KISLPai5ZtOzE60/HQEuzHYGa9yN1AXPNHtka4nMdRx/QuHQ2kT6wYHTSAB+/GXF93VTcqLR05pJ+sGOU0ECXvkyIsOg5zqHVHBVGRc9LUvfjJoab4ecWy+XSyNxh1tFptAJh22ivKyw0DZhT7VvoDJtKP5egSimMy6EgA2oa0bNx0GxsoKVCzewbqd+E3IDHFkkXFEbhohV3rkXgTIrAv3aZ80IGbnmaVsGt67+YWv2uw6Ja+uNi+uGvSa9zaxRpE04I+tCShTWGXsKTUhBsZIEzGo4wgxVB0gSROJqA/OOzSxNyQMIqt0u/AXyNhf7wPcN47L0nEcOGIO8GIc6WWnJJCWD72BJ/c8tlvQdDCzkUA6d+WdAZXOAfrFYon3vLXDEl/aicjSOXbvjGZk+hzHASpGnL+6zdlEVcT0/AEYAdBBvSPbCxIOUAH63e8UFCQ97a8QSb34rTb6elki29ZDOoTGV7/jwHDEXCg7mhgFO4UgbfcdYqmMcmCHoLKMG9zQUQ06DkJBwilG6HgNO4UMSVf1eBCCPvBZmjOk6kvzuwn6UAVcYCJBFNwgcR0KLqRV8Ac+AkOZZT6rfSou5qVd7wnJcLL8R3gVlp7edYaPXhQ8YNWsGJGL+evTs9fz2WUYeGkZg6OKLfGGD9luFGzi7EcSbAL5mwVf3D5kBaGSpn4IBKwgU4rWZFQnV+d/RwIeapyonj05X/AFzwFYlnGrA8LgYs6+RxExr0b+rZdlKIlILlS0eT4jyCOzv0HfQkmeie5Q1vMQxl5GEyoi55vQqcI69CVTh3Po3RxH3ariN1Cj0IFIG7OhH50yVkNJ4z7oRoMJztCXO1w0hh7kmqN3sHdGcBYP20taxCc1K0wlJRPoKt5s86LyLhOonaTRdy9ZPXoJ8BrFdeSjp4v5fxcF38yu/+tbVfbV7CbBMvPN7HT2z/kMD045AS7mZ69/VvOQvhfQC7BpxFbLEXnPzDldurWDU8iIyF5iDDjmoDhgADSBAAs5JxCTB2LY28WxvCNnYfxvG+/p360p1bEdXNCiwjt0nwdAPId6Rqy70GuiOZRU1iheJ9728fkA1wDrGSELibC384KPqmAhjOqihTD/EgX/yFEri17hnVP57c3sDoNl9k9KhrGVsmLLfP1gm2y+WqvafaZsN1Wtenk36kQdMuKgV0DjGaYJurC3EwyK2dBLPLcuSD3l820TVUGzlJkQE0M19IF1EaehO6jbCA09tjCVE0ifPVUTmqEHkSYsQw8adUwGO02zLGWuXk66oTLChehJ26FhPBGnLTxEiS67B92rHavR/sFkTlQBHhxQqsI8OKB0s+VCPDig2YR5cEBrD2bFbu309/VsCw4wsbe6H+syywPO9tyB8pftSeq4LncaSdpD1m4qc8VlE9kUB90bwhTu1Yw9kMnGYkce6WJvRY8zfaUJneFwk9h76ypEzeh8EcdFyuhM55CmgzTsxN7Ohl1diKvQFT1nAxu1QnJOYLgQt/Eq7A8LypK9VpXjOnvIgkUTksJMvMChKOCMQsQJvSQqyTs+PSib4pgoEwjDxY69dN98Gft1Y8yqg0uMsSLmxXVUUKx9DwE5Wn/BIgSO9bAXw+BMJT+k8YQ9IPZiPKvIDeZHFfzNMrMC/PTn0+kQ1cZ+eBnwhDTxylFEBg4Dpa+iMYBBlsScfy+miTu1og0M0R1ZewlOwAel9wGpAFSbA32xiY0zRI/5U9EY2KCxl/2sPk6AWEblta+fiUKNdhtRrnivrSjbka53B2BLrLZCsmGn/G6EFiufCoVeRK47yL7UjFuah1mwDYMVbsDF/PTkRNiM8tTaWmG6fDpbww8CeYwslJCJ5YV4Q51miReIUWZukyBaBVsvFPrF5TRcMgjXG5p8Ct4joYjMdUW3TapVeCs3VXDTR8cMxhtLDRabyCLUGDPG0vTgsgnsqJ4Jfnk3Ubk0zcgaR7ynrrx05YnvuhR+ayZtgZEsyTEI5jTPATqHnVFImY5ifUoAQh6Z3UXWi4DeqOKuO+50AX1GBF1r2V5Cb0/gRhnkQ1ijkw8HaMrXHRUo2yWQ1Z/2al2dGm1TraiWgNM91TkK5gSHAtdyTa72i74MNEkgdf9Fk8m7m7um8OvD0Q00wJpdI2A4DZGnkwcBULcxdQQk5aOAYq1G0dMmkTpciJ/BBn33BVLHoZ1EMukfXRsJTqVZzV7o5I31Nguu+uMhLHLSpxN3b22rgENZIknhw9i/0oPHJtjIm/GRoHntblowaN5XGhcS0IPrXQfPUJWZeuGxGKqJlhv41exxgcGYTx6lxWTSQvvG27iwGE+DPRi1ZCpV1wJkO6ThVs4G0+kqtbcDj5Dm+8HIIPljpLurtNTwmHB5OgJkt9epGiLkbzeK7YCL0TRgGlcJtgOS6p3cUTAkfSlimhNgy/OcfT7vtTvA2ZVjXulrEI5GdE9uCCa5YrLUqac9h2MfgJCNZ+PVQo9i+9EKG41HBU2r/TgIDhSPtzqHgeK5T7EuxUsXY2gmsnceBpvXOrnR+BEwZNuvh7QZVrymqdsN7wJkiocdnEzynQSF1TQeFQqKJyycA+Ad+xBE4yfNPdog4KB66INe4ngz8tZGXmVsXj4HcTH372M8/KXJPW8rL0IHqF5VqaYqfQ2syZdQCZsM1VPnMK+KtuWSVkhnUlVb5tNXDuwbhLqBPFDVTDbzmumlRVo1nUlVd5nPpPJ6Dw5UWSfBFQVbPXn24Eeogk2GqqlzmPWk2caDnWlSZf3BGfT1iJEUxNqAPFCdTDZ9zYI/vFixmAWql86lrbXVhYXa2iSoFpKq71S7cgrk2ySIfPmOvRYV7VoqYqJNAxFRPjBuIyBV4liSTyW7mKxiS9jHjrR+VzMqP79EqJ20ZNvphhXMV2Ht1jhl8VT4dP6Ij+22AUs03kUAX+xeOpZaTlM9YxMULFLCSSAlZbtDNlU+MBZsgt8xc4Sh6Rkk+GsoWKP27XDOFFF1YjhCJw/BjsazwIwjsCPCQBNqTNbIDOABthjZyjvDiVTLXdLP51KpzliheHhczhRD83CXXdQeCgHE6OQBsMO/VGoCItWpl9suTo4vxkwXZI7ckNfhxGL3UVV/64/uukkbaqg6KzXoYJoN7c+KprMJu9Dxejet6DNkXdm9sbsCbNb2QtV/hZXGvo66iewzsJUbVOQNzwrISkvCCq1B1wBI4I5dGg40350ywWg+GBgu7T0jCosbTf9FqxxnAnGM7kqNQ5SHGZPoipbaQy9m6KWimZWE833FmIoye78P8EBhACAxAaCa3H5UdJs/vCxKth/7z3bZLTY0641uvIdcCPmj1pJI+9UhO4obWiUTxDvc7iM3UDeFx+ibtPNFeSxcfcA/hUfnzxef84jEvCt/vUVpsG5JnGOaEVoxd55NnuvoIa7vXrkW1Vn4oP8o83xympZkwYO3Ike0K5SmxeNoRZBBEpLzHvnX0U2ebfOMCI3NffhMM4Nc4arqP18IbT6/KUIspy66gJsZkBOem+iXPAj9pt2/AkHOJCTI3XAVWpCMZUZCDK6fG0qfijehTAhV7GuutO/QZhuSNesmWnokyKJ92zB08arrrZ5vm1cAZUT0A8Gy/fxt4K0Tb5NWNNry+CfGsL95+uv/Awjwwg7bTQEA + + + dbo + + \ No newline at end of file diff --git a/Backend/src/Trackable.EntityFramework/Trackable.EntityFramework.csproj b/Backend/src/Trackable.EntityFramework/Trackable.EntityFramework.csproj index c0f645b..87ac141 100644 --- a/Backend/src/Trackable.EntityFramework/Trackable.EntityFramework.csproj +++ b/Backend/src/Trackable.EntityFramework/Trackable.EntityFramework.csproj @@ -167,6 +167,10 @@ 201711131016182_CreateTokenTable.cs + + + 201802071124064_GeofenceUpdates-UniqueGeofenceId.cs + @@ -249,6 +253,9 @@ 201711131016182_CreateTokenTable.cs + + 201802071124064_GeofenceUpdates-UniqueGeofenceId.cs + diff --git a/Backend/src/Trackable.EntityFramework/TrackableDbContext.cs b/Backend/src/Trackable.EntityFramework/TrackableDbContext.cs index 337b670..39869bd 100644 --- a/Backend/src/Trackable.EntityFramework/TrackableDbContext.cs +++ b/Backend/src/Trackable.EntityFramework/TrackableDbContext.cs @@ -41,7 +41,7 @@ public TrackableDbContext(string connString) : base(connString) public DbSet AssetProperties { get; set; } public DbSet DeploymentId { get; set; } - + public DbSet Tokens { get; set; } public override int SaveChanges() diff --git a/Backend/src/Trackable.Models/GeoFenceUpdate.cs b/Backend/src/Trackable.Models/GeoFenceUpdate.cs index a11f4fc..d108dbf 100644 --- a/Backend/src/Trackable.Models/GeoFenceUpdate.cs +++ b/Backend/src/Trackable.Models/GeoFenceUpdate.cs @@ -1,9 +1,11 @@ using System; +using Trackable.Models.Helpers; namespace Trackable.Models { public class GeoFenceUpdate : ModelBase { + [Mutable] public NotificationStatus NotificationStatus { get; set; } = NotificationStatus.Unknown; public DateTime UpdatedAt { get; set; } diff --git a/Backend/src/Trackable.Repositories/Helpers/GeoFenceExtensions.cs b/Backend/src/Trackable.Repositories/Helpers/GeoFenceExtensions.cs deleted file mode 100644 index e27b9af..0000000 --- a/Backend/src/Trackable.Repositories/Helpers/GeoFenceExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Trackable.Models; - -namespace Trackable.Repositories.Helpers -{ - public static class GeoFenceExtensions - { - public static bool IsTriggeredByPoint(this GeoFence geofence, IPoint latestPoint) - { - var intersects = GeographyHelper.CreatePolygon(geofence.FencePolygon).MakeValid().Intersects(GeographyHelper.CreateDbPoint(latestPoint)); - - if (geofence.FenceType == FenceType.Inbound) - { - return intersects; - } - else - { - return !intersects; - } - } - } -} diff --git a/Backend/src/Trackable.Repositories/Helpers/GeographyHelper.cs b/Backend/src/Trackable.Repositories/Helpers/GeographyHelper.cs index 6ea37f0..44e7697 100644 --- a/Backend/src/Trackable.Repositories/Helpers/GeographyHelper.cs +++ b/Backend/src/Trackable.Repositories/Helpers/GeographyHelper.cs @@ -1,11 +1,10 @@ -using System; +using Microsoft.SqlServer.Types; +using System; using System.Collections.Generic; using System.Data.Entity.Spatial; +using System.Data.SqlTypes; using System.Globalization; using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; using Trackable.Models; namespace Trackable.Repositories.Helpers @@ -22,6 +21,18 @@ public static DbGeography CreateDbPoint(IPoint point) EarthSRID); } + public static DbGeography CreateDbMultiPoint(IPoint[] points) + { + var distinctPoints = points.Distinct(new PointEqualityComparer()); + var pointsString = String.Join(", ", distinctPoints.Select(p => string.Format(CultureInfo.InvariantCulture, + "({0} {1})", p.Longitude, p.Latitude))); + + + return DbGeography.MultiPointFromText( + string.Format(CultureInfo.InvariantCulture, "MULTIPOINT({0})", pointsString), + EarthSRID); + } + public static DbGeography CreateDbLine(IEnumerable points) { var distinctPoints = points.Distinct(new PointEqualityComparer()); @@ -33,18 +44,29 @@ public static DbGeography CreateDbLine(IEnumerable points) public static DbGeography CreatePolygon(IEnumerable points) { - var pointsList = points.ToList(); - var equalityComparer = new PointEqualityComparer(); + var listPoints = points.ToList(); - if (!equalityComparer.Equals(pointsList.Last(), pointsList.First())) + if (!new PointEqualityComparer().Equals(listPoints.Last(), listPoints.First())) { - pointsList.Add(pointsList.First()); + listPoints.Add(listPoints.First()); } - var pointsString = String.Join(", ", pointsList.Select(p => string.Format(CultureInfo.InvariantCulture, + var pointsString = String.Join(", ", listPoints.Select(p => string.Format(CultureInfo.InvariantCulture, "{0} {1}", p.Longitude, p.Latitude))); - return DbGeography.PolygonFromText($"POLYGON (({pointsString}))", EarthSRID); + var wktString = $"POLYGON (({pointsString}))"; + + var sqlGeography = SqlGeography.STGeomFromText(new SqlChars(wktString), EarthSRID).MakeValid(); + + // SQL Server is left-handed when it comes to polygons + // Bing Maps Control is ambidextrous + var invertedSqlGeography = sqlGeography.ReorientObject(); + if (sqlGeography.STArea() > invertedSqlGeography.STArea()) + { + sqlGeography = invertedSqlGeography; + } + + return DbSpatialServices.Default.GeographyFromProviderValue(sqlGeography); } public static IEnumerable FromDbLine(DbGeography line) @@ -58,25 +80,14 @@ public static IEnumerable FromDbLine(DbGeography line) public static IEnumerable FromPolygon(DbGeography polygon) { - try - { - return FromPolygonInternal(polygon); - } - catch (Exception) - { - var validPolygon = polygon.MakeValid(); - return FromPolygonInternal(validPolygon); - } - } + var sqlGeography = SqlGeography.STGeomFromText(new SqlChars(polygon.WellKnownValue.WellKnownText), EarthSRID); - private static IEnumerable FromPolygonInternal(DbGeography polygon) - { var list = new List(); for (int i = 1; i <= polygon.PointCount; i++) { - var p = polygon.PointAt(i); - list.Add(new Point() { Latitude = p.Latitude.Value, Longitude = p.Longitude.Value }); + var p = sqlGeography.STPointN(i); + list.Add(new Point() { Latitude = p.Lat.Value, Longitude = p.Long.Value }); } return list; diff --git a/Backend/src/Trackable.Repositories/IGeoFenceRepository.cs b/Backend/src/Trackable.Repositories/IGeoFenceRepository.cs index a97ffcc..6bc5ea9 100644 --- a/Backend/src/Trackable.Repositories/IGeoFenceRepository.cs +++ b/Backend/src/Trackable.Repositories/IGeoFenceRepository.cs @@ -10,7 +10,7 @@ namespace Trackable.Repositories /// public interface IGeoFenceRepository : IRepository, ICountableRepository { - Task> GetByAssetIdAsync(string assetId); + Task> GetByAssetIdWithIntersectionAsync(string assetId, IPoint[] points); Task UpdateAssetsAsync(GeoFence fence, IEnumerable assetIds); } diff --git a/Backend/src/Trackable.Repositories/INotificationUpdateRepository.cs b/Backend/src/Trackable.Repositories/IGeoFenceUpdateRepository.cs similarity index 64% rename from Backend/src/Trackable.Repositories/INotificationUpdateRepository.cs rename to Backend/src/Trackable.Repositories/IGeoFenceUpdateRepository.cs index 8e9210d..559063e 100644 --- a/Backend/src/Trackable.Repositories/INotificationUpdateRepository.cs +++ b/Backend/src/Trackable.Repositories/IGeoFenceUpdateRepository.cs @@ -10,7 +10,9 @@ namespace Trackable.Repositories /// public interface IGeoFenceUpdateRepository : IRepository { - Task> GetLatestAsync(string assetId); + Task> GetByGeofenceIdsAsync(string assetId, IEnumerable geofenceIds); + + Task UpdateStatusAsync(int geofenceId, NotificationStatus status); } } diff --git a/Backend/src/Trackable.Repositories/Repositories/DbRepositoryBase.cs b/Backend/src/Trackable.Repositories/Repositories/DbRepositoryBase.cs index 15f31f7..488f080 100644 --- a/Backend/src/Trackable.Repositories/Repositories/DbRepositoryBase.cs +++ b/Backend/src/Trackable.Repositories/Repositories/DbRepositoryBase.cs @@ -85,7 +85,6 @@ public virtual async Task> AddAsync(IEnumerable mode } var dataModels = models.Select(m => ObjectMapper.Map(m)); - var modelIds = dataModels.Select(d => d.Id).ToList(); var resultingData = this.Db.Set().AddRange(dataModels); diff --git a/Backend/src/Trackable.Repositories/Repositories/GeoFenceRepository.cs b/Backend/src/Trackable.Repositories/Repositories/GeoFenceRepository.cs index 711127f..e033dd2 100644 --- a/Backend/src/Trackable.Repositories/Repositories/GeoFenceRepository.cs +++ b/Backend/src/Trackable.Repositories/Repositories/GeoFenceRepository.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using AutoMapper; +using System; +using System.Collections.Generic; using System.Data.Entity; using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using Trackable.EntityFramework; using Trackable.Models; -using System; -using AutoMapper; -using System.Linq.Expressions; +using Trackable.Repositories.Helpers; namespace Trackable.Repositories { @@ -34,16 +35,18 @@ public async Task UpdateAssetsAsync(GeoFence fence, IEnumerable(fenceData); } - public async Task> GetByAssetIdAsync(string assetId) + public async Task GetCountAsync() { - var data = await this.FindBy(g => g.AssetDatas.Select(a => a.Id).Contains(assetId)) - .ToListAsync(); - return data.Select(d => this.ObjectMapper.Map(d)); + return await this.FindBy(a => true).CountAsync(); } - public async Task GetCountAsync() + public async Task> GetByAssetIdWithIntersectionAsync(string assetId, IPoint[] points) { - return await this.FindBy(a => true).CountAsync(); + var pointsGeography = GeographyHelper.CreateDbMultiPoint(points); + + return await this.FindBy(g => g.AssetDatas.Any(a => a.Id == assetId)) + .Select(g => new { GeoFence = g, Intersects = g.Polygon.Intersects(pointsGeography) }) + .ToDictionaryAsync(r => this.ObjectMapper.Map(r.GeoFence), r => r.Intersects); } protected override Expression>[] Includes => new Expression>[] diff --git a/Backend/src/Trackable.Repositories/Repositories/GeoFenceUpdateRepository.cs b/Backend/src/Trackable.Repositories/Repositories/GeoFenceUpdateRepository.cs index 7aa96c4..5845dbb 100644 --- a/Backend/src/Trackable.Repositories/Repositories/GeoFenceUpdateRepository.cs +++ b/Backend/src/Trackable.Repositories/Repositories/GeoFenceUpdateRepository.cs @@ -4,7 +4,6 @@ using System.Data.Entity; using System.Linq; using System.Linq.Expressions; -using System.Text; using System.Threading.Tasks; using Trackable.EntityFramework; using Trackable.Models; @@ -18,15 +17,26 @@ public GeoFenceUpdateRepository(TrackableDbContext db, IMapper mapper) { } - public async Task> GetLatestAsync(string assetId) + public async Task> GetByGeofenceIdsAsync(string assetId, IEnumerable geofenceIds) { - return await this.FindBy(n => n.AssetDataId == assetId) - .GroupBy(n => n.GeoFenceDataId) - .ToDictionaryAsync( g => g.Key, g => g.AsQueryable() - .OrderByDescending(n => n.CreatedAtTimeUtc) - .AsEnumerable() - .Select(d => this.ObjectMapper.Map(d)) - .FirstOrDefault()); + return await this.Db.GeoFenceUpdates + .Where(g => g.AssetDataId == assetId && geofenceIds.Contains(g.GeoFenceDataId)) + .ToDictionaryAsync(r => r.GeoFenceDataId, r => this.ObjectMapper.Map(r)); + } + + public async Task UpdateStatusAsync(int geofenceId, NotificationStatus status) + { + var geofenceUpdate = await this.Db.GeoFenceUpdates.SingleAsync(g => geofenceId == g.Id); + + if (geofenceUpdate == null) + { + return; + } + + geofenceUpdate.Status = (int)status; + geofenceUpdate.CreatedAtTimeUtc = DateTime.UtcNow; + + await this.Db.SaveChangesAsync(); } protected override Expression>[] Includes => null; diff --git a/Backend/src/Trackable.Repositories/Repositories/TrackingPointRepository.cs b/Backend/src/Trackable.Repositories/Repositories/TrackingPointRepository.cs index 05e704a..dad80b7 100644 --- a/Backend/src/Trackable.Repositories/Repositories/TrackingPointRepository.cs +++ b/Backend/src/Trackable.Repositories/Repositories/TrackingPointRepository.cs @@ -1,14 +1,14 @@ -using System; +using AutoMapper; +using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Spatial; using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using Trackable.Common; using Trackable.EntityFramework; using Trackable.Models; -using AutoMapper; -using System.Linq.Expressions; namespace Trackable.Repositories { @@ -22,6 +22,7 @@ public TrackingPointRepository(TrackableDbContext db, IMapper mapper) public override async Task AddAsync(TrackingPoint model) { var device = await this.Db.TrackingDevices + .Where(d => !d.Deleted) .Include(d => d.Asset) .SingleAsync(d => d.Id == model.TrackingDeviceId); @@ -32,16 +33,7 @@ public override async Task AddAsync(TrackingPoint model) model.AssetId = device.Asset.Id; - var data = this.ObjectMapper.Map(model); - - var addedData = this.Db.Set().Add(data); - - device.LatestPosition = addedData; - device.Asset.LatestPosition = addedData; - - await this.Db.SaveChangesAsync(); - - return this.ObjectMapper.Map(await this.FindAsync(addedData.Id)); + return await this.AddAsyncInternal(model, device); } public override async Task> AddAsync(IEnumerable models) @@ -54,6 +46,7 @@ public override async Task> AddAsync(IEnumerable !d.Deleted) .Include(d => d.Asset) .SingleAsync(d => d.Id == deviceId); @@ -66,7 +59,7 @@ public override async Task> AddAsync(IEnumerable p.DeviceTimestampUtc); var savedModels = (await base.AddAsync(orderedModels.Take(models.Count() - 1))).ToList(); - var latestPoint = await this.AddAsync(orderedModels.Last()); + var latestPoint = await this.AddAsyncInternal(orderedModels.Last(), device); savedModels.Add(latestPoint); @@ -75,7 +68,7 @@ public override async Task> AddAsync(IEnumerable> GetByAssetIdAsync(string assetId) { - var data = await this + var data = await this .FindBy(a => a.AssetId == assetId) .ToListAsync(); return data.Select(d => this.ObjectMapper.Map(d)); @@ -86,7 +79,7 @@ public async Task> GetByDeviceIdAsync(string deviceId var data = await this .FindBy(a => a.TrackingDeviceId == deviceId) .ToListAsync(); - return data.Select(d => this.ObjectMapper.Map(d)); + return data.Select(d => this.ObjectMapper.Map(d)); } public async Task> GetByAssetIdAfterDateAsync(string assetId, DateTime date, bool includeDebug) @@ -151,5 +144,19 @@ public async Task GetCountAsync() { data => data.Asset }; + + private async Task AddAsyncInternal(TrackingPoint trackingPoint, TrackingDeviceData deviceData) + { + var data = this.ObjectMapper.Map(trackingPoint); + + var addedData = this.Db.Set().Add(data); + + deviceData.LatestPosition = addedData; + deviceData.Asset.LatestPosition = addedData; + + await this.Db.SaveChangesAsync(); + + return this.ObjectMapper.Map(await this.FindAsync(addedData.Id)); + } } } diff --git a/Backend/src/Trackable.Repositories/RepositoriesExtensions.cs b/Backend/src/Trackable.Repositories/RepositoriesExtensions.cs index e0474c9..6d15225 100644 --- a/Backend/src/Trackable.Repositories/RepositoriesExtensions.cs +++ b/Backend/src/Trackable.Repositories/RepositoriesExtensions.cs @@ -13,6 +13,9 @@ public static IServiceCollection AddRepositories( string dbConnectionString, string rootPath) { + var mapperConfiguration = new MapperConfiguration(cfg => cfg.AddProfile()); + mapperConfiguration.CompileMappings(); + return services .AddDbContext(dbConnectionString, rootPath) @@ -30,7 +33,7 @@ public static IServiceCollection AddRepositories( .AddTransient() .AddTransient() - .AddSingleton(new MapperConfiguration(cfg => cfg.AddProfile())) + .AddSingleton(mapperConfiguration) .AddScoped() .AddScoped() diff --git a/Backend/src/Trackable.Repositories/Trackable.Repositories.csproj b/Backend/src/Trackable.Repositories/Trackable.Repositories.csproj index ff0c1e8..64555e6 100644 --- a/Backend/src/Trackable.Repositories/Trackable.Repositories.csproj +++ b/Backend/src/Trackable.Repositories/Trackable.Repositories.csproj @@ -145,13 +145,12 @@ - - + @@ -205,10 +204,7 @@ Trackable.Models - - - - +