From e11cfb55509aa3240110fd70d66b7d973b0cb602 Mon Sep 17 00:00:00 2001 From: AT Mulyana Date: Thu, 14 Apr 2016 21:35:18 +0700 Subject: [PATCH 1/8] add feature: INSERT INTO... SELECT ... --- Database/MySql/tracker.sql | 473 ++++ Database/SqlServer/Tracker.sql | Bin 39454 -> 68004 bytes Database/SqlServer/Tracker/DacMetadata.xml | 5 + Database/SqlServer/Tracker/Origin.xml | 22 + Database/SqlServer/Tracker/model.sql | 123 ++ Database/SqlServer/Tracker/model.xml | 1925 +++++++++++++++++ Database/SqlServer/Tracker/postdeploy.sql | 471 ++++ ...EntityFramework.Extended.Test.net40.csproj | 3 + ...EntityFramework.Extended.Test.net45.csproj | 29 +- .../packages.config | 9 +- Source/EntityFramework.Extended.net40.sln | 4 +- Source/EntityFramework.Extended.net45.sln | 20 +- .../Batch/IBatchRunner.cs | 43 + .../Batch/MySqlBatchRunner.cs | 219 +- .../Batch/QueryHelper.cs | 219 ++ .../Batch/SqlServerBatchRunner.cs | 221 +- .../EntityFramework.Extended.net40.csproj | 3 +- .../EntityFramework.Extended.net45.csproj | 1 + .../Extensions/BatchExtensions.cs | 100 +- Source/EntityFramework.Extended/Locator.cs | 1 + .../Mapping/MetadataMappingProvider.cs | 2 +- .../net40/Tracker.SqlServer.Entities/Audit.cs | 46 +- .../net40/Tracker.SqlServer.Entities/Item.cs | 49 + .../Tracker.SqlServer.Entities/Item_2.cs | 49 + .../Tracker.SqlServer.Entities/Priority.cs | 49 +- .../Tracker.SqlServer.Entities/Product.cs | 47 + .../ProductSummary.cs | 31 + .../net40/Tracker.SqlServer.Entities/Role.cs | 46 +- .../Tracker.SqlServer.Entities/Status.cs | 49 +- .../net40/Tracker.SqlServer.Entities/Task.cs | 85 +- .../TaskExtended.cs | 37 +- .../Tracker.Context.cs | 70 +- .../Tracker.Designer.cs | 2 +- .../Tracker.SqlServer.Entities.net40.csproj | 12 + .../Tracker.SqlServer.Entities/Tracker.cs | 5 +- .../Tracker.SqlServer.Entities/Tracker.edmx | 235 +- .../Tracker.edmx.diagram | 8 +- .../net40/Tracker.SqlServer.Entities/User.cs | 94 +- .../Tracker.SqlServer.Test/ExtensionTest.cs | 165 ++ .../Tracker.SqlServer.Test.net40.csproj | 3 + .../Properties/AssemblyInfo.cs | 36 + .../Tracker.MySql.Entities/Tracker.Context.cs | 40 + .../Tracker.MySql.Entities/Tracker.Context.tt | 636 ++++++ .../Tracker.Designer.cs | 10 + .../Tracker.MySql.Entities.csproj | 147 ++ .../net45/Tracker.MySql.Entities/Tracker.cs | 9 + .../net45/Tracker.MySql.Entities/Tracker.edmx | 832 +++++++ .../Tracker.edmx.diagram | 31 + .../net45/Tracker.MySql.Entities/Tracker.tt | 733 +++++++ .../net45/Tracker.MySql.Entities/app.config | 33 + .../net45/Tracker.MySql.Entities/audit.cs | 29 + .../net45/Tracker.MySql.Entities/item.cs | 31 + .../net45/Tracker.MySql.Entities/item_2.cs | 31 + .../Tracker.MySql.Entities/packages.config | 6 + .../net45/Tracker.MySql.Entities/priority.cs | 34 + .../net45/Tracker.MySql.Entities/product.cs | 34 + .../Tracker.MySql.Entities/productsummary.cs | 22 + .../net45/Tracker.MySql.Entities/role.cs | 24 + .../net45/Tracker.MySql.Entities/status.cs | 34 + .../net45/Tracker.MySql.Entities/task.cs | 46 + .../Tracker.MySql.Entities/taskextended.cs | 26 + .../net45/Tracker.MySql.Entities/user.cs | 49 + .../net45/Tracker.MySql.Test/App.config | 35 + .../net45/Tracker.MySql.Test/ExtensionTest.cs | 279 +++ .../Properties/AssemblyInfo.cs | 36 + .../Tracker.MySql.Test.csproj | 120 + .../net45/Tracker.MySql.Test/packages.config | 6 + .../Tracker.SqlServer.CodeFirst.csproj | 29 +- .../packages.config | 8 + .../net45/Tracker.SqlServer.Entities/Audit.cs | 46 +- .../net45/Tracker.SqlServer.Entities/Item.cs | 49 + .../Tracker.SqlServer.Entities/Item_2.cs | 49 + .../Tracker.SqlServer.Entities/Priority.cs | 49 +- .../Tracker.SqlServer.Entities/Product.cs | 47 + .../ProductSummary.cs | 31 + .../net45/Tracker.SqlServer.Entities/Role.cs | 46 +- .../Tracker.SqlServer.Entities/Status.cs | 49 +- .../net45/Tracker.SqlServer.Entities/Task.cs | 85 +- .../TaskExtended.cs | 37 +- .../Tracker.Context.cs | 70 +- .../Tracker.Designer.cs | 2 +- .../Tracker.SqlServer.Entities.csproj | 49 +- .../Tracker.SqlServer.Entities/Tracker.cs | 5 +- .../Tracker.SqlServer.Entities/Tracker.edmx | 230 ++ .../Tracker.edmx.diagram | 8 +- .../net45/Tracker.SqlServer.Entities/User.cs | 94 +- .../packages.config | 10 + .../Tracker.SqlServer.Test/ExtensionTest.cs | 178 +- .../Tracker.SqlServer.Test.csproj | 28 +- .../Tracker.SqlServer.Test/packages.config | 9 +- 90 files changed, 8894 insertions(+), 638 deletions(-) create mode 100644 Database/MySql/tracker.sql create mode 100644 Database/SqlServer/Tracker/DacMetadata.xml create mode 100644 Database/SqlServer/Tracker/Origin.xml create mode 100644 Database/SqlServer/Tracker/model.sql create mode 100644 Database/SqlServer/Tracker/model.xml create mode 100644 Database/SqlServer/Tracker/postdeploy.sql create mode 100644 Source/EntityFramework.Extended/Batch/QueryHelper.cs create mode 100644 Source/Samples/net40/Tracker.SqlServer.Entities/Item.cs create mode 100644 Source/Samples/net40/Tracker.SqlServer.Entities/Item_2.cs create mode 100644 Source/Samples/net40/Tracker.SqlServer.Entities/Product.cs create mode 100644 Source/Samples/net40/Tracker.SqlServer.Entities/ProductSummary.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Properties/AssemblyInfo.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.Context.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.Context.tt create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.Designer.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.MySql.Entities.csproj create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx.diagram create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/Tracker.tt create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/app.config create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/audit.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/item.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/item_2.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/packages.config create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/priority.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/product.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/productsummary.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/role.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/status.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/task.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/taskextended.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Entities/user.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Test/App.config create mode 100644 Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Test/Properties/AssemblyInfo.cs create mode 100644 Source/Samples/net45/Tracker.MySql.Test/Tracker.MySql.Test.csproj create mode 100644 Source/Samples/net45/Tracker.MySql.Test/packages.config create mode 100644 Source/Samples/net45/Tracker.SqlServer.Entities/Item.cs create mode 100644 Source/Samples/net45/Tracker.SqlServer.Entities/Item_2.cs create mode 100644 Source/Samples/net45/Tracker.SqlServer.Entities/Product.cs create mode 100644 Source/Samples/net45/Tracker.SqlServer.Entities/ProductSummary.cs diff --git a/Database/MySql/tracker.sql b/Database/MySql/tracker.sql new file mode 100644 index 0000000..e49c4a0 --- /dev/null +++ b/Database/MySql/tracker.sql @@ -0,0 +1,473 @@ +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- Database: `tracker` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `audit` +-- + +DROP TABLE IF EXISTS `audit`; +CREATE TABLE `audit` ( + `Id` int(11) NOT NULL, + `Date` datetime NOT NULL, + `UserId` int(11) DEFAULT NULL, + `TaskId` int(11) DEFAULT NULL, + `Content` longtext NOT NULL, + `Username` varchar(50) NOT NULL, + `CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `RowVersion` binary(8) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `item` +-- + +DROP TABLE IF EXISTS `item`; +CREATE TABLE `item` ( + `ItemId` varchar(10) NOT NULL, + `ProductId` varchar(10) NOT NULL, + `ListPrice` decimal(10,2) DEFAULT NULL, + `UnitCost` decimal(10,2) DEFAULT NULL, + `Supplier` int(11) DEFAULT NULL, + `Status` varchar(2) DEFAULT NULL, + `Attr1` varchar(80) DEFAULT NULL, + `Attr2` varchar(80) DEFAULT NULL, + `Attr3` varchar(80) DEFAULT NULL, + `Attr4` varchar(80) DEFAULT NULL, + `Attr5` varchar(80) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `item` +-- + +INSERT INTO `item` (`ItemId`, `ProductId`, `ListPrice`, `UnitCost`, `Supplier`, `Status`, `Attr1`, `Attr2`, `Attr3`, `Attr4`, `Attr5`) VALUES +('EST-1', 'FI-SW-01', '16.50', '10.00', 1, 'P', 'Large', NULL, NULL, NULL, NULL), +('EST-10', 'K9-DL-01', '18.50', '12.00', 1, 'P', 'Spotted Adult Female', NULL, NULL, NULL, NULL), +('EST-11', 'RP-SN-01', '18.50', '12.00', 1, 'P', 'Venomless', NULL, NULL, NULL, NULL), +('EST-12', 'RP-SN-01', '18.50', '12.00', 1, 'P', 'Rattleless', NULL, NULL, NULL, NULL), +('EST-13', 'RP-LI-02', '18.50', '12.00', 1, 'P', 'Green Adult', NULL, NULL, NULL, NULL), +('EST-14', 'FL-DSH-01', '58.50', '12.00', 1, 'P', 'Tailless', NULL, NULL, NULL, NULL), +('EST-15', 'FL-DSH-01', '23.50', '12.00', 1, 'P', 'Tailed', NULL, NULL, NULL, NULL), +('EST-16', 'FL-DLH-02', '93.50', '12.00', 1, 'P', 'Adult Female', NULL, NULL, NULL, NULL), +('EST-17', 'FL-DLH-02', '93.50', '12.00', 1, 'P', 'Adult Male', NULL, NULL, NULL, NULL), +('EST-18', 'AV-CB-01', '193.50', '92.00', 1, 'P', 'Adult Male', NULL, NULL, NULL, NULL), +('EST-19', 'AV-SB-02', '15.50', '2.00', 1, 'P', 'Adult Male', NULL, NULL, NULL, NULL), +('EST-2', 'FI-SW-01', '16.50', '10.00', 1, 'P', 'Small', NULL, NULL, NULL, NULL), +('EST-20', 'FI-FW-02', '5.50', '2.00', 1, 'P', 'Adult Male', NULL, NULL, NULL, NULL), +('EST-21', 'FI-FW-02', '5.29', '1.00', 1, 'P', 'Adult Female', NULL, NULL, NULL, NULL), +('EST-22', 'K9-RT-02', '135.50', '100.00', 1, 'P', 'Adult Male', NULL, NULL, NULL, NULL), +('EST-23', 'K9-RT-02', '145.49', '100.00', 1, 'P', 'Adult Female', NULL, NULL, NULL, NULL), +('EST-24', 'K9-RT-02', '255.50', '92.00', 1, 'P', 'Adult Male', NULL, NULL, NULL, NULL), +('EST-25', 'K9-RT-02', '325.29', '90.00', 1, 'P', 'Adult Female', NULL, NULL, NULL, NULL), +('EST-26', 'K9-CW-01', '125.50', '92.00', 1, 'P', 'Adult Male', NULL, NULL, NULL, NULL), +('EST-27', 'K9-CW-01', '155.29', '90.00', 1, 'P', 'Adult Female', NULL, NULL, NULL, NULL), +('EST-28', 'K9-RT-01', '155.29', '90.00', 1, 'P', 'Adult Female', NULL, NULL, NULL, NULL), +('EST-3', 'FI-SW-02', '18.50', '12.00', 1, 'P', 'Toothless', NULL, NULL, NULL, NULL), +('EST-4', 'FI-FW-01', '18.50', '12.00', 1, 'P', 'Spotted', NULL, NULL, NULL, NULL), +('EST-5', 'FI-FW-01', '18.50', '12.00', 1, 'P', 'Spotless', NULL, NULL, NULL, NULL), +('EST-6', 'K9-BD-01', '18.50', '12.00', 1, 'P', 'Male Adult', NULL, NULL, NULL, NULL), +('EST-7', 'K9-BD-01', '18.50', '12.00', 1, 'P', 'Female Puppy', NULL, NULL, NULL, NULL), +('EST-8', 'K9-PO-02', '18.50', '12.00', 1, 'P', 'Male Puppy', NULL, NULL, NULL, NULL), +('EST-9', 'K9-DL-01', '18.50', '12.00', 1, 'P', 'Spotless Male Puppy', NULL, NULL, NULL, NULL); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `item 2` +-- + +DROP TABLE IF EXISTS `item 2`; +CREATE TABLE `item 2` ( + `ItemId` varchar(10) NOT NULL, + `ProductId` varchar(10) NOT NULL, + `ListPrice` decimal(10,2) DEFAULT NULL, + `UnitCost` decimal(10,2) DEFAULT NULL, + `Supplier` int(11) DEFAULT NULL, + `Status` varchar(2) DEFAULT NULL, + `Attr1` varchar(80) DEFAULT NULL, + `Attr2` varchar(80) DEFAULT NULL, + `Attr3` varchar(80) DEFAULT NULL, + `Attr4` varchar(80) DEFAULT NULL, + `Attr5` varchar(80) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `priority` +-- + +DROP TABLE IF EXISTS `priority`; +CREATE TABLE `priority` ( + `Id` int(11) NOT NULL, + `Name` varchar(50) NOT NULL, + `Order` int(11) NOT NULL, + `Description` varchar(200) DEFAULT NULL, + `CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ModifiedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `RowVersion` binary(8) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `priority` +-- + +INSERT INTO `priority` (`Id`, `Name`, `Order`, `Description`, `CreatedDate`, `ModifiedDate`, `RowVersion`) VALUES +(1, 'High', 1, 'A High Priority', '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d1), +(2, 'Normal', 2, 'A Normal Priority', '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d2), +(3, 'Low', 3, 'A Low Priority', '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d3); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `product` +-- + +DROP TABLE IF EXISTS `product`; +CREATE TABLE `product` ( + `ProductId` varchar(10) NOT NULL, + `Category` varchar(10) NOT NULL, + `Name` varchar(80) DEFAULT NULL, + `Descn` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `product` +-- + +INSERT INTO `product` (`ProductId`, `Category`, `Name`, `Descn`) VALUES +('AV-CB-01', 'BIRDS', 'Amazon Parrot', 'Great companion for up to 75 years'), +('AV-SB-02', 'BIRDS', 'Finch', 'Great stress reliever'), +('FI-FW-01', 'FISH', 'Koi', 'Freshwater fish from Japan'), +('FI-FW-02', 'FISH', 'Goldfish', 'Freshwater fish from China'), +('FI-SW-01', 'FISH', 'Angelfish', 'Saltwater fish from Australia'), +('FI-SW-02', 'FISH', 'Tiger Shark', 'Saltwater fish from Australia'), +('FL-DLH-02', 'CATS', 'Persian', 'Friendly house cat, doubles as a princess'), +('FL-DSH-01', 'CATS', 'Manx', 'Great for reducing mouse populations'), +('K9-BD-01', 'DOGS', 'Bulldog', 'Friendly dog from England'), +('K9-CW-01', 'DOGS', 'Chihuahua', 'Great companion dog'), +('K9-DL-01', 'DOGS', 'Dalmation', 'Great dog for a fire station'), +('K9-PO-02', 'DOGS', 'Poodle', 'Cute dog from France'), +('K9-RT-01', 'DOGS', 'Golden Retriever', 'Great family dog'), +('K9-RT-02', 'DOGS', 'Labrador Retriever', 'Great hunting dog'), +('RP-LI-02', 'REPTILES', 'Iguana', 'Friendly green friend'), +('RP-SN-01', 'REPTILES', 'Rattlesnake', 'Doubles as a watch dog'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `productsummary` +-- + +DROP TABLE IF EXISTS `productsummary`; +CREATE TABLE `productsummary` ( + `ProductId` varchar(10) NOT NULL, + `Name` varchar(80) DEFAULT NULL, + `AvgPrice` decimal(10,2) NOT NULL, + `Verified` tinyint(1) NOT NULL DEFAULT '1' +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `role` +-- + +DROP TABLE IF EXISTS `role`; +CREATE TABLE `role` ( + `Id` int(11) NOT NULL, + `Name` varchar(50) NOT NULL, + `Description` varchar(150) DEFAULT NULL, + `CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ModifiedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `RowVersion` binary(8) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `role` +-- + +INSERT INTO `role` (`Id`, `Name`, `Description`, `CreatedDate`, `ModifiedDate`, `RowVersion`) VALUES +(1, 'Admin', NULL, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d4), +(2, 'Manager', NULL, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d5), +(3, 'Newb', NULL, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d6), +(4, 'Nobody', NULL, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d7), +(5, 'Power User', NULL, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d8); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `status` +-- + +DROP TABLE IF EXISTS `status`; +CREATE TABLE `status` ( + `Id` int(11) NOT NULL, + `Name` varchar(50) NOT NULL, + `Description` varchar(150) DEFAULT NULL, + `Order` int(11) NOT NULL, + `CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ModifiedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `RowVersion` binary(8) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `status` +-- + +INSERT INTO `status` (`Id`, `Name`, `Description`, `Order`, `CreatedDate`, `ModifiedDate`, `RowVersion`) VALUES +(1, 'Not Started', NULL, 1, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007d9), +(2, 'In Progress', NULL, 2, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007da), +(3, 'Completed', NULL, 3, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007db), +(4, 'Waiting on someone else', NULL, 4, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007dc), +(5, 'Deferred', NULL, 5, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007dd), +(6, 'Done', NULL, 6, '2011-09-08 06:57:02', '2011-09-08 06:57:02', 0x00000000000007de); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `task` +-- + +DROP TABLE IF EXISTS `task`; +CREATE TABLE `task` ( + `Id` int(11) NOT NULL, + `StatusId` int(11) NOT NULL, + `PriorityId` int(11) DEFAULT NULL, + `CreatedId` int(11) NOT NULL, + `Summary` varchar(255) NOT NULL, + `Details` varchar(2000) DEFAULT NULL, + `StartDate` datetime DEFAULT NULL, + `DueDate` datetime DEFAULT NULL, + `CompleteDate` datetime DEFAULT NULL, + `AssignedId` int(11) DEFAULT NULL, + `CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ModifiedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `RowVersion` binary(8) NOT NULL, + `LastModifiedBy` varchar(50) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `task` +-- + +INSERT INTO `task` (`Id`, `StatusId`, `PriorityId`, `CreatedId`, `Summary`, `Details`, `StartDate`, `DueDate`, `CompleteDate`, `AssignedId`, `CreatedDate`, `ModifiedDate`, `RowVersion`, `LastModifiedBy`) VALUES +(1, 1, 1, 2, 'Make it to Earth', 'Find and make it to earth while avoiding the cylons.', NULL, NULL, NULL, 1, '2009-12-18 04:01:58', '2009-12-18 04:01:58', 0x00000000000007df, 'laura.roslin@battlestar.com'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `taskextended` +-- + +DROP TABLE IF EXISTS `taskextended`; +CREATE TABLE `taskextended` ( + `TaskId` int(11) NOT NULL, + `Browser` varchar(200) DEFAULT NULL, + `OS` varchar(150) DEFAULT NULL, + `CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ModifiedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `RowVersion` binary(8) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user` +-- + +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `Id` int(11) NOT NULL, + `EmailAddress` varchar(250) NOT NULL, + `FirstName` varchar(200) DEFAULT NULL, + `LastName` varchar(200) DEFAULT NULL, + `Avatar` longblob, + `CreatedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ModifiedDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `RowVersion` binary(8) NOT NULL, + `PasswordHash` char(86) NOT NULL DEFAULT '', + `PasswordSalt` char(5) NOT NULL DEFAULT '', + `Comment` longtext, + `IsApproved` tinyint(1) NOT NULL DEFAULT '1', + `LastLoginDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `LastActivityDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `LastPasswordChangeDate` datetime DEFAULT NULL, + `AvatarType` varchar(150) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `user` +-- + +INSERT INTO `user` (`Id`, `EmailAddress`, `FirstName`, `LastName`, `Avatar`, `CreatedDate`, `ModifiedDate`, `RowVersion`, `PasswordHash`, `PasswordSalt`, `Comment`, `IsApproved`, `LastLoginDate`, `LastActivityDate`, `LastPasswordChangeDate`, `AvatarType`) VALUES +(1, 'william.adama@battlestar.com', 'William', 'Adama', NULL, '2009-05-06 10:46:20', '2009-05-06 10:46:20', 0x00000000000007e0, '1+v5rvSXnyX7tvwTKfM+aq+s0hDmNXsduGZfq8sQv1ggPnGlQdDdBdbUP0bUmbMbiU40PvRQWKRAy5QUd1xrAA', '?#nkY', NULL, 1, NULL, '2009-05-06 10:46:20', NULL, NULL), +(2, 'laura.roslin@battlestar.com', 'Laura', 'Roslin', NULL, '2009-05-06 10:47:00', '2009-05-06 10:47:00', 0x00000000000007e1, 'Sx/jwRHFW/CQpO0E6G8d+jo344AmAKfX+C48a0iAZyMrb4sE8VoDuyZorbhbLZAX9f4UZk67O7eCjk854OrYSg', 'Ph)6;', NULL, 1, NULL, '2009-05-06 10:47:00', NULL, NULL), +(3, 'kara.thrace@battlestar.com', 'Kara', 'Thrace', NULL, '2009-05-06 10:47:43', '2009-05-06 10:47:43', 0x00000000000007e2, '5KhtS4ax7G1aGkq97w02ooVZMmJp8bcySEKMSxruXu/Z/wRKoNAxMlkjRVY1yLavrC3GRYQZjy5b6JW8cR5EWg', '!]@2/', NULL, 1, NULL, '2009-05-06 10:47:43', NULL, NULL), +(4, 'lee.adama@battlestar.com', 'Lee', 'Adama', NULL, '2009-05-06 10:48:02', '2009-05-06 10:48:02', 0x00000000000007e3, 'IrK8OhI2n4Ev3YA4y5kP7vy+n2CffX2MgcONbAh6/kZpNZYBYoYyrMhqdYztehL0NAIdvcInQ6zOjMplq+zWQA', 'e@_a{', NULL, 1, NULL, '2009-05-06 10:48:02', NULL, NULL), +(5, 'gaius.baltar@battlestar.com', 'Gaius', 'Baltar', NULL, '2009-05-06 10:48:26', '2009-05-06 10:48:26', 0x00000000000007e4, '7tfajMaEerDNVgi6Oi6UJ6JxsUXZ0u4zQeUrZQxnaXJQ2f2vd9AzBR4sVOaH7LZtCjQopMzlQ38QqNEnpK0B/g', '_qpA2', NULL, 1, NULL, '2009-05-06 10:48:26', NULL, NULL), +(6, 'saul.tigh@battlestar.com', 'Saul', 'Tigh', NULL, '2009-05-06 10:49:26', '2009-05-06 10:49:26', 0x00000000000007e5, 'wzkR89zRXe7hND1jqAP9xgupYJBvEZcjsfUe3TaU45kxRajjjS9u0fOTLK+uglzk67EGochJdeoikWs7hxMNRA', 'h]-zG', NULL, 1, NULL, '2009-05-06 10:49:26', NULL, NULL); + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `audit` +-- +ALTER TABLE `audit` + ADD PRIMARY KEY (`Id`), + ADD KEY `FK_Audit_Task` (`TaskId`), + ADD KEY `FK_Audit_User` (`UserId`); + +-- +-- Indexes for table `item` +-- +ALTER TABLE `item` + ADD PRIMARY KEY (`ItemId`), + ADD KEY `FK__Item__ProductId__04E4BC85` (`ProductId`); + +-- +-- Indexes for table `item 2` +-- +ALTER TABLE `item 2` + ADD PRIMARY KEY (`ItemId`), + ADD KEY `FK__Item 2__ProductI__0A9D95DB` (`ProductId`); + +-- +-- Indexes for table `priority` +-- +ALTER TABLE `priority` + ADD PRIMARY KEY (`Id`); + +-- +-- Indexes for table `product` +-- +ALTER TABLE `product` + ADD PRIMARY KEY (`ProductId`); + +-- +-- Indexes for table `productsummary` +-- +ALTER TABLE `productsummary` + ADD PRIMARY KEY (`ProductId`); + +-- +-- Indexes for table `role` +-- +ALTER TABLE `role` + ADD PRIMARY KEY (`Id`); + +-- +-- Indexes for table `status` +-- +ALTER TABLE `status` + ADD PRIMARY KEY (`Id`); + +-- +-- Indexes for table `task` +-- +ALTER TABLE `task` + ADD PRIMARY KEY (`Id`), + ADD KEY `IX_Task` (`AssignedId`,`StatusId`), + ADD KEY `FK_Task_Priority` (`PriorityId`), + ADD KEY `FK_Task_Status` (`StatusId`), + ADD KEY `FK_Task_User_Created` (`CreatedId`); + +-- +-- Indexes for table `taskextended` +-- +ALTER TABLE `taskextended` + ADD PRIMARY KEY (`TaskId`); + +-- +-- Indexes for table `user` +-- +ALTER TABLE `user` + ADD PRIMARY KEY (`Id`), + ADD UNIQUE KEY `IX_User` (`EmailAddress`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `audit` +-- +ALTER TABLE `audit` + MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT; +-- +-- AUTO_INCREMENT for table `role` +-- +ALTER TABLE `role` + MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6; +-- +-- AUTO_INCREMENT for table `status` +-- +ALTER TABLE `status` + MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7; +-- +-- AUTO_INCREMENT for table `task` +-- +ALTER TABLE `task` + MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; +-- +-- AUTO_INCREMENT for table `user` +-- +ALTER TABLE `user` + MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7; +-- +-- Constraints for dumped tables +-- + +-- +-- Constraints for table `audit` +-- +ALTER TABLE `audit` + ADD CONSTRAINT `FK_Audit_Task` FOREIGN KEY (`TaskId`) REFERENCES `task` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + ADD CONSTRAINT `FK_Audit_User` FOREIGN KEY (`UserId`) REFERENCES `user` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- +-- Constraints for table `item` +-- +ALTER TABLE `item` + ADD CONSTRAINT `FK__Item__ProductId__04E4BC85` FOREIGN KEY (`ProductId`) REFERENCES `product` (`ProductId`) ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- +-- Constraints for table `item 2` +-- +ALTER TABLE `item 2` + ADD CONSTRAINT `FK__Item 2__ProductI__0A9D95DB` FOREIGN KEY (`ProductId`) REFERENCES `product` (`ProductId`) ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- +-- Constraints for table `task` +-- +ALTER TABLE `task` + ADD CONSTRAINT `FK_Task_Priority` FOREIGN KEY (`PriorityId`) REFERENCES `priority` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + ADD CONSTRAINT `FK_Task_Status` FOREIGN KEY (`StatusId`) REFERENCES `status` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + ADD CONSTRAINT `FK_Task_User_Assigned` FOREIGN KEY (`AssignedId`) REFERENCES `user` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + ADD CONSTRAINT `FK_Task_User_Created` FOREIGN KEY (`CreatedId`) REFERENCES `user` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- +-- Constraints for table `taskextended` +-- +ALTER TABLE `taskextended` + ADD CONSTRAINT `FK_TaskExtended_Task` FOREIGN KEY (`TaskId`) REFERENCES `task` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/Database/SqlServer/Tracker.sql b/Database/SqlServer/Tracker.sql index 6dc6c92cebecf059cdf048e9a866da40a9d17ded..978d49fae9d2d45fbfa436521efa90de88ae152a 100644 GIT binary patch literal 68004 zcmeHQX;&M`lC7^Z=gfc5y!n9rx{VQwfu89%PY8@@Y``owwoe}pnAI$%5qO*bJ~Q`b zD3qDCNmWt_6|Tbpq^im_B5p)RL}mWp|Lz1^!BS8QJ_MJ+e$WbTgQMUk_%ryo;9rB~ zVBP$G8vNb)sp0BD@Cna-#M8f_Y%N$s*<1V`m^zM3ecPyKAJ3e(YvgloJx@*DE5UPI zUqhYS_`ekF1}o^%HhyctBh$B+xY{&#uTbA5YOA5lCQ7`*r&jPCzvICn?zuf^;ojTe z1ADt-MHs^pWkha?jX1@WB-7$9t593qj6BJ5!~VX8OHu6dKfpu>?!BAfnIH4WPA9( z6THL^=Xkw6QtgXA{Tk1ZCQBH}9!f3Z(;B{$UMu*#nNrraw1HL^@x%FCjwz9a`hQ3H zDBV9hycnjrdv6bObch;A{dG|A4~&6aC1ZPp{*7TalV+COAv3s$|F__|YeS2(;0oWR z^bTrx+yfU02a&5ObxAEgGW_!|cwz<5khUK%f~z*2U3zQ>@6dyK@GE}VOO++1@~@qm zL`sj^wTZ+Wp~f>KRcs@<><*-i$Pm|?XiMbR79_v7r<>qrj_jy2z7N3{)NljzI5q$O z#0ZH`-@1%Yd#Tbc^L7$gC(z%1rFVHIc_8#Ba8|kt3J4xEu|8(qdS)2A>x)nml^<2Z;~5^Zs%NXRHPcYl&eCuY)wWjzn!4N;@6nX#Mbr-( zUKhoePPZlF%Oth91)YCse5E&S-_i$o8>8OTN!YS8T|A()k8iO)Kf~%u?1-eg=s8I8 z9mwq6pr&4YsNuO-#CVQ!m84gh9#yUk>BG&_dIRI8oqK{gq!%Kd7U4cunyYusMPJWL z@K_5p3j2H(QaLz&E!NFTZjqw=TF@QK@HOBPqA?P)p=VNJRFxl_-i)|tqS7ilYs1x$ z(e|yz)IP?eT$09FfQOm!Er}0`e>z^{BxaufJ_w2k52L)|=7WI_R-DqAu zpY)QkyJ;;qpbrm$i;TdfTh7*sNy69Xus?5%ebn9OofgAWE33a+8fGnV3yY0eV$21Q zwZ#E?d1bCLqU~pE4BkFY%7j=%jByV?N@sqKv&MP79?S=vuLYEu!BZmJ_YtSeGMccB z%vFtyrhySnBg#63an0l$&0zEvZTbyk)3=}nndxwdybRj-r){2b^LpljXR+(hWJ^Ad z%5m*+UZx<6bC{V0e3}eq6X#{jWN%pw9{D?IP^KYkV6U+>wP^nL+-o`Fp{ zi&>iJx%aa6Wv--VjD0Y+IxTjNAnK`D0d*Q!EnkCLpCQv~(SJaGon{w?a}BbMF|Gv} zV-sVWhZHX0?|kAInd9c?2q>bxM2}Al{`9`a&(`<2mZa~Cpw1F{_8c^tOzeAlu7lRz zFd4OHn0?6%>SZ*(*2p(1w_O?45=J!%G?_N|m=76sRAEVKnw-8XlmF4n2*cXL)j-?r zOiYo=xK&5e+97$SB%4k(2x}g0c19U<>D<{GzEw`GRpozl%V$;^+1VPd&28JYwzu{v z&IS)_8>^eOrrt~SD(zZxe^MVFm8_tX`+vVHZ0)T(_G~iDOk}Q0H%(@rpVJCBHLCl^zd7bLemaBlTZK~Rwyodq?Ndq>RPcS3qBS3b`h}Y0GOx9ePs3cF^$T#^U7~ zsPYv)pGs;N(H0Bx{^&B@(QdR%_}1?sJRTKoMu9dn9olRd%Y$fE>qH(NB8>)NXN2G2 z%vYzIk;fHfcKanh9gnDH8>) z^|J!oM^++?S!jefNX`KAY(-rkbg?iSGdW)3@qif7o zIDsT`F9z-+!(8Db^Nst<9Kk=te72}uN=+4IM}@N13L`ok4EgiO-U8~HE@r2_f?mgdDcJGAWW|8$cC7l^) z`d*4$wKlQMD$wWscSz)FSdWKG#lQQZW#jh-lDo+fC%Lbg5#i_LeFyHd6Caflt6u(E zi<15{?y*)gdX2kNea7b{T;)4A8o%QHR?H;4!g`^Gd-9)=kZ4)%dL(6Q@CBWkd1n3l z$V~Z+Z`_wdv?uH5osaNJTo@VVUTje*cKv(q=cILt#L4Q2iakM`Orm9V6DQ-}8C+qs zj1jc#{@B6Q%{jfmtlVL?_R*so%s>lQI6MD@w}v~L+~Pa4KqX^x95Y2rgOPXF)1D6< zTsd3^b^X1O^3|!);C#s7mJTCBa(1jpMO>b%FE3#~oSO-xYXl!l!ZIC}xbXdOu9D4K zl%9m|+pIvjAI`gzt8tlc+F!+jxEwZr^lp|GBXi-tYfa%k#^+l}-R=z9T&iRQBFBF< zct9@aj%DNcduTrGcjbAu$r|2zH7Ht)A+D`8={3#$ii_E3EPcjA)Ssd5x{0MvBb)Fy zAjbsyHiLNkELzMOQ6F609B)1N1;{xAeE0&-%`VX4JO0iB6TV}Ph2yL0&d8l~!3yPP2R;0kvxaE1HR9U^~{``r)&|O}g%y#Aoub8+*T*0sR-5Zp%ZFL` zT{3)#NA!N?i6JM;Qr5bOM_Z8gr`W;wE5`T&R>NC-YXS+b;YXi<<@gM!G6z)n14}fR~$iv{S zjJv?(V?4QoZ|7)h7Ol>LJDX_j9A#&4kG{Nj=;uiXd&YoXr?8ry1^?N(W~tF19%Y|H z+j50Rm5rpigVslU4%v`30ar+}&-78x_R(s)w4x68sjq8Q0$d0pH^Z zT15xInLmJwb7*4|-5*_`p@Fynd3=>I`tP}aJMK`TIkRml1}kZ1)`eFZF90yazm6B_tTL^y~2F^g;8 zVf|d;nI>$QW1!6>kmUp@!3=~0Jbj80J;l@Sfl+Jt`wpwJcR-*fo?=7dOg3Oo)y&o8g=|Yji2y5_pjlq@COj+HJ-E7pg!3HhW$_Hnni;C?4j&) zXj`r@sKOw{ARC)I0iT{hGt)avzbN0NrG?bZORDR$~!`W+nC-n01Zz ze;EJL6wvWCaEN>1U*Y;H+FAh4xi--r?q8$c$wKfb+ZdZ*GC#@4(sP z;C6Y-t5+nV$Dbt?akw^f-xE`QBXVWIp!p64akVp849Z$JG3YBK{~Y)*2dwyk|GU7H zS=cK)*{BX=`U{f2fqUP80e6u5cR-j0V9OE^fbZozi6c?!V1Utw*1?ocJVGzB^Wp!1^bA@txx}$ZhnD97Y(pH_UFryh#SK z|L&{p8)Lopt>^g60*O&IkrTtGxra(LmqMT66Y<>CeP1Xy?wpTb+=xoq_{H|UH-6FQ zN}4ypSyC ztH~}t4Pd^UUl+eq6SIGPN-_2=r?35v_QoKUN*n*|KKDG6-p*X({PDCpp2w8FCsQxI zW-Yz#CdG5@cxn|9IVpdEJYdhva%FgQX=^o$-Mx?d%h)mU8!h`;%sfw3IYdLCioAu_~XM)n* zPPmOfo+jV=R95-cat}+s?WoGY zE&1fzj(AwJRXsz|djXzD+!?TrD z=ELR7FZ*dTSsVgrtNn43%Q^Y^rTiG+dIWzq`S-`3Cj0J_S>@g>`;cVbAJ;>acYlQC zmvyyWxQ6#nq59b*KW!$`a3DNyS+PLdeMmTzQa!6j zAzZgRM`@^`qs)pITJGUk6KOM8y#5KdK3FbuIjc91L%2UKKa~|vRIlou!h48==~)}a zQ8?UccFIO;@NpxrH!@tWJ0EG7p|h41Gqha!F(Yl}%En2(V?)}g9}XtyD2ucbwGAr~ z==3g(zl*pg` zsJR+xIj!!!?72{kA#%s|J5YeYh@+l*#Cb4Jl44FSS$y;K7opAatG+d{ieAyAZyH94|hW4>Ac)`Pj$I_V!3MM__v3o)hNy<^}2zc z-eT`!+TBazb76mV zRcoSZO>~3R?o|k=yAMP0XH$2lq`H$xQim96? zHn*_r+XnvMnoqRAZ&0F+owPo|J9UC`|9~x`r{nNs816pLH&E#ZlfKFO%r)cD((`I& z;is?R9e6o=riDJsnpo|c(WcQ>n?`o;yLcMO2|UEm;%UPDRpV(wKO`x{w+d?JlXw-s z;2KZlF2~F>iueSv#J3UY;GHeJ4{qm}`#_&L-v=!RT$i>$Rqm^8=lpqlmQS&pnncTZ zLa;u|Pn6F~T>As}MMlaw`!3%{XP>*cT>vF?EOzzz5Kzk33_k-+UP4DKA|~^~ z=n9uA+@o{bv!iI;+<^OdzSUKM)c9z0f=>%-40<@ZTjB+*p&Gd2vyp`(e3lZl0T0d9 z17oRChciEje{0Yptl$McD}(=6@Z^4PYk*rD|aEZfBhMHZ3uvFu6Bf*B>XJkK>R zjxJKaPnW3F$WyL`#eJ)}w=DUFX9JhR#uW-QW3+pe0%E(If)4vY4gM{f3fwW>TDz`v z#I5?2Gs-mc)EQwkiq1SwV!SXbWR>xvXWn0DxHE4#pF7NnPN3aQoZ^c97Jaq7EE6p@ zfH|avtG%PN*Z}@8=aN2*qN(8P?j~f88Ihu$sR``*jtE8Mp;j&z_H4pB>MjAg!lUKF z%wqb7#F9~;f_kn7z2Xyo_t$%_Jn$R^`nANGDkBCd1=9JRr&%2sF5?&}`NOBeOrk!Z zKH~0@Y9p144x8|JK7l5nzsbiRX6$U3>|O3q=z3#UU~Nzbi!Y%l?kB2Wn|USP`FIqy zicY{6JPV34N^H`VMp+O^=&%J3co>nA>A>>_>?z!&2H%A!PBZuv@$J+_Nah`+l(h0! zZQ>81Us$zA7Mf@bMxzt@UE;muz4#0GUz&;`4J{lkD`0lbF5kqeqdL538>#vvUa#{68$$7 z*4X^h;aN2^nRtdL7mgCo&_1p(W|$cBtTW$ZSc}BR-7Utzh`@tH%aXzSsx63`1{X2a7m%ciyp}KRI{yc8+HJxtLS&4)>C-c!zoD?)u927DxR# z^^wW8;rny9coU(@w;BGL++9=oKI1s78IAlbKO0?b`zppze2l?eNtKOTDs&k>xOTMg delta 9 QcmZ27nPuJ + + Tracker + 1.0.0.0 + \ No newline at end of file diff --git a/Database/SqlServer/Tracker/Origin.xml b/Database/SqlServer/Tracker/Origin.xml new file mode 100644 index 0000000..8cd1b07 --- /dev/null +++ b/Database/SqlServer/Tracker/Origin.xml @@ -0,0 +1,22 @@ + + + + 3.0.0.0 + false + + 2.0.0.0 + 1.0.0.0 + + + + b115af09-6ff8-4ee2-aa71-27c1b9dcda60 + 2014-09-04T15:26:48.1499192-05:00 + 2014-09-04T15:26:48.2608526-05:00 + Microsoft.Data.Tools.Schema.Tasks.Sql, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + 12.0.40706.0 + http://schemas.microsoft.com/sqlserver/dac/Serialization/2012/02 + + + 4A52E1138674B39C68C0111BF1D1A0CA20D3D4633B60409E97BF619E229EF901 + + \ No newline at end of file diff --git a/Database/SqlServer/Tracker/model.sql b/Database/SqlServer/Tracker/model.sql new file mode 100644 index 0000000..3a3dac9 --- /dev/null +++ b/Database/SqlServer/Tracker/model.sql @@ -0,0 +1,123 @@ +CREATE TABLE [dbo].[Audit] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [Date] DATETIME NOT NULL, + [UserId] INT NULL, + [TaskId] INT NULL, + [Content] VARCHAR (MAX) NOT NULL, + [Username] NVARCHAR (50) NOT NULL, + [CreatedDate] DATETIME CONSTRAINT [DF_Audit_CreatedDate] DEFAULT (getdate()) NOT NULL, + [RowVersion] ROWVERSION NOT NULL, + CONSTRAINT [PK_Audit] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_Audit_Task] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task] ([Id]), + CONSTRAINT [FK_Audit_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) +); + +GO +CREATE TABLE [dbo].[Priority] ( + [Id] INT NOT NULL, + [Name] NVARCHAR (50) NOT NULL, + [Order] INT NOT NULL, + [Description] NVARCHAR (200) NULL, + [CreatedDate] DATETIME CONSTRAINT [DF__Priority__CreatedDate] DEFAULT (getdate()) NOT NULL, + [ModifiedDate] DATETIME CONSTRAINT [DF__Priority__ModifiedDate] DEFAULT (getdate()) NOT NULL, + [RowVersion] ROWVERSION NOT NULL, + CONSTRAINT [PK_Priority] PRIMARY KEY CLUSTERED ([Id] ASC) +); + +GO +CREATE TABLE [dbo].[Role] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [Name] NVARCHAR (50) NOT NULL, + [Description] NVARCHAR (150) NULL, + [CreatedDate] DATETIME CONSTRAINT [DF__Role__CreatedDate] DEFAULT (getdate()) NOT NULL, + [ModifiedDate] DATETIME CONSTRAINT [DF__Role__ModifiedDate] DEFAULT (getdate()) NOT NULL, + [RowVersion] ROWVERSION NOT NULL, + CONSTRAINT [PK_Role] PRIMARY KEY CLUSTERED ([Id] ASC) +); + +GO +CREATE TABLE [dbo].[Status] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [Name] NVARCHAR (50) NOT NULL, + [Description] NVARCHAR (150) NULL, + [Order] INT NOT NULL, + [CreatedDate] DATETIME CONSTRAINT [DF__Status__CreatedDate] DEFAULT (getdate()) NOT NULL, + [ModifiedDate] DATETIME CONSTRAINT [DF__Status__ModifiedDate] DEFAULT (getdate()) NOT NULL, + [RowVersion] ROWVERSION NOT NULL, + CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED ([Id] ASC) +); + +GO +CREATE TABLE [dbo].[Task] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [StatusId] INT NOT NULL, + [PriorityId] INT NULL, + [CreatedId] INT NOT NULL, + [Summary] NVARCHAR (255) NOT NULL, + [Details] NVARCHAR (2000) NULL, + [StartDate] DATETIME NULL, + [DueDate] DATETIME NULL, + [CompleteDate] DATETIME NULL, + [AssignedId] INT NULL, + [CreatedDate] DATETIME CONSTRAINT [DF__Task__CreatedDate] DEFAULT (getdate()) NOT NULL, + [ModifiedDate] DATETIME CONSTRAINT [DF__Task__ModifiedDate] DEFAULT (getdate()) NOT NULL, + [RowVersion] ROWVERSION NOT NULL, + [LastModifiedBy] NVARCHAR (50) NULL, + CONSTRAINT [PK_Task] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_Task_Priority] FOREIGN KEY ([PriorityId]) REFERENCES [dbo].[Priority] ([Id]), + CONSTRAINT [FK_Task_Status] FOREIGN KEY ([StatusId]) REFERENCES [dbo].[Status] ([Id]), + CONSTRAINT [FK_Task_User_Assigned] FOREIGN KEY ([AssignedId]) REFERENCES [dbo].[User] ([Id]), + CONSTRAINT [FK_Task_User_Created] FOREIGN KEY ([CreatedId]) REFERENCES [dbo].[User] ([Id]) +); + +GO +CREATE TABLE [dbo].[TaskExtended] ( + [TaskId] INT NOT NULL, + [Browser] NVARCHAR (200) NULL, + [OS] NVARCHAR (150) NULL, + [CreatedDate] DATETIME DEFAULT (getdate()) NOT NULL, + [ModifiedDate] DATETIME DEFAULT (getdate()) NOT NULL, + [RowVersion] ROWVERSION NOT NULL, + CONSTRAINT [PK_TaskExtended] PRIMARY KEY CLUSTERED ([TaskId] ASC), + CONSTRAINT [FK_TaskExtended_Task] FOREIGN KEY ([TaskId]) REFERENCES [dbo].[Task] ([Id]) +); + +GO +CREATE TABLE [dbo].[User] ( + [Id] INT IDENTITY (1, 1) NOT NULL, + [EmailAddress] NVARCHAR (250) NOT NULL, + [FirstName] NVARCHAR (200) NULL, + [LastName] NVARCHAR (200) NULL, + [Avatar] VARBINARY (MAX) NULL, + [CreatedDate] DATETIME CONSTRAINT [DF__User__CreatedDate] DEFAULT (getdate()) NOT NULL, + [ModifiedDate] DATETIME CONSTRAINT [DF__User__ModifiedDate] DEFAULT (getdate()) NOT NULL, + [RowVersion] ROWVERSION NOT NULL, + [PasswordHash] CHAR (86) CONSTRAINT [DF__User__PasswordHash] DEFAULT ('') NOT NULL, + [PasswordSalt] CHAR (5) CONSTRAINT [DF__User__PasswordSalt] DEFAULT ('') NOT NULL, + [Comment] TEXT NULL, + [IsApproved] BIT CONSTRAINT [DF__User__IsApproved] DEFAULT ((1)) NOT NULL, + [LastLoginDate] DATETIME CONSTRAINT [DF__User__LastLoginDate] DEFAULT (getdate()) NULL, + [LastActivityDate] DATETIME CONSTRAINT [DF__User__LastActivityDate] DEFAULT (getdate()) NOT NULL, + [LastPasswordChangeDate] DATETIME NULL, + [AvatarType] NVARCHAR (150) NULL, + CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC) +); + +GO +CREATE TABLE [dbo].[UserRole] ( + [UserId] INT NOT NULL, + [RoleId] INT NOT NULL, + CONSTRAINT [PK_UserRole] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC), + CONSTRAINT [FK_UserRole_Role] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id]), + CONSTRAINT [FK_UserRole_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) +); + +GO +CREATE NONCLUSTERED INDEX [IX_Task] + ON [dbo].[Task]([AssignedId] ASC, [StatusId] ASC); + +GO +CREATE UNIQUE NONCLUSTERED INDEX [IX_User] + ON [dbo].[User]([EmailAddress] ASC); + +GO diff --git a/Database/SqlServer/Tracker/model.xml b/Database/SqlServer/Tracker/model.xml new file mode 100644 index 0000000..f07b170 --- /dev/null +++ b/Database/SqlServer/Tracker/model.xml @@ -0,0 +1,1925 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/Database/SqlServer/Tracker/postdeploy.sql b/Database/SqlServer/Tracker/postdeploy.sql new file mode 100644 index 0000000..b72b0b1 --- /dev/null +++ b/Database/SqlServer/Tracker/postdeploy.sql @@ -0,0 +1,471 @@ +/* +Post-Deployment Script Template +-------------------------------------------------------------------------------------- + This file contains SQL statements that will be appended to the build script. + Use SQLCMD syntax to include a file in the post-deployment script. + Example: :r .\myfile.sql + Use SQLCMD syntax to reference a variable in the post-deployment script. + Example: :setvar TableName MyTable + SELECT * FROM [$(TableName)] +-------------------------------------------------------------------------------------- +*/ + +-- Table [dbo].[Priority] data +MERGE INTO [dbo].[Priority] AS t +USING +( + VALUES + ( + 1, + 'High', + 1, + 'A High Priority' + ), + ( + 2, + 'Normal', + 2, + 'A Normal Priority' + ), + ( + 3, + 'Low', + 3, + 'A Low Priority' + ) +) +AS s +( + [Id], + [Name], + [Order], + [Description] +) +ON +( + t.[Id] = s.[Id] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [Id], + [Name], + [Order], + [Description] + ) + VALUES + ( + s.[Id], + s.[Name], + s.[Order], + s.[Description] + ) +WHEN MATCHED THEN + UPDATE SET + t.[Name] = s.[Name], + t.[Order] = s.[Order], + t.[Description] = s.[Description] +OUTPUT $action as [Action]; + + +SET IDENTITY_INSERT [dbo].[Role] ON + +-- Table [dbo].[Role] data +MERGE INTO [dbo].[Role] AS t +USING +( + VALUES + ( + 1, + 'Admin', + 'Admin Role' + ), + ( + 2, + 'Manager', + NULL + ), + ( + 3, + 'Newb', + NULL + ), + ( + 4, + 'Nobody', + NULL + ), + ( + 5, + 'Power User', + NULL + ) +) +AS s +( + [Id], + [Name], + [Description] +) +ON +( + t.[Id] = s.[Id] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [Id], + [Name], + [Description] + ) + VALUES + ( + s.[Id], + s.[Name], + s.[Description] + ) +WHEN MATCHED THEN + UPDATE SET + t.[Name] = s.[Name], + t.[Description] = s.[Description] +OUTPUT $action as [Action]; + +SET IDENTITY_INSERT [dbo].[Role] OFF + + +SET IDENTITY_INSERT [dbo].[Status] ON + +-- Table [dbo].[Status] data +MERGE INTO [dbo].[Status] AS t +USING +( + VALUES + ( + 1, + 'Not Started', + NULL, + 1 + ), + ( + 2, + 'In Progress', + NULL, + 2 + ), + ( + 3, + 'Completed', + NULL, + 3 + ), + ( + 4, + 'Waiting on someone else', + NULL, + 4 + ), + ( + 5, + 'Deferred', + NULL, + 5 + ), + ( + 6, + 'Done', + NULL, + 6 + ) +) +AS s +( + [Id], + [Name], + [Description], + [Order] +) +ON +( + t.[Id] = s.[Id] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [Id], + [Name], + [Description], + [Order] + ) + VALUES + ( + s.[Id], + s.[Name], + s.[Description], + s.[Order] + ) +WHEN MATCHED THEN + UPDATE SET + t.[Name] = s.[Name], + t.[Description] = s.[Description], + t.[Order] = s.[Order] +OUTPUT $action as [Action]; + +SET IDENTITY_INSERT [dbo].[Status] OFF + + +SET IDENTITY_INSERT [dbo].[User] ON + +-- Table [dbo].[User] data +MERGE INTO [dbo].[User] AS t +USING +( + VALUES + ( + 1, + 'william.adama@battlestar.com', + 'William', + 'Adama', + NULL, + '1+v5rvSXnyX7tvwTKfM+aq+s0hDmNXsduGZfq8sQv1ggPnGlQdDdBdbUP0bUmbMbiU40PvRQWKRAy5QUd1xrAA', + '?#nkY', + 'Data Merge 635324524904242477', + 1, + '2014-04-07 07:26:54', + '2009-05-06 17:46:20', + NULL, + NULL + ), + ( + 2, + 'laura.roslin@battlestar.com', + 'Laura', + 'Roslin', + NULL, + 'Sx/jwRHFW/CQpO0E6G8d+jo344AmAKfX+C48a0iAZyMrb4sE8VoDuyZorbhbLZAX9f4UZk67O7eCjk854OrYSg', + 'Ph)6;', + 'Data Merge 635324524904242477', + 1, + '2014-04-07 07:26:54', + '2009-05-06 17:47:00', + NULL, + NULL + ), + ( + 3, + 'kara.thrace@battlestar.com', + 'Kara', + 'Thrace', + NULL, + '5KhtS4ax7G1aGkq97w02ooVZMmJp8bcySEKMSxruXu/Z/wRKoNAxMlkjRVY1yLavrC3GRYQZjy5b6JW8cR5EWg', + '!]@2/', + 'Data Merge 635324524147981355', + 1, + '2014-04-07 07:26:54', + '2009-05-06 17:47:43', + NULL, + NULL + ), + ( + 4, + 'lee.adama@battlestar.com', + 'Lee', + 'Adama', + NULL, + 'IrK8OhI2n4Ev3YA4y5kP7vy+n2CffX2MgcONbAh6/kZpNZYBYoYyrMhqdYztehL0NAIdvcInQ6zOjMplq+zWQA', + 'e@_a{', + 'Data Merge 635324524147981355', + 1, + '2014-04-07 07:26:54', + '2009-05-06 17:48:02', + NULL, + NULL + ), + ( + 5, + 'gaius.baltar@battlestar.com', + 'Gaius', + 'Baltar', + NULL, + '7tfajMaEerDNVgi6Oi6UJ6JxsUXZ0u4zQeUrZQxnaXJQ2f2vd9AzBR4sVOaH7LZtCjQopMzlQ38QqNEnpK0B/g', + '_qpA2', + 'Data Merge 635324524147981355', + 1, + '2014-04-07 07:26:54', + '2009-05-06 17:48:26', + NULL, + NULL + ), + ( + 6, + 'saul.tigh@battlestar.com', + 'Saul', + 'Tigh', + NULL, + 'wzkR89zRXe7hND1jqAP9xgupYJBvEZcjsfUe3TaU45kxRajjjS9u0fOTLK+uglzk67EGochJdeoikWs7hxMNRA', + 'h]-zG', + 'Data Merge 635324524147981355', + 1, + '2014-04-07 07:26:54', + '2009-05-06 17:49:26', + NULL, + NULL + ) +) +AS s +( + [Id], + [EmailAddress], + [FirstName], + [LastName], + [Avatar], + [PasswordHash], + [PasswordSalt], + [Comment], + [IsApproved], + [LastLoginDate], + [LastActivityDate], + [LastPasswordChangeDate], + [AvatarType] +) +ON +( + t.[Id] = s.[Id] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [Id], + [EmailAddress], + [FirstName], + [LastName], + [Avatar], + [PasswordHash], + [PasswordSalt], + [Comment], + [IsApproved], + [LastLoginDate], + [LastActivityDate], + [LastPasswordChangeDate], + [AvatarType] + ) + VALUES + ( + s.[Id], + s.[EmailAddress], + s.[FirstName], + s.[LastName], + s.[Avatar], + s.[PasswordHash], + s.[PasswordSalt], + s.[Comment], + s.[IsApproved], + s.[LastLoginDate], + s.[LastActivityDate], + s.[LastPasswordChangeDate], + s.[AvatarType] + ) +WHEN MATCHED THEN + UPDATE SET + t.[EmailAddress] = s.[EmailAddress], + t.[FirstName] = s.[FirstName], + t.[LastName] = s.[LastName], + t.[Avatar] = s.[Avatar], + t.[PasswordHash] = s.[PasswordHash], + t.[PasswordSalt] = s.[PasswordSalt], + t.[Comment] = s.[Comment], + t.[IsApproved] = s.[IsApproved], + t.[LastLoginDate] = s.[LastLoginDate], + t.[LastActivityDate] = s.[LastActivityDate], + t.[LastPasswordChangeDate] = s.[LastPasswordChangeDate], + t.[AvatarType] = s.[AvatarType] +OUTPUT $action as [Action]; + +SET IDENTITY_INSERT [dbo].[User] OFF + + +SET IDENTITY_INSERT [dbo].[Task] ON + +-- Table [dbo].[Task] data +MERGE INTO [dbo].[Task] AS t +USING +( + VALUES + ( + 1, + 1, + 1, + 2, + 'Make it to Earth', + 'Find and make it to earth while avoiding the cylons.', + NULL, + NULL, + NULL, + 1, + 'laura.roslin@battlestar.com' + ) +) +AS s +( + [Id], + [StatusId], + [PriorityId], + [CreatedId], + [Summary], + [Details], + [StartDate], + [DueDate], + [CompleteDate], + [AssignedId], + [LastModifiedBy] +) +ON +( + t.[Id] = s.[Id] +) +WHEN NOT MATCHED BY TARGET THEN + INSERT + ( + [Id], + [StatusId], + [PriorityId], + [CreatedId], + [Summary], + [Details], + [StartDate], + [DueDate], + [CompleteDate], + [AssignedId], + [LastModifiedBy] + ) + VALUES + ( + s.[Id], + s.[StatusId], + s.[PriorityId], + s.[CreatedId], + s.[Summary], + s.[Details], + s.[StartDate], + s.[DueDate], + s.[CompleteDate], + s.[AssignedId], + s.[LastModifiedBy] + ) +WHEN MATCHED THEN + UPDATE SET + t.[StatusId] = s.[StatusId], + t.[PriorityId] = s.[PriorityId], + t.[CreatedId] = s.[CreatedId], + t.[Summary] = s.[Summary], + t.[Details] = s.[Details], + t.[StartDate] = s.[StartDate], + t.[DueDate] = s.[DueDate], + t.[CompleteDate] = s.[CompleteDate], + t.[AssignedId] = s.[AssignedId], + t.[LastModifiedBy] = s.[LastModifiedBy] +OUTPUT $action as [Action]; + +SET IDENTITY_INSERT [dbo].[Task] OFF + + +GO diff --git a/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.net40.csproj b/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.net40.csproj index 78c0411..9ffc6b2 100644 --- a/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.net40.csproj +++ b/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.net40.csproj @@ -112,6 +112,9 @@ + + + - + \ No newline at end of file diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj b/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj index ddf8af6..2f80482 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj @@ -77,6 +77,7 @@ + diff --git a/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs b/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs index 8cb019f..18aabfa 100644 --- a/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs +++ b/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs @@ -394,10 +394,108 @@ public static Task UpdateAsync( var runner = ResolveRunner(); return runner.UpdateAsync(objectContext, entityMap, sourceQuery, updateExpression); } +#endif + + private static Tuple, EntityMap> CheckInsertParams(IDbSet destination, + IQueryable source) + where TEntity : class + where TModel : class + { + if (destination == null) + throw new ArgumentNullException("destination"); + if (source == null) + throw new ArgumentNullException("source"); + + ObjectQuery destQuery = destination.ToObjectQuery(); + if (destQuery == null) + throw new ArgumentException("The DbSet must be of type ObjectQuery or DbQuery.", "destination"); + + //ObjectContext destContext = destQuery.Context; + //if (destContext == null) + // throw new ArgumentException("The ObjectContext for the DbSet can not be null.", "destination"); + + EntityMap entityMap = destQuery.GetEntityMap(); + if (entityMap == null) + throw new ArgumentException("Could not load the entity mapping information for the destination.", "destination"); + + ObjectQuery sourceQuery = source.ToObjectQuery(); + if (sourceQuery == null) + throw new ArgumentException("The query must be of type ObjectQuery or DbQuery.", "source"); + + ObjectContext sourceContext = sourceQuery.Context; + if (sourceContext == null) + throw new ArgumentException("The ObjectContext for the query can not be null.", "source"); + return Tuple.Create(sourceQuery, entityMap); + } + + /// + /// Executes a statement `INSERT INTO [Table] (...) SELECT ...`. + /// + /// The type of the entity representing a record in database table. + /// The type of the query item. + /// The target table where the new data will be inserted into. + /// The query which retrieves the new data that will be inserted into the target table. + /// The number of row inserted. + /// Copy all items whose product id "K9-RT-02" from table item into item_2. + /// + /// + /// + /// When executing this method, the statement is immediately executed on the database provider + /// and is not part of the change tracking system. Also, changes will not be reflected on + /// any entities that have already been materialized in the current context. + /// + public static int Insert( + this IDbSet destination, + IQueryable source) + where TEntity : class + where TModel : class + { + var p = CheckInsertParams(destination, source); + return ResolveRunner().Insert(source, p.Item1, p.Item2); + } + +#if NET45 + /// + /// Executes a statement `INSERT INTO [Table] (...) SELECT ...` asynchronously. + /// + /// The type of the entity representing a record in database table. + /// The type of the query item. + /// The target table where the new data will be inserted into. + /// The query which retrieves the new data that will be inserted into the target table. + /// The number of row inserted. + /// Copy all items whose product id "K9-RT-02" from table item into item_2. + /// + /// + /// + /// When executing this method, the statement is immediately executed on the database provider + /// and is not part of the change tracking system. Also, changes will not be reflected on + /// any entities that have already been materialized in the current context. + /// + public static Task InsertAsync( + this IDbSet destination, + IQueryable source) + where TEntity : class + where TModel : class + { + var p = CheckInsertParams(destination, source); + return ResolveRunner().InsertAsync(source, p.Item1, p.Item2); + } #endif - private static IBatchRunner ResolveRunner() + internal static IBatchRunner ResolveRunner() { var provider = Locator.Current.Resolve(); if (provider == null) diff --git a/Source/EntityFramework.Extended/Locator.cs b/Source/EntityFramework.Extended/Locator.cs index 284dd0c..e669053 100644 --- a/Source/EntityFramework.Extended/Locator.cs +++ b/Source/EntityFramework.Extended/Locator.cs @@ -89,6 +89,7 @@ public static void RegisterDefaults(IContainer container) { container.Register(() => new MetadataMappingProvider()); container.Register(() => new SqlServerBatchRunner()); + //container.Register(() => new MySqlBatchRunner()); container.Register(() => new FutureRunner()); container.Register(() => new MemoryCacheProvider()); diff --git a/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs b/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs index 6c9235a..5678557 100644 --- a/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs +++ b/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs @@ -239,7 +239,7 @@ private static void SetTableName(EntityMap entityMap) private static string QuoteIdentifier(string name) { - return ("[" + name.Replace("]", "]]") + "]"); + return Extensions.BatchExtensions.ResolveRunner().Quote(name); } } } \ No newline at end of file diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Audit.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Audit.cs index 4c21db4..1652bfe 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Audit.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Audit.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,23 +8,38 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class Audit - { - public int Id { get; set; } - public System.DateTime Date { get; set; } - public Nullable UserId { get; set; } - public Nullable TaskId { get; set; } - public string Content { get; set; } - public string Username { get; set; } - public System.DateTime CreatedDate { get; set; } - public byte[] RowVersion { get; set; } - - public virtual Task Task { get; set; } - public virtual User User { get; set; } - } +public partial class Audit +{ + + public int Id { get; set; } + + public System.DateTime Date { get; set; } + + public Nullable UserId { get; set; } + + public Nullable TaskId { get; set; } + + public string Content { get; set; } + + public string Username { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public byte[] RowVersion { get; set; } + + + + public virtual Task Task { get; set; } + + public virtual User User { get; set; } + +} + } diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Item.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Item.cs new file mode 100644 index 0000000..2256104 --- /dev/null +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Item.cs @@ -0,0 +1,49 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + + +namespace Tracker.SqlServer.Entities +{ + +using System; + using System.Collections.Generic; + +public partial class Item +{ + + public string ItemId { get; set; } + + public string ProductId { get; set; } + + public Nullable ListPrice { get; set; } + + public Nullable UnitCost { get; set; } + + public Nullable Supplier { get; set; } + + public string Status { get; set; } + + public string Attr1 { get; set; } + + public string Attr2 { get; set; } + + public string Attr3 { get; set; } + + public string Attr4 { get; set; } + + public string Attr5 { get; set; } + + + + public virtual Product Product { get; set; } + +} + +} diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Item_2.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Item_2.cs new file mode 100644 index 0000000..52feeaa --- /dev/null +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Item_2.cs @@ -0,0 +1,49 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + + +namespace Tracker.SqlServer.Entities +{ + +using System; + using System.Collections.Generic; + +public partial class Item_2 +{ + + public string ItemId { get; set; } + + public string ProductId { get; set; } + + public Nullable ListPrice { get; set; } + + public Nullable UnitCost { get; set; } + + public Nullable Supplier { get; set; } + + public string Status { get; set; } + + public string Attr1 { get; set; } + + public string Attr2 { get; set; } + + public string Attr3 { get; set; } + + public string Attr4 { get; set; } + + public string Attr5 { get; set; } + + + + public virtual Product Product { get; set; } + +} + +} diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Priority.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Priority.cs index fad1458..8e20972 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Priority.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Priority.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,26 +8,42 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class Priority +public partial class Priority +{ + + public Priority() { - public Priority() - { - this.Tasks = new HashSet(); - } - - public int Id { get; set; } - public string Name { get; set; } - public int Order { get; set; } - public string Description { get; set; } - public System.DateTime CreatedDate { get; set; } - public System.DateTime ModifiedDate { get; set; } - public byte[] RowVersion { get; set; } - - public virtual ICollection Tasks { get; set; } + + this.Tasks = new HashSet(); + } + + + public int Id { get; set; } + + public string Name { get; set; } + + public int Order { get; set; } + + public string Description { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public System.DateTime ModifiedDate { get; set; } + + public byte[] RowVersion { get; set; } + + + + public virtual ICollection Tasks { get; set; } + +} + } diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Product.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Product.cs new file mode 100644 index 0000000..2366ea0 --- /dev/null +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Product.cs @@ -0,0 +1,47 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + + +namespace Tracker.SqlServer.Entities +{ + +using System; + using System.Collections.Generic; + +public partial class Product +{ + + public Product() + { + + this.Items = new HashSet(); + + this.Item_2 = new HashSet(); + + } + + + public string ProductId { get; set; } + + public string Category { get; set; } + + public string Name { get; set; } + + public string Descn { get; set; } + + + + public virtual ICollection Items { get; set; } + + public virtual ICollection Item_2 { get; set; } + +} + +} diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/ProductSummary.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/ProductSummary.cs new file mode 100644 index 0000000..9b8f007 --- /dev/null +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/ProductSummary.cs @@ -0,0 +1,31 @@ + +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + + +namespace Tracker.SqlServer.Entities +{ + +using System; + using System.Collections.Generic; + +public partial class ProductSummary +{ + + public string ProductId { get; set; } + + public string Name { get; set; } + + public decimal AvgPrice { get; set; } + + public bool Verified { get; set; } + +} + +} diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Role.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Role.cs index 6b2b08f..1619326 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Role.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Role.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,25 +8,40 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class Role +public partial class Role +{ + + public Role() { - public Role() - { - this.Users = new HashSet(); - } - - public int Id { get; set; } - public string Name { get; set; } - public string Description { get; set; } - public System.DateTime CreatedDate { get; set; } - public System.DateTime ModifiedDate { get; set; } - public byte[] RowVersion { get; set; } - - public virtual ICollection Users { get; set; } + + this.Users = new HashSet(); + } + + + public int Id { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public System.DateTime ModifiedDate { get; set; } + + public byte[] RowVersion { get; set; } + + + + public virtual ICollection Users { get; set; } + +} + } diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Status.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Status.cs index 2468b9d..e1b566f 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Status.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Status.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,26 +8,42 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class Status +public partial class Status +{ + + public Status() { - public Status() - { - this.Tasks = new HashSet(); - } - - public int Id { get; set; } - public string Name { get; set; } - public string Description { get; set; } - public int Order { get; set; } - public System.DateTime CreatedDate { get; set; } - public System.DateTime ModifiedDate { get; set; } - public byte[] RowVersion { get; set; } - - public virtual ICollection Tasks { get; set; } + + this.Tasks = new HashSet(); + } + + + public int Id { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public int Order { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public System.DateTime ModifiedDate { get; set; } + + public byte[] RowVersion { get; set; } + + + + public virtual ICollection Tasks { get; set; } + +} + } diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Task.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Task.cs index cea704c..461c102 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Task.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Task.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,38 +8,66 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class Task +public partial class Task +{ + + public Task() { - public Task() - { - this.Audits = new HashSet(); - } - - public int Id { get; set; } - public int StatusId { get; set; } - public Nullable PriorityId { get; set; } - public int CreatedId { get; set; } - public string Summary { get; set; } - public string Details { get; set; } - public Nullable StartDate { get; set; } - public Nullable DueDate { get; set; } - public Nullable CompleteDate { get; set; } - public Nullable AssignedId { get; set; } - public System.DateTime CreatedDate { get; set; } - public System.DateTime ModifiedDate { get; set; } - public byte[] RowVersion { get; set; } - public string LastModifiedBy { get; set; } - - public virtual ICollection Audits { get; set; } - public virtual Priority Priority { get; set; } - public virtual Status Status { get; set; } - public virtual User User { get; set; } - public virtual User User1 { get; set; } - public virtual TaskExtended TaskExtended { get; set; } + + this.Audits = new HashSet(); + } + + + public int Id { get; set; } + + public int StatusId { get; set; } + + public Nullable PriorityId { get; set; } + + public int CreatedId { get; set; } + + public string Summary { get; set; } + + public string Details { get; set; } + + public Nullable StartDate { get; set; } + + public Nullable DueDate { get; set; } + + public Nullable CompleteDate { get; set; } + + public Nullable AssignedId { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public System.DateTime ModifiedDate { get; set; } + + public byte[] RowVersion { get; set; } + + public string LastModifiedBy { get; set; } + + + + public virtual ICollection Audits { get; set; } + + public virtual Priority Priority { get; set; } + + public virtual Status Status { get; set; } + + public virtual User User { get; set; } + + public virtual User User1 { get; set; } + + public virtual TaskExtended TaskExtended { get; set; } + +} + } diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/TaskExtended.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/TaskExtended.cs index 5f59f9d..4533b5f 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/TaskExtended.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/TaskExtended.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,20 +8,32 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class TaskExtended - { - public int TaskId { get; set; } - public string Browser { get; set; } - public string OS { get; set; } - public System.DateTime CreatedDate { get; set; } - public System.DateTime ModifiedDate { get; set; } - public byte[] RowVersion { get; set; } - - public virtual Task Task { get; set; } - } +public partial class TaskExtended +{ + + public int TaskId { get; set; } + + public string Browser { get; set; } + + public string OS { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public System.DateTime ModifiedDate { get; set; } + + public byte[] RowVersion { get; set; } + + + + public virtual Task Task { get; set; } + +} + } diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Context.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Context.cs index ccf31f8..b196ef4 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Context.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Context.cs @@ -1,4 +1,6 @@ -//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ // // This code was generated from a template. // @@ -7,30 +9,52 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; - using System.Data.Entity; - using System.Data.Entity.Infrastructure; - - public partial class TrackerEntities : DbContext + +using System; +using System.Data.Entity; +using System.Data.Entity.Infrastructure; + + +public partial class TrackerEntities : DbContext +{ + public TrackerEntities() + : base("name=TrackerEntities") { - public TrackerEntities() - : base("name=TrackerEntities") - { - } - - protected override void OnModelCreating(DbModelBuilder modelBuilder) - { - throw new UnintentionalCodeFirstException(); - } - - public virtual DbSet Audits { get; set; } - public virtual DbSet Priorities { get; set; } - public virtual DbSet Roles { get; set; } - public virtual DbSet Status { get; set; } - public virtual DbSet Tasks { get; set; } - public virtual DbSet TaskExtendeds { get; set; } - public virtual DbSet Users { get; set; } + + } + + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + throw new UnintentionalCodeFirstException(); } + + + public virtual DbSet Audits { get; set; } + + public virtual DbSet Priorities { get; set; } + + public virtual DbSet Roles { get; set; } + + public virtual DbSet Status { get; set; } + + public virtual DbSet Tasks { get; set; } + + public virtual DbSet TaskExtendeds { get; set; } + + public virtual DbSet Users { get; set; } + + public virtual DbSet Items { get; set; } + + public virtual DbSet Item_2 { get; set; } + + public virtual DbSet Products { get; set; } + + public virtual DbSet ProductSummaries { get; set; } + } + +} + diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Designer.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Designer.cs index 48f2f0b..71f238c 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Designer.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.Designer.cs @@ -1,4 +1,4 @@ -// T4 code generation is enabled for model 'C:\Projects\github\EntityFramework.Extended\Source\Samples\net40\Tracker.SqlServer.Entities\Tracker.edmx'. +// T4 code generation is enabled for model 'E:\Projects\EntityFramework.Extended\modif\Source\Samples\net40\Tracker.SqlServer.Entities\Tracker.edmx'. // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model // is open in the designer. diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.net40.csproj b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.net40.csproj index b1734e5..e23ec36 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.net40.csproj +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.net40.csproj @@ -59,9 +59,21 @@ Tracker.tt + + Tracker.tt + + + Tracker.tt + Tracker.tt + + Tracker.tt + + + Tracker.tt + Tracker.tt diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.cs index 7cc0662..c36263e 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.cs @@ -1,4 +1,6 @@ -//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ // // This code was generated from a template. // @@ -7,3 +9,4 @@ // //------------------------------------------------------------------------------ + diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx index b55f266..1e8559c 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx @@ -4,7 +4,7 @@ - + @@ -18,6 +18,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -30,6 +62,24 @@ + + + + + + + + + + + + + + + + + + @@ -112,6 +162,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -222,13 +296,25 @@ + + + + + + + + + + + + @@ -266,8 +352,7 @@ - - + @@ -514,7 +599,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -635,6 +810,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx.diagram b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx.diagram index 80b310d..d2bcfda 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx.diagram +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/Tracker.edmx.diagram @@ -4,7 +4,7 @@ - + @@ -20,6 +20,12 @@ + + + + + + diff --git a/Source/Samples/net40/Tracker.SqlServer.Entities/User.cs b/Source/Samples/net40/Tracker.SqlServer.Entities/User.cs index 725ec8d..71b871c 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Entities/User.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Entities/User.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,41 +8,72 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class User +public partial class User +{ + + public User() { - public User() - { - this.Audits = new HashSet(); - this.Tasks = new HashSet(); - this.Tasks1 = new HashSet(); - this.Roles = new HashSet(); - } - - public int Id { get; set; } - public string EmailAddress { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public byte[] Avatar { get; set; } - public System.DateTime CreatedDate { get; set; } - public System.DateTime ModifiedDate { get; set; } - public byte[] RowVersion { get; set; } - public string PasswordHash { get; set; } - public string PasswordSalt { get; set; } - public string Comment { get; set; } - public bool IsApproved { get; set; } - public Nullable LastLoginDate { get; set; } - public System.DateTime LastActivityDate { get; set; } - public Nullable LastPasswordChangeDate { get; set; } - public string AvatarType { get; set; } - - public virtual ICollection Audits { get; set; } - public virtual ICollection Tasks { get; set; } - public virtual ICollection Tasks1 { get; set; } - public virtual ICollection Roles { get; set; } + + this.Audits = new HashSet(); + + this.Tasks = new HashSet(); + + this.Tasks1 = new HashSet(); + + this.Roles = new HashSet(); + } + + + public int Id { get; set; } + + public string EmailAddress { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + + public byte[] Avatar { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public System.DateTime ModifiedDate { get; set; } + + public byte[] RowVersion { get; set; } + + public string PasswordHash { get; set; } + + public string PasswordSalt { get; set; } + + public string Comment { get; set; } + + public bool IsApproved { get; set; } + + public Nullable LastLoginDate { get; set; } + + public System.DateTime LastActivityDate { get; set; } + + public Nullable LastPasswordChangeDate { get; set; } + + public string AvatarType { get; set; } + + + + public virtual ICollection Audits { get; set; } + + public virtual ICollection Tasks { get; set; } + + public virtual ICollection Tasks1 { get; set; } + + public virtual ICollection Roles { get; set; } + +} + } diff --git a/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs b/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs index 11dd2d0..adc14d6 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs @@ -70,5 +70,170 @@ public void TransactionScopeObjectContext() } } + + private void _Insert(TrackerEntities db) + { + db.ProductSummaries.Delete(); + var query = from product in db.Products + join item2 in ( + from item in db.Items + group item by item.ProductId into grItem + select new + { + ProductId = grItem.Key, + AvgPrice = grItem.Average(x => x.ListPrice + x.UnitCost) + } + ) on product.ProductId equals item2.ProductId into items + from item3 in items.DefaultIfEmpty() + select new ProductSummary2 + { + ProductId = product.ProductId, + Name = product.Name, + AvgPrice = item3.AvgPrice ?? 0 + }; + db.ProductSummaries.Insert(query); + var source = query.ToArray(); + var result = db.ProductSummaries.ToArray(); + for (int i = 0; i < source.Length; i++) + { + source[i].AvgPrice = Math.Round(source[i].AvgPrice, 2, MidpointRounding.AwayFromZero); //In database, only two digits after decimal point + source[i].Verified = true; //Verified was not set in query. In database, its default value is true (1) + } + Assert.True(result.OrderBy(i => i.ProductId).SequenceEqual(source.OrderBy(i => i.ProductId), new ProductSummaryComparer())); + + db.Item_2.Delete(); + var query2 = db.Items.Where(item => item.ListPrice / item.UnitCost >= 5); + db.Item_2.Insert(query2); + var source2 = query2.ToArray().OrderBy(i => i.ItemId); + var result2 = db.Item_2.ToArray().Select(i => ItemComparer.GetItem(i)).OrderBy(i => i.ItemId); + Assert.True(result2.SequenceEqual(source2, new ItemComparer())); + + + db.Item_2.Delete(); + var query3 = from item in db.Items where item.ProductId == "K9-RT-02" select item; + db.Item_2.Insert(query3); + var source3 = query3.ToArray().OrderBy(item => item.ItemId); + var result3 = db.Item_2.ToArray().Select(i => ItemComparer.GetItem(i)).OrderBy(item => item.ItemId); + Assert.True(result3.SequenceEqual(source3, new ItemComparer())); + } + + [Fact] + public void InsertNoTransaction() + { + using (var db = new TrackerEntities()) + { + _Insert(db); + } + } + + [Fact] + public void InsertInTransaction() + { + using (var db = new TrackerEntities()) + using (var tx = db.Database.BeginTransaction()) + { + _Insert(db); + tx.Commit(); + } + } + + [Fact] + public void InsertInTransactionScope() + { + using (var tx = new TransactionScope()) + using (var db = new TrackerEntities()) + { + _Insert(db); + tx.Complete(); + } + } + } + + class ProductSummary2 : ProductSummary { } + + class ProductSummaryComparer : IEqualityComparer, System.Collections.IComparer + { + public int Compare(object x, object y) + { + if (x == null && y == null) return 0; + var x2 = x as ProductSummary; + var y2 = y as ProductSummary; + if (x2 != null && y2 != null) + { + if (x2.ProductId == y2.ProductId + && x2.Name == y2.Name + && x2.AvgPrice == y2.AvgPrice + && x2.Verified == y2.Verified) + return 0; + } + return -1; + } + + public bool Equals(ProductSummary x, ProductSummary y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(ProductSummary obj) + { + if (obj == null) return 0; + return obj.ProductId.GetHashCode(); + } + } + + class ItemComparer : IEqualityComparer, System.Collections.IComparer + { + public int Compare(object x, object y) + { + if (x == null && y == null) return 0; + if (x == null || y == null) return -1; + Item item = GetItem(x), item2 = GetItem(y); + if (item.ItemId == item2.ItemId + && item.ProductId == item2.ProductId + && item.ListPrice == item2.ListPrice + && item.UnitCost == item2.UnitCost + && item.Supplier == item2.Supplier + && item.Status == item2.Status + && item.Attr1 == item2.Attr1 + && item.Attr2 == item2.Attr2 + && item.Attr3 == item2.Attr3 + && item.Attr4 == item2.Attr4 + && item.Attr5 == item2.Attr5) return 0; + return -1; + } + + public bool Equals(Item x, Item y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(Item obj) + { + return obj.ItemId.GetHashCode(); + } + + public static Item GetItem(object obj) + { + if (obj is Item) return obj as Item; + if (obj is Item_2) + { + var item2 = obj as Item_2; + return new Item + { + ItemId = item2.ItemId, + ProductId = item2.ProductId, + ListPrice = item2.ListPrice, + UnitCost = item2.UnitCost, + Supplier = item2.Supplier, + Status = item2.Status, + Attr1 = item2.Attr1, + Attr2 = item2.Attr2, + Attr3 = item2.Attr3, + Attr4 = item2.Attr4, + Attr5 = item2.Attr5 + }; + } + return null; + } } } diff --git a/Source/Samples/net40/Tracker.SqlServer.Test/Tracker.SqlServer.Test.net40.csproj b/Source/Samples/net40/Tracker.SqlServer.Test/Tracker.SqlServer.Test.net40.csproj index 1295e6a..944dc16 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Test/Tracker.SqlServer.Test.net40.csproj +++ b/Source/Samples/net40/Tracker.SqlServer.Test/Tracker.SqlServer.Test.net40.csproj @@ -117,6 +117,9 @@ + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.MySql.Entities/Tracker.cs b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.cs new file mode 100644 index 0000000..7cc0662 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.cs @@ -0,0 +1,9 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + diff --git a/Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx new file mode 100644 index 0000000..a85e3fd --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx @@ -0,0 +1,832 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx.diagram b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx.diagram new file mode 100644 index 0000000..41ce5d7 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.edmx.diagram @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.MySql.Entities/Tracker.tt b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.tt new file mode 100644 index 0000000..b856ae3 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.tt @@ -0,0 +1,733 @@ +<#@ template language="C#" debug="false" hostspecific="true"#> +<#@ include file="EF6.Utility.CS.ttinclude"#><#@ + output extension=".cs"#><# + +const string inputFile = @"Tracker.edmx"; +var textTransform = DynamicTextTransformation.Create(this); +var code = new CodeGenerationTools(this); +var ef = new MetadataTools(this); +var typeMapper = new TypeMapper(code, ef, textTransform.Errors); +var fileManager = EntityFrameworkTemplateFileManager.Create(this); +var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); +var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); + +if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) +{ + return string.Empty; +} + +WriteHeader(codeStringGenerator, fileManager); + +foreach (var entity in typeMapper.GetItemsToGenerate(itemCollection)) +{ + fileManager.StartNewFile(entity.Name + ".cs"); + BeginNamespace(code); +#> +<#=codeStringGenerator.UsingDirectives(inHeader: false)#> +<#=codeStringGenerator.EntityClassOpening(entity)#> +{ +<# + var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity); + var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity); + var complexProperties = typeMapper.GetComplexProperties(entity); + + if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any()) + { +#> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public <#=code.Escape(entity)#>() + { +<# + foreach (var edmProperty in propertiesWithDefaultValues) + { +#> + this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; +<# + } + + foreach (var navigationProperty in collectionNavigationProperties) + { +#> + this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>(); +<# + } + + foreach (var complexProperty in complexProperties) + { +#> + this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); +<# + } +#> + } + +<# + } + + var simpleProperties = typeMapper.GetSimpleProperties(entity); + if (simpleProperties.Any()) + { + foreach (var edmProperty in simpleProperties) + { +#> + <#=codeStringGenerator.Property(edmProperty)#> +<# + } + } + + if (complexProperties.Any()) + { +#> + +<# + foreach(var complexProperty in complexProperties) + { +#> + <#=codeStringGenerator.Property(complexProperty)#> +<# + } + } + + var navigationProperties = typeMapper.GetNavigationProperties(entity); + if (navigationProperties.Any()) + { +#> + +<# + foreach (var navigationProperty in navigationProperties) + { + if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) + { +#> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] +<# + } +#> + <#=codeStringGenerator.NavigationProperty(navigationProperty)#> +<# + } + } +#> +} +<# + EndNamespace(code); +} + +foreach (var complex in typeMapper.GetItemsToGenerate(itemCollection)) +{ + fileManager.StartNewFile(complex.Name + ".cs"); + BeginNamespace(code); +#> +<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> +<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> +{ +<# + var complexProperties = typeMapper.GetComplexProperties(complex); + var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex); + + if (propertiesWithDefaultValues.Any() || complexProperties.Any()) + { +#> + public <#=code.Escape(complex)#>() + { +<# + foreach (var edmProperty in propertiesWithDefaultValues) + { +#> + this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; +<# + } + + foreach (var complexProperty in complexProperties) + { +#> + this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); +<# + } +#> + } + +<# + } + + var simpleProperties = typeMapper.GetSimpleProperties(complex); + if (simpleProperties.Any()) + { + foreach(var edmProperty in simpleProperties) + { +#> + <#=codeStringGenerator.Property(edmProperty)#> +<# + } + } + + if (complexProperties.Any()) + { +#> + +<# + foreach(var edmProperty in complexProperties) + { +#> + <#=codeStringGenerator.Property(edmProperty)#> +<# + } + } +#> +} +<# + EndNamespace(code); +} + +foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection)) +{ + fileManager.StartNewFile(enumType.Name + ".cs"); + BeginNamespace(code); +#> +<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> +<# + if (typeMapper.EnumIsFlags(enumType)) + { +#> +[Flags] +<# + } +#> +<#=codeStringGenerator.EnumOpening(enumType)#> +{ +<# + var foundOne = false; + + foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType)) + { + foundOne = true; +#> + <#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>, +<# + } + + if (foundOne) + { + this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1); + } +#> +} +<# + EndNamespace(code); +} + +fileManager.Process(); + +#> +<#+ + +public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager) +{ + fileManager.StartHeader(); +#> +//------------------------------------------------------------------------------ +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> +// +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> +// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> +// +//------------------------------------------------------------------------------ +<#=codeStringGenerator.UsingDirectives(inHeader: true)#> +<#+ + fileManager.EndBlock(); +} + +public void BeginNamespace(CodeGenerationTools code) +{ + var codeNamespace = code.VsNamespaceSuggestion(); + if (!String.IsNullOrEmpty(codeNamespace)) + { +#> +namespace <#=code.EscapeNamespace(codeNamespace)#> +{ +<#+ + PushIndent(" "); + } +} + +public void EndNamespace(CodeGenerationTools code) +{ + if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion())) + { + PopIndent(); +#> +} +<#+ + } +} + +public const string TemplateId = "CSharp_DbContext_Types_EF6"; + +public class CodeStringGenerator +{ + private readonly CodeGenerationTools _code; + private readonly TypeMapper _typeMapper; + private readonly MetadataTools _ef; + + public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) + { + ArgumentNotNull(code, "code"); + ArgumentNotNull(typeMapper, "typeMapper"); + ArgumentNotNull(ef, "ef"); + + _code = code; + _typeMapper = typeMapper; + _ef = ef; + } + + public string Property(EdmProperty edmProperty) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2} {{ {3}get; {4}set; }}", + Accessibility.ForProperty(edmProperty), + _typeMapper.GetTypeName(edmProperty.TypeUsage), + _code.Escape(edmProperty), + _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), + _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); + } + + public string NavigationProperty(NavigationProperty navProp) + { + var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType()); + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2} {{ {3}get; {4}set; }}", + AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)), + navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, + _code.Escape(navProp), + _code.SpaceAfter(Accessibility.ForGetter(navProp)), + _code.SpaceAfter(Accessibility.ForSetter(navProp))); + } + + public string AccessibilityAndVirtual(string accessibility) + { + return accessibility + (accessibility != "private" ? " virtual" : ""); + } + + public string EntityClassOpening(EntityType entity) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1}partial class {2}{3}", + Accessibility.ForType(entity), + _code.SpaceAfter(_code.AbstractOption(entity)), + _code.Escape(entity), + _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); + } + + public string EnumOpening(SimpleType enumType) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} enum {1} : {2}", + Accessibility.ForType(enumType), + _code.Escape(enumType), + _code.Escape(_typeMapper.UnderlyingClrType(enumType))); + } + + public void WriteFunctionParameters(EdmFunction edmFunction, Action writeParameter) + { + var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); + foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) + { + var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; + var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")"; + var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + "))"; + writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); + } + } + + public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) + { + var parameters = _typeMapper.GetParameters(edmFunction); + + return string.Format( + CultureInfo.InvariantCulture, + "{0} IQueryable<{1}> {2}({3})", + AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), + _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), + _code.Escape(edmFunction), + string.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray())); + } + + public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) + { + var parameters = _typeMapper.GetParameters(edmFunction); + + return string.Format( + CultureInfo.InvariantCulture, + "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", + _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), + edmFunction.NamespaceName, + edmFunction.Name, + string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), + _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); + } + + public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) + { + var parameters = _typeMapper.GetParameters(edmFunction); + var returnType = _typeMapper.GetReturnType(edmFunction); + + var paramList = String.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray()); + if (includeMergeOption) + { + paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; + } + + return string.Format( + CultureInfo.InvariantCulture, + "{0} {1} {2}({3})", + AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), + returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", + _code.Escape(edmFunction), + paramList); + } + + public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) + { + var parameters = _typeMapper.GetParameters(edmFunction); + var returnType = _typeMapper.GetReturnType(edmFunction); + + var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); + if (includeMergeOption) + { + callParams = ", mergeOption" + callParams; + } + + return string.Format( + CultureInfo.InvariantCulture, + "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", + returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", + edmFunction.Name, + callParams); + } + + public string DbSet(EntitySet entitySet) + { + return string.Format( + CultureInfo.InvariantCulture, + "{0} virtual DbSet<{1}> {2} {{ get; set; }}", + Accessibility.ForReadOnlyProperty(entitySet), + _typeMapper.GetTypeName(entitySet.ElementType), + _code.Escape(entitySet)); + } + + public string UsingDirectives(bool inHeader, bool includeCollections = true) + { + return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) + ? string.Format( + CultureInfo.InvariantCulture, + "{0}using System;{1}" + + "{2}", + inHeader ? Environment.NewLine : "", + includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", + inHeader ? "" : Environment.NewLine) + : ""; + } +} + +public class TypeMapper +{ + private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; + + private readonly System.Collections.IList _errors; + private readonly CodeGenerationTools _code; + private readonly MetadataTools _ef; + + public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) + { + ArgumentNotNull(code, "code"); + ArgumentNotNull(ef, "ef"); + ArgumentNotNull(errors, "errors"); + + _code = code; + _ef = ef; + _errors = errors; + } + + public static string FixNamespaces(string typeName) + { + return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial."); + } + + public string GetTypeName(TypeUsage typeUsage) + { + return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); + } + + public string GetTypeName(EdmType edmType) + { + return GetTypeName(edmType, isNullable: null, modelNamespace: null); + } + + public string GetTypeName(TypeUsage typeUsage, string modelNamespace) + { + return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); + } + + public string GetTypeName(EdmType edmType, string modelNamespace) + { + return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); + } + + public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) + { + if (edmType == null) + { + return null; + } + + var collectionType = edmType as CollectionType; + if (collectionType != null) + { + return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); + } + + var typeName = _code.Escape(edmType.MetadataProperties + .Where(p => p.Name == ExternalTypeNameAttributeName) + .Select(p => (string)p.Value) + .FirstOrDefault()) + ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? + _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : + _code.Escape(edmType)); + + if (edmType is StructuralType) + { + return typeName; + } + + if (edmType is SimpleType) + { + var clrType = UnderlyingClrType(edmType); + if (!IsEnumType(edmType)) + { + typeName = _code.Escape(clrType); + } + + typeName = FixNamespaces(typeName); + + return clrType.IsValueType && isNullable == true ? + String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : + typeName; + } + + throw new ArgumentException("edmType"); + } + + public Type UnderlyingClrType(EdmType edmType) + { + ArgumentNotNull(edmType, "edmType"); + + var primitiveType = edmType as PrimitiveType; + if (primitiveType != null) + { + return primitiveType.ClrEquivalentType; + } + + if (IsEnumType(edmType)) + { + return GetEnumUnderlyingType(edmType).ClrEquivalentType; + } + + return typeof(object); + } + + public object GetEnumMemberValue(MetadataItem enumMember) + { + ArgumentNotNull(enumMember, "enumMember"); + + var valueProperty = enumMember.GetType().GetProperty("Value"); + return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); + } + + public string GetEnumMemberName(MetadataItem enumMember) + { + ArgumentNotNull(enumMember, "enumMember"); + + var nameProperty = enumMember.GetType().GetProperty("Name"); + return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); + } + + public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + var membersProperty = enumType.GetType().GetProperty("Members"); + return membersProperty != null + ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) + : Enumerable.Empty(); + } + + public bool EnumIsFlags(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); + return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); + } + + public bool IsEnumType(GlobalItem edmType) + { + ArgumentNotNull(edmType, "edmType"); + + return edmType.GetType().Name == "EnumType"; + } + + public PrimitiveType GetEnumUnderlyingType(EdmType enumType) + { + ArgumentNotNull(enumType, "enumType"); + + return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); + } + + public string CreateLiteral(object value) + { + if (value == null || value.GetType() != typeof(TimeSpan)) + { + return _code.CreateLiteral(value); + } + + return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); + } + + public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable types, string sourceFile) + { + ArgumentNotNull(types, "types"); + ArgumentNotNull(sourceFile, "sourceFile"); + + var hash = new HashSet(StringComparer.InvariantCultureIgnoreCase); + if (types.Any(item => !hash.Add(item))) + { + _errors.Add( + new CompilerError(sourceFile, -1, -1, "6023", + String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString("Template_CaseInsensitiveTypeConflict")))); + return false; + } + return true; + } + + public IEnumerable GetEnumItemsToGenerate(IEnumerable itemCollection) + { + return GetItemsToGenerate(itemCollection) + .Where(e => IsEnumType(e)); + } + + public IEnumerable GetItemsToGenerate(IEnumerable itemCollection) where T: EdmType + { + return itemCollection + .OfType() + .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) + .OrderBy(i => i.Name); + } + + public IEnumerable GetAllGlobalItems(IEnumerable itemCollection) + { + return itemCollection + .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) + .Select(g => GetGlobalItemName(g)); + } + + public string GetGlobalItemName(GlobalItem item) + { + if (item is EdmType) + { + return ((EdmType)item).Name; + } + else + { + return ((EntityContainer)item).Name; + } + } + + public IEnumerable GetSimpleProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable GetSimpleProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); + } + + public IEnumerable GetComplexProperties(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable GetComplexProperties(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); + } + + public IEnumerable GetPropertiesWithDefaultValues(EntityType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable GetPropertiesWithDefaultValues(ComplexType type) + { + return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); + } + + public IEnumerable GetNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type); + } + + public IEnumerable GetCollectionNavigationProperties(EntityType type) + { + return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); + } + + public FunctionParameter GetReturnParameter(EdmFunction edmFunction) + { + ArgumentNotNull(edmFunction, "edmFunction"); + + var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); + return returnParamsProperty == null + ? edmFunction.ReturnParameter + : ((IEnumerable)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); + } + + public bool IsComposable(EdmFunction edmFunction) + { + ArgumentNotNull(edmFunction, "edmFunction"); + + var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); + return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); + } + + public IEnumerable GetParameters(EdmFunction edmFunction) + { + return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); + } + + public TypeUsage GetReturnType(EdmFunction edmFunction) + { + var returnParam = GetReturnParameter(edmFunction); + return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); + } + + public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) + { + var returnType = GetReturnType(edmFunction); + return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; + } +} + +public static void ArgumentNotNull(T arg, string name) where T : class +{ + if (arg == null) + { + throw new ArgumentNullException(name); + } +} +#> \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.MySql.Entities/app.config b/Source/Samples/net45/Tracker.MySql.Entities/app.config new file mode 100644 index 0000000..7f43469 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/app.config @@ -0,0 +1,33 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Samples/net45/Tracker.MySql.Entities/audit.cs b/Source/Samples/net45/Tracker.MySql.Entities/audit.cs new file mode 100644 index 0000000..754ead6 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/audit.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class audit + { + public int Id { get; set; } + public System.DateTime Date { get; set; } + public Nullable UserId { get; set; } + public Nullable TaskId { get; set; } + public string Content { get; set; } + public string Username { get; set; } + public System.DateTime CreatedDate { get; set; } + public byte[] RowVersion { get; set; } + + public virtual task task { get; set; } + public virtual user user { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/item.cs b/Source/Samples/net45/Tracker.MySql.Entities/item.cs new file mode 100644 index 0000000..db0847c --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/item.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class item + { + public string ItemId { get; set; } + public string ProductId { get; set; } + public Nullable ListPrice { get; set; } + public Nullable UnitCost { get; set; } + public Nullable Supplier { get; set; } + public string Status { get; set; } + public string Attr1 { get; set; } + public string Attr2 { get; set; } + public string Attr3 { get; set; } + public string Attr4 { get; set; } + public string Attr5 { get; set; } + + public virtual product product { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/item_2.cs b/Source/Samples/net45/Tracker.MySql.Entities/item_2.cs new file mode 100644 index 0000000..dc42fdb --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/item_2.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class item_2 + { + public string ItemId { get; set; } + public string ProductId { get; set; } + public Nullable ListPrice { get; set; } + public Nullable UnitCost { get; set; } + public Nullable Supplier { get; set; } + public string Status { get; set; } + public string Attr1 { get; set; } + public string Attr2 { get; set; } + public string Attr3 { get; set; } + public string Attr4 { get; set; } + public string Attr5 { get; set; } + + public virtual product product { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/packages.config b/Source/Samples/net45/Tracker.MySql.Entities/packages.config new file mode 100644 index 0000000..6a68e72 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.MySql.Entities/priority.cs b/Source/Samples/net45/Tracker.MySql.Entities/priority.cs new file mode 100644 index 0000000..815797a --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/priority.cs @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class priority + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public priority() + { + this.tasks = new HashSet(); + } + + public int Id { get; set; } + public string Name { get; set; } + public int Order { get; set; } + public string Description { get; set; } + public System.DateTime CreatedDate { get; set; } + public System.DateTime ModifiedDate { get; set; } + public byte[] RowVersion { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection tasks { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/product.cs b/Source/Samples/net45/Tracker.MySql.Entities/product.cs new file mode 100644 index 0000000..54d674d --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/product.cs @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class product + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public product() + { + this.items = new HashSet(); + this.item_2 = new HashSet(); + } + + public string ProductId { get; set; } + public string Category { get; set; } + public string Name { get; set; } + public string Descn { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection items { get; set; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection item_2 { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/productsummary.cs b/Source/Samples/net45/Tracker.MySql.Entities/productsummary.cs new file mode 100644 index 0000000..ac8f8e2 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/productsummary.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class productsummary + { + public string ProductId { get; set; } + public string Name { get; set; } + public decimal AvgPrice { get; set; } + public bool Verified { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/role.cs b/Source/Samples/net45/Tracker.MySql.Entities/role.cs new file mode 100644 index 0000000..62f638d --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/role.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class role + { + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public System.DateTime CreatedDate { get; set; } + public System.DateTime ModifiedDate { get; set; } + public byte[] RowVersion { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/status.cs b/Source/Samples/net45/Tracker.MySql.Entities/status.cs new file mode 100644 index 0000000..a261c4d --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/status.cs @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class status + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public status() + { + this.tasks = new HashSet(); + } + + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public int Order { get; set; } + public System.DateTime CreatedDate { get; set; } + public System.DateTime ModifiedDate { get; set; } + public byte[] RowVersion { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection tasks { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/task.cs b/Source/Samples/net45/Tracker.MySql.Entities/task.cs new file mode 100644 index 0000000..dfd000e --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/task.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class task + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public task() + { + this.audits = new HashSet(); + } + + public int Id { get; set; } + public int StatusId { get; set; } + public Nullable PriorityId { get; set; } + public int CreatedId { get; set; } + public string Summary { get; set; } + public string Details { get; set; } + public Nullable StartDate { get; set; } + public Nullable DueDate { get; set; } + public Nullable CompleteDate { get; set; } + public Nullable AssignedId { get; set; } + public System.DateTime CreatedDate { get; set; } + public System.DateTime ModifiedDate { get; set; } + public byte[] RowVersion { get; set; } + public string LastModifiedBy { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection audits { get; set; } + public virtual priority priority { get; set; } + public virtual status status { get; set; } + public virtual user user { get; set; } + public virtual user user1 { get; set; } + public virtual taskextended taskextended { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/taskextended.cs b/Source/Samples/net45/Tracker.MySql.Entities/taskextended.cs new file mode 100644 index 0000000..6129387 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/taskextended.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class taskextended + { + public int TaskId { get; set; } + public string Browser { get; set; } + public string OS { get; set; } + public System.DateTime CreatedDate { get; set; } + public System.DateTime ModifiedDate { get; set; } + public byte[] RowVersion { get; set; } + + public virtual task task { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Entities/user.cs b/Source/Samples/net45/Tracker.MySql.Entities/user.cs new file mode 100644 index 0000000..e0b6e63 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Entities/user.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.MySql.Entities +{ + using System; + using System.Collections.Generic; + + public partial class user + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public user() + { + this.audits = new HashSet(); + this.tasks = new HashSet(); + this.tasks1 = new HashSet(); + } + + public int Id { get; set; } + public string EmailAddress { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public byte[] Avatar { get; set; } + public System.DateTime CreatedDate { get; set; } + public System.DateTime ModifiedDate { get; set; } + public byte[] RowVersion { get; set; } + public string PasswordHash { get; set; } + public string PasswordSalt { get; set; } + public string Comment { get; set; } + public bool IsApproved { get; set; } + public Nullable LastLoginDate { get; set; } + public System.DateTime LastActivityDate { get; set; } + public Nullable LastPasswordChangeDate { get; set; } + public string AvatarType { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection audits { get; set; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection tasks { get; set; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual ICollection tasks1 { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Test/App.config b/Source/Samples/net45/Tracker.MySql.Test/App.config new file mode 100644 index 0000000..45e8c4d --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Test/App.config @@ -0,0 +1,35 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs b/Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs new file mode 100644 index 0000000..9a117c8 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Transactions; +using EntityFramework.Extensions; +using Xunit; +using Tracker.MySql.Entities; +using EntityFramework; +using EntityFramework.Batch; + +namespace Tracker.MySql.Test +{ + public class ExtensionTest + { + [Fact] + public void BeginTransactionObjectContext() + { + Locator.Current.Register(() => new MySqlBatchRunner()); + try + { + using (var db = new TrackerEntities()) + using (var tx = db.Database.BeginTransaction()) + { + string emailDomain = "@test.com"; + + int count = db.users + .Where(u => u.EmailAddress.EndsWith(emailDomain)) + .Update(u => new user { IsApproved = false, LastActivityDate = DateTime.Now }); + + count = db.users + .Where(u => u.EmailAddress.EndsWith(emailDomain)) + .Delete(); + + tx.Commit(); + } + } + finally + { + Locator.Current.Register(() => new SqlServerBatchRunner()); + } + } + + [Fact] + public void NoTransactionObjectContext() + { + Locator.Current.Register(() => new MySqlBatchRunner()); + try + { + using (var db = new TrackerEntities()) + { + string emailDomain = "@test.com"; + + int count = db.users + .Where(u => u.EmailAddress.EndsWith(emailDomain)) + .Update(u => new user { IsApproved = false, LastActivityDate = DateTime.Now }); + + count = db.users + .Where(u => u.EmailAddress.EndsWith(emailDomain)) + .Delete(); + + } + } + finally + { + Locator.Current.Register(() => new SqlServerBatchRunner()); + } + } + + /*** MySQL seems not to support TransactionScope ***/ + //[Fact] + //public void TransactionScopeObjectContext() + //{ + // using (var tx = new TransactionScope()) + // using (var db = new TrackerEntities()) + // { + // string emailDomain = "@test.com"; + + // int count = db.users + // .Where(u => u.EmailAddress.EndsWith(emailDomain)) + // .Update(u => new user { IsApproved = false, LastActivityDate = DateTime.Now }); + + // count = db.users + // .Where(u => u.EmailAddress.EndsWith(emailDomain)) + // .Delete(); + + // tx.Complete(); + // } + //} + + + private void _Insert(TrackerEntities db, bool isAsync = false) + { + Locator.Current.Register(() => new MySqlBatchRunner()); + try + { + db.productsummaries.Delete(); + var query = from product in db.products + join item2 in ( + from item in db.items + group item by item.ProductId into grItem + select new + { + ProductId = grItem.Key, + AvgPrice = grItem.Average(x => x.ListPrice + x.UnitCost) + } + ) on product.ProductId equals item2.ProductId into items + from item3 in items.DefaultIfEmpty() + select new ProductSummary2 + { + ProductId = product.ProductId, + Name = product.Name, + AvgPrice = item3.AvgPrice ?? 0 + }; + if (isAsync) db.productsummaries.InsertAsync(query).Wait(); + else db.productsummaries.Insert(query); + var source = query.ToArray(); + var result = db.productsummaries.ToArray(); + for (int i = 0; i < source.Length; i++) + { + source[i].AvgPrice = Math.Round(source[i].AvgPrice, 2, MidpointRounding.AwayFromZero); //In database, only two digits after decimal point + source[i].Verified = true; //Verified was not set in query. In database, its default value is true (1) + } + Assert.True(result.OrderBy(i => i.ProductId).SequenceEqual(source.OrderBy(i => i.ProductId), new ProductSummaryComparer())); + + db.item_2.Delete(); + var query2 = db.items.Where(item => item.ListPrice / item.UnitCost >= 5); + if (isAsync) db.item_2.InsertAsync(query2).Wait(); + else db.item_2.Insert(query2); + var source2 = query2.ToArray().OrderBy(i => i.ItemId); + var result2 = db.item_2.ToArray().Select(i => ItemComparer.GetItem(i)).OrderBy(i => i.ItemId); + Assert.True(result2.SequenceEqual(source2, new ItemComparer())); + + + db.item_2.Delete(); + //var query3 = from item in db.items where item.ProductId == "K9-RT-02" select item; //Using MySQL provider, ObjectQuery.Parameters is not filled if its parameter is constant + string productId = "K9-RT-02"; + var query3 = from item in db.items where item.ProductId == productId select item; + if (isAsync) db.item_2.InsertAsync(query3).Wait(); + else db.item_2.Insert(query3); + var source3 = query3.ToArray().OrderBy(item => item.ItemId); + var result3 = db.item_2.ToArray().Select(i => ItemComparer.GetItem(i)).OrderBy(item => item.ItemId); + Assert.True(result3.SequenceEqual(source3, new ItemComparer())); + } + finally + { + Locator.Current.Register(() => new SqlServerBatchRunner()); + } + } + + [Fact] + public void InsertNoTransaction() + { + using (var db = new TrackerEntities()) + { + _Insert(db); + } + } + + [Fact] + public void InsertInTransaction() + { + using (var db = new TrackerEntities()) + using (var tx = db.Database.BeginTransaction()) + { + _Insert(db); + tx.Commit(); + } + } + + /*** MySQL seems not to support TransactionScope ***/ + //[Fact] + //public void InsertInTransactionScope() + //{ + // using (var tx = new TransactionScope()) + // using (var db = new TrackerEntities()) + // { + // _Insert(db); + // tx.Complete(); + // } + //} + + [Fact] + public void InsertAsync() + { + using (var db = new TrackerEntities()) + { + _Insert(db, true); + } + } + } + + class ProductSummary2 : productsummary { } + + class ProductSummaryComparer : IEqualityComparer, System.Collections.IComparer + { + public int Compare(object x, object y) + { + if (x == null && y == null) return 0; + var x2 = x as productsummary; + var y2 = y as productsummary; + if (x2 != null && y2 != null) + { + if (x2.ProductId == y2.ProductId + && x2.Name == y2.Name + && x2.AvgPrice == y2.AvgPrice + && x2.Verified == y2.Verified) + return 0; + } + return -1; + } + + public bool Equals(productsummary x, productsummary y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(productsummary obj) + { + if (obj == null) return 0; + return obj.ProductId.GetHashCode(); + } + } + + class ItemComparer : IEqualityComparer, System.Collections.IComparer + { + public int Compare(object x, object y) + { + if (x == null && y == null) return 0; + if (x == null || y == null) return -1; + item item = GetItem(x), item2 = GetItem(y); + if (item.ItemId == item2.ItemId + && item.ProductId == item2.ProductId + && item.ListPrice == item2.ListPrice + && item.UnitCost == item2.UnitCost + && item.Supplier == item2.Supplier + && item.Status == item2.Status + && item.Attr1 == item2.Attr1 + && item.Attr2 == item2.Attr2 + && item.Attr3 == item2.Attr3 + && item.Attr4 == item2.Attr4 + && item.Attr5 == item2.Attr5) return 0; + return -1; + } + + public bool Equals(item x, item y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(item obj) + { + return obj.ItemId.GetHashCode(); + } + + public static item GetItem(object obj) + { + if (obj is item) return obj as item; + if (obj is item_2) + { + var item2 = obj as item_2; + return new item + { + ItemId = item2.ItemId, + ProductId = item2.ProductId, + ListPrice = item2.ListPrice, + UnitCost = item2.UnitCost, + Supplier = item2.Supplier, + Status = item2.Status, + Attr1 = item2.Attr1, + Attr2 = item2.Attr2, + Attr3 = item2.Attr3, + Attr4 = item2.Attr4, + Attr5 = item2.Attr5 + }; + } + return null; + } + } +} diff --git a/Source/Samples/net45/Tracker.MySql.Test/Properties/AssemblyInfo.cs b/Source/Samples/net45/Tracker.MySql.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3b3f8b6 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("Tracker.MySql.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Tracker.MySql.Test")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[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("d8f3c9ee-afd3-4291-96f2-4dbeb842e5aa")] + +// 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/Source/Samples/net45/Tracker.MySql.Test/Tracker.MySql.Test.csproj b/Source/Samples/net45/Tracker.MySql.Test/Tracker.MySql.Test.csproj new file mode 100644 index 0000000..2f7a472 --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Test/Tracker.MySql.Test.csproj @@ -0,0 +1,120 @@ + + + + Debug + AnyCPU + {D8F3C9EE-AFD3-4291-96F2-4DBEB842E5AA} + Library + Properties + Tracker.MySql.Test + Tracker.MySql.Test + v4.5.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll + True + + + ..\..\..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll + True + + + ..\..\..\packages\MySql.Data.Entity.6.9.8\lib\net45\MySql.Data.Entity.EF6.dll + True + + + + + + + ..\..\..\packages\xunit.runner.visualstudio.2.1.0\build\_common\xunit.abstractions.dll + + + ..\..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll + + + ..\..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll + + + ..\..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll + + + + + + + + + + + + + + + + + {d390c235-242c-4e92-9e0b-d2463e87b0f0} + EntityFramework.Extended.net45 + + + {e445e980-4ab0-4012-8929-118b01392393} + Tracker.MySql.Entities + + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.MySql.Test/packages.config b/Source/Samples/net45/Tracker.MySql.Test/packages.config new file mode 100644 index 0000000..a1b2edf --- /dev/null +++ b/Source/Samples/net45/Tracker.MySql.Test/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.SqlServer.CodeFirst/Tracker.SqlServer.CodeFirst.csproj b/Source/Samples/net45/Tracker.SqlServer.CodeFirst/Tracker.SqlServer.CodeFirst.csproj index db31bd7..f101e01 100644 --- a/Source/Samples/net45/Tracker.SqlServer.CodeFirst/Tracker.SqlServer.CodeFirst.csproj +++ b/Source/Samples/net45/Tracker.SqlServer.CodeFirst/Tracker.SqlServer.CodeFirst.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -15,6 +16,8 @@ ..\ true + + true @@ -54,6 +57,22 @@ + + ..\..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll + True + + + ..\..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll + True + + + ..\..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll + True + + + ..\..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll + True + @@ -120,11 +139,19 @@ - + + Designer + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + @@ -634,6 +810,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram index 0971141..9eb8e03 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram @@ -4,7 +4,7 @@ - + @@ -20,6 +20,12 @@ + + + + + + diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs b/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs index 725ec8d..71b871c 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs @@ -1,3 +1,4 @@ + //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -7,41 +8,72 @@ // //------------------------------------------------------------------------------ + namespace Tracker.SqlServer.Entities { - using System; + +using System; using System.Collections.Generic; - public partial class User +public partial class User +{ + + public User() { - public User() - { - this.Audits = new HashSet(); - this.Tasks = new HashSet(); - this.Tasks1 = new HashSet(); - this.Roles = new HashSet(); - } - - public int Id { get; set; } - public string EmailAddress { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public byte[] Avatar { get; set; } - public System.DateTime CreatedDate { get; set; } - public System.DateTime ModifiedDate { get; set; } - public byte[] RowVersion { get; set; } - public string PasswordHash { get; set; } - public string PasswordSalt { get; set; } - public string Comment { get; set; } - public bool IsApproved { get; set; } - public Nullable LastLoginDate { get; set; } - public System.DateTime LastActivityDate { get; set; } - public Nullable LastPasswordChangeDate { get; set; } - public string AvatarType { get; set; } - - public virtual ICollection Audits { get; set; } - public virtual ICollection Tasks { get; set; } - public virtual ICollection Tasks1 { get; set; } - public virtual ICollection Roles { get; set; } + + this.Audits = new HashSet(); + + this.Tasks = new HashSet(); + + this.Tasks1 = new HashSet(); + + this.Roles = new HashSet(); + } + + + public int Id { get; set; } + + public string EmailAddress { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + + public byte[] Avatar { get; set; } + + public System.DateTime CreatedDate { get; set; } + + public System.DateTime ModifiedDate { get; set; } + + public byte[] RowVersion { get; set; } + + public string PasswordHash { get; set; } + + public string PasswordSalt { get; set; } + + public string Comment { get; set; } + + public bool IsApproved { get; set; } + + public Nullable LastLoginDate { get; set; } + + public System.DateTime LastActivityDate { get; set; } + + public Nullable LastPasswordChangeDate { get; set; } + + public string AvatarType { get; set; } + + + + public virtual ICollection Audits { get; set; } + + public virtual ICollection Tasks { get; set; } + + public virtual ICollection Tasks1 { get; set; } + + public virtual ICollection Roles { get; set; } + +} + } diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/packages.config b/Source/Samples/net45/Tracker.SqlServer.Entities/packages.config index 9e8db31..2c634d7 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/packages.config +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/packages.config @@ -1,4 +1,14 @@  + + + + + + + + + + \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs index 5598da6..697dd3b 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs @@ -9,7 +9,6 @@ namespace Tracker.SqlServer.Test { - public class ExtensionTest { [Fact] @@ -70,5 +69,182 @@ public void TransactionScopeObjectContext() } } + + private void _Insert(TrackerEntities db, bool isAsync=false) + { + db.ProductSummaries.Delete(); + var query = from product in db.Products + join item2 in ( + from item in db.Items + group item by item.ProductId into grItem + select new + { + ProductId = grItem.Key, + AvgPrice = grItem.Average(x => x.ListPrice + x.UnitCost) + } + ) on product.ProductId equals item2.ProductId into items + from item3 in items.DefaultIfEmpty() + select new ProductSummary2 + { + ProductId = product.ProductId, + Name = product.Name, + AvgPrice = item3.AvgPrice ?? 0 + }; + if (isAsync) db.ProductSummaries.InsertAsync(query).Wait(); + else db.ProductSummaries.Insert(query); + var source = query.ToArray(); + var result = db.ProductSummaries.ToArray(); + for (int i = 0; i < source.Length; i++) + { + source[i].AvgPrice = Math.Round(source[i].AvgPrice, 2, MidpointRounding.AwayFromZero); //In database, only two digits after decimal point + source[i].Verified = true; //Verified was not set in query. In database, its default value is true (1) + } + Assert.True(result.OrderBy(i => i.ProductId).SequenceEqual(source.OrderBy(i => i.ProductId), new ProductSummaryComparer())); + + db.Item_2.Delete(); + var query2 = db.Items.Where(item => item.ListPrice / item.UnitCost >= 5); + if (isAsync) db.Item_2.InsertAsync(query2).Wait(); + else db.Item_2.Insert(query2); + var source2 = query2.ToArray().OrderBy(i => i.ItemId); + var result2 = db.Item_2.ToArray().Select(i => ItemComparer.GetItem(i)).OrderBy(i => i.ItemId); + Assert.True(result2.SequenceEqual(source2, new ItemComparer())); + + + db.Item_2.Delete(); + var query3 = from item in db.Items where item.ProductId == "K9-RT-02" select item; + if (isAsync) db.Item_2.InsertAsync(query3).Wait(); + else db.Item_2.Insert(query3); + var source3 = query3.ToArray().OrderBy(item => item.ItemId); + var result3 = db.Item_2.ToArray().Select(i => ItemComparer.GetItem(i)).OrderBy(item => item.ItemId); + Assert.True(result3.SequenceEqual(source3, new ItemComparer())); + } + + [Fact] + public void InsertNoTransaction() + { + using (var db = new TrackerEntities()) + { + _Insert(db); + } + } + + [Fact] + public void InsertInTransaction() + { + using (var db = new TrackerEntities()) + using (var tx = db.Database.BeginTransaction()) + { + _Insert(db); + tx.Commit(); + } + } + + [Fact] + public void InsertInTransactionScope() + { + using (var tx = new TransactionScope()) + using (var db = new TrackerEntities()) + { + _Insert(db); + tx.Complete(); + } + } + + [Fact] + public void InsertAsync() + { + using (var db = new TrackerEntities()) + { + _Insert(db, true); + } + } + } + + class ProductSummary2 : ProductSummary { } + + class ProductSummaryComparer : IEqualityComparer, System.Collections.IComparer + { + public int Compare(object x, object y) + { + if (x == null && y == null) return 0; + var x2 = x as ProductSummary; + var y2 = y as ProductSummary; + if (x2 != null && y2 != null) + { + if (x2.ProductId == y2.ProductId + && x2.Name == y2.Name + && x2.AvgPrice == y2.AvgPrice + && x2.Verified == y2.Verified) + return 0; + } + return -1; + } + + public bool Equals(ProductSummary x, ProductSummary y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(ProductSummary obj) + { + if (obj == null) return 0; + return obj.ProductId.GetHashCode(); + } + } + + class ItemComparer : IEqualityComparer, System.Collections.IComparer + { + public int Compare(object x, object y) + { + if (x == null && y == null) return 0; + if (x == null || y == null) return -1; + Item item = GetItem(x), item2 = GetItem(y); + if (item.ItemId == item2.ItemId + && item.ProductId == item2.ProductId + && item.ListPrice == item2.ListPrice + && item.UnitCost == item2.UnitCost + && item.Supplier == item2.Supplier + && item.Status == item2.Status + && item.Attr1 == item2.Attr1 + && item.Attr2 == item2.Attr2 + && item.Attr3 == item2.Attr3 + && item.Attr4 == item2.Attr4 + && item.Attr5 == item2.Attr5) return 0; + return -1; + } + + public bool Equals(Item x, Item y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(Item obj) + { + return obj.ItemId.GetHashCode(); + } + + public static Item GetItem(object obj) + { + if (obj is Item) return obj as Item; + if (obj is Item_2) + { + var item2 = obj as Item_2; + return new Item + { + ItemId = item2.ItemId, + ProductId = item2.ProductId, + ListPrice = item2.ListPrice, + UnitCost = item2.UnitCost, + Supplier = item2.Supplier, + Status = item2.Status, + Attr1 = item2.Attr1, + Attr2 = item2.Attr2, + Attr3 = item2.Attr3, + Attr4 = item2.Attr4, + Attr5 = item2.Attr5 + }; + } + return null; + } } } diff --git a/Source/Samples/net45/Tracker.SqlServer.Test/Tracker.SqlServer.Test.csproj b/Source/Samples/net45/Tracker.SqlServer.Test/Tracker.SqlServer.Test.csproj index a0bfffc..3f9f6b0 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Test/Tracker.SqlServer.Test.csproj +++ b/Source/Samples/net45/Tracker.SqlServer.Test/Tracker.SqlServer.Test.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -15,6 +16,8 @@ ..\ true + + true @@ -72,8 +75,21 @@ - - ..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll + + False + ..\..\..\packages\xunit.runner.visualstudio.2.1.0\build\_common\xunit.abstractions.dll + + + ..\..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll + True + + + ..\..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll + True + + + ..\..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll + True @@ -96,7 +112,7 @@ {D390C235-242C-4E92-9E0B-D2463E87B0F0} - EntityFramework.Extended + EntityFramework.Extended.net45 {FA68FB7C-D87E-497B-A300-F2A7827FE92C} @@ -121,6 +137,12 @@ + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + @@ -837,16 +877,6 @@ - - - - - - - - - - @@ -864,6 +894,17 @@ + + + + + + + + + + + diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram index 9eb8e03..116bafd 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram @@ -4,7 +4,7 @@ - + @@ -22,10 +22,11 @@ - + + diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs b/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs index 71b871c..725ec8d 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/User.cs @@ -1,4 +1,3 @@ - //------------------------------------------------------------------------------ // // This code was generated from a template. @@ -8,72 +7,41 @@ // //------------------------------------------------------------------------------ - namespace Tracker.SqlServer.Entities { - -using System; + using System; using System.Collections.Generic; -public partial class User -{ - - public User() + public partial class User { - - this.Audits = new HashSet(); - - this.Tasks = new HashSet(); - - this.Tasks1 = new HashSet(); - - this.Roles = new HashSet(); - + public User() + { + this.Audits = new HashSet(); + this.Tasks = new HashSet(); + this.Tasks1 = new HashSet(); + this.Roles = new HashSet(); + } + + public int Id { get; set; } + public string EmailAddress { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public byte[] Avatar { get; set; } + public System.DateTime CreatedDate { get; set; } + public System.DateTime ModifiedDate { get; set; } + public byte[] RowVersion { get; set; } + public string PasswordHash { get; set; } + public string PasswordSalt { get; set; } + public string Comment { get; set; } + public bool IsApproved { get; set; } + public Nullable LastLoginDate { get; set; } + public System.DateTime LastActivityDate { get; set; } + public Nullable LastPasswordChangeDate { get; set; } + public string AvatarType { get; set; } + + public virtual ICollection Audits { get; set; } + public virtual ICollection Tasks { get; set; } + public virtual ICollection Tasks1 { get; set; } + public virtual ICollection Roles { get; set; } } - - - public int Id { get; set; } - - public string EmailAddress { get; set; } - - public string FirstName { get; set; } - - public string LastName { get; set; } - - public byte[] Avatar { get; set; } - - public System.DateTime CreatedDate { get; set; } - - public System.DateTime ModifiedDate { get; set; } - - public byte[] RowVersion { get; set; } - - public string PasswordHash { get; set; } - - public string PasswordSalt { get; set; } - - public string Comment { get; set; } - - public bool IsApproved { get; set; } - - public Nullable LastLoginDate { get; set; } - - public System.DateTime LastActivityDate { get; set; } - - public Nullable LastPasswordChangeDate { get; set; } - - public string AvatarType { get; set; } - - - - public virtual ICollection Audits { get; set; } - - public virtual ICollection Tasks { get; set; } - - public virtual ICollection Tasks1 { get; set; } - - public virtual ICollection Roles { get; set; } - -} - } diff --git a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs index 697dd3b..893db6f 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs @@ -6,6 +6,7 @@ using EntityFramework.Extensions; using Xunit; using Tracker.SqlServer.Entities; +using System.Threading.Tasks; namespace Tracker.SqlServer.Test { @@ -158,6 +159,84 @@ public void InsertAsync() _Insert(db, true); } } + + private void _InsertBulk(TrackerEntities db, bool isAsync = false) + { + db.ProductSummaries.Delete(); + + //Create 1000 random records + var entities = new List(); + Random r = new Random(); + int n = 1000; + int m = (int)Math.Pow(10, 8) - 1; + for (int i = 1; i <= n; i++) + { + entities.Add(new ProductSummary + { + ProductId = i.ToString(), + Name = Guid.NewGuid().ToString().Replace("-", "").PadRight(80).Substring(0, 80), + AvgPrice = r.Next(-m, m), + Verified = r.Next() % 2 == 0, + Date = DateTime.Now.AddDays(r.Next(-100, 100)), + }); + } + + var start = DateTime.Now; + + if (isAsync) db.ProductSummaries.InsertAsync(entities).Wait(); + else db.ProductSummaries.Insert(entities); + + /** + * Compare the execution time with these two lines of commented code below + // * **/ + //db.ProductSummaries.AddRange(entities); + //db.SaveChanges(); + + System.Diagnostics.Debug.WriteLine("***** Executing bulk insert (" + n + " items) takes " + + DateTime.Now.Subtract(start).ToString(@"hh\:mm\:ss\.ffffff")); + + Assert.True(db.ProductSummaries.Count() == n); + } + + [Fact] + public void InsertBulkNoTransaction() + { + using (var db = new TrackerEntities()) + { + _InsertBulk(db); + } + } + + [Fact] + public void InsertBulkInTransaction() + { + using (var db = new TrackerEntities()) + using (var tx = db.Database.BeginTransaction()) + { + _InsertBulk(db); + tx.Commit(); + } + } + + [Fact] + public void InsertBulkInTransactionScope() + { + using (var tx = new TransactionScope()) + using (var db = new TrackerEntities()) + { + _InsertBulk(db); + tx.Complete(); + } + } + + [Fact] + public void InsertBulkAsync() + { + using (var db = new TrackerEntities()) + { + _InsertBulk(db, true); + } + } } class ProductSummary2 : ProductSummary { } From 84c24648e32125be2749b8301a541d936029e12e Mon Sep 17 00:00:00 2001 From: AT Mulyana Date: Thu, 2 Jun 2016 22:06:37 +0700 Subject: [PATCH 3/8] - --- Database/SqlServer/Tracker.sql | Bin 68004 -> 68644 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Database/SqlServer/Tracker.sql b/Database/SqlServer/Tracker.sql index 978d49fae9d2d45fbfa436521efa90de88ae152a..1c26377febb1a11f70ae29ab0c43d2551953d86c 100644 GIT binary patch delta 199 zcmZ27nPtfgmJRn-Oitirnf#7fWwL-Y&*pn8elSg*R-rc8VH@9Mh1Einrn^iQS}nsE zKY8O);mKdhq$c-l$_w^q@MBP52xSOhaA9y{2w`vq@|=OJU@++db;i51!~ODCta2~6JAAqTQqFdk@25ko#h3PUMFGD8VN?B=^mJsBr|ILonF IY;9i*09y|@bpQYW delta 44 zcmV+{0Mq}Zm;|Jg1hC$ylb#_Ula#C+li(QylZXpCvy7>J0<-R=I02Jz&L C0~DtK From 75b3e604c060006490ad9d155b7c76463829d519 Mon Sep 17 00:00:00 2001 From: AT Mulyana Date: Mon, 13 Jun 2016 20:55:19 +0700 Subject: [PATCH 4/8] Can process auto increment column in Bulk Insert --- Database/SqlServer/Tracker.sql | Bin 68644 -> 69608 bytes .../Batch/EntityDataReader.cs | 8 +++-- .../Mapping/MetadataMappingProvider.cs | 1 + .../Mapping/PropertyMap.cs | 4 +++ .../Tracker.SqlServer.Entities/ATable.cs | 23 ++++++++++++ .../Tracker.Context.cs | 1 + .../Tracker.SqlServer.Entities.csproj | 3 ++ .../Tracker.SqlServer.Entities/Tracker.edmx | 33 ++++++++++++++++++ .../Tracker.edmx.diagram | 1 + .../Tracker.SqlServer.Test/ExtensionTest.cs | 21 ++++++++++- 10 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 Source/Samples/net45/Tracker.SqlServer.Entities/ATable.cs diff --git a/Database/SqlServer/Tracker.sql b/Database/SqlServer/Tracker.sql index 1c26377febb1a11f70ae29ab0c43d2551953d86c..636cab38698cf5459b324d0ed08477cb00778591 100644 GIT binary patch delta 169 zcmZ27gXP71mJMx;lbaZwxI-8c8Il-s7*ZKxCtu{LpPa_5rHUlRpuxb)z{SAH5Y6Do z5X+zdq|<<^^BEE+Z&Eli;ZclB33h;0Cwg1_1~OGQ$sv diff --git a/Source/EntityFramework.Extended/Batch/EntityDataReader.cs b/Source/EntityFramework.Extended/Batch/EntityDataReader.cs index 5e93769..569e300 100644 --- a/Source/EntityFramework.Extended/Batch/EntityDataReader.cs +++ b/Source/EntityFramework.Extended/Batch/EntityDataReader.cs @@ -34,8 +34,10 @@ public EntityDataReader(IEnumerable entities, EntityMap entityMap) _propInfos = new PropertyInfo[FieldCount]; for (int idx = _entityMap.PropertyMaps.Count-1, i = 0; idx >= 0; idx--, i++) { - _columnNameIndex[_entityMap.PropertyMaps[idx].ColumnName] = i; - _propInfos[i] = tEntity.GetProperty(_entityMap.PropertyMaps[idx].PropertyName); + var propMap = _entityMap.PropertyMaps[idx]; + _columnNameIndex[propMap.ColumnName] = i; + if (propMap.AutoGeneratedColumn) continue; + _propInfos[i] = tEntity.GetProperty(propMap.PropertyName); } } @@ -231,7 +233,7 @@ public bool Read() { bool isNext = _entityEnumerator.MoveNext(); _current = isNext ? _entityEnumerator.Current : null; - if (_current != null) for (int i=0; i<_propInfos.Length; i++) _values[i] = _propInfos[i].GetValue(_current); + if (_current != null) for (int i=0; i<_propInfos.Length; i++) if (_propInfos[i] != null) _values[i] = _propInfos[i].GetValue(_current); return isNext; } } diff --git a/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs b/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs index 5678557..4d8b5b7 100644 --- a/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs +++ b/Source/EntityFramework.Extended/Mapping/MetadataMappingProvider.cs @@ -176,6 +176,7 @@ private static void SetProperties(EntityMap entityMap, EntitySetMapping mapping, if (scalarPropertyMapping != null) { map.ColumnName = scalarPropertyMapping.Column.Name; + map.AutoGeneratedColumn = scalarPropertyMapping.Column.StoreGeneratedPattern != StoreGeneratedPattern.None; continue; } diff --git a/Source/EntityFramework.Extended/Mapping/PropertyMap.cs b/Source/EntityFramework.Extended/Mapping/PropertyMap.cs index abe0d78..d34d8c2 100644 --- a/Source/EntityFramework.Extended/Mapping/PropertyMap.cs +++ b/Source/EntityFramework.Extended/Mapping/PropertyMap.cs @@ -16,5 +16,9 @@ public class PropertyMap /// Gets or sets the name of the column. /// public string ColumnName { get; set; } + /// + /// Is it auto generated column, such as SQL Server Identity column? + /// + public bool AutoGeneratedColumn { get; set; } } } \ No newline at end of file diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/ATable.cs b/Source/Samples/net45/Tracker.SqlServer.Entities/ATable.cs new file mode 100644 index 0000000..94ef773 --- /dev/null +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/ATable.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tracker.SqlServer.Entities +{ + using System; + using System.Collections.Generic; + + public partial class ATable + { + public double A { get; set; } + public string B { get; set; } + public int C { get; set; } + public System.DateTime D { get; set; } + public Nullable E { get; set; } + } +} diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.Context.cs b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.Context.cs index 219a316..0f59132 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.Context.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.Context.cs @@ -36,5 +36,6 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) public virtual DbSet Products { get; set; } public virtual DbSet Item_2 { get; set; } public virtual DbSet ProductSummaries { get; set; } + public virtual DbSet ATables { get; set; } } } diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.csproj b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.csproj index 2ddfe72..3c9c91d 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.csproj +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.SqlServer.Entities.csproj @@ -83,6 +83,9 @@ + + Tracker.tt + Tracker.tt diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx index 41d6a70..879d6ff 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx @@ -5,6 +5,16 @@ + + + + + + + + + + @@ -310,6 +320,7 @@ + @@ -634,6 +645,7 @@ + @@ -730,6 +742,16 @@ + + + + + + + + + + @@ -905,6 +927,17 @@ + + + + + + + + + + + diff --git a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram index 116bafd..f0f43d0 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram +++ b/Source/Samples/net45/Tracker.SqlServer.Entities/Tracker.edmx.diagram @@ -27,6 +27,7 @@ + diff --git a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs index 893db6f..35e1a1e 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs @@ -163,11 +163,12 @@ public void InsertAsync() private void _InsertBulk(TrackerEntities db, bool isAsync = false) { db.ProductSummaries.Delete(); + db.Database.ExecuteSqlCommand("TRUNCATE TABLE ATable"); //Create 1000 random records - var entities = new List(); Random r = new Random(); int n = 1000; + var entities = new List(); int m = (int)Math.Pow(10, 8) - 1; for (int i = 1; i <= n; i++) { @@ -196,6 +197,24 @@ private void _InsertBulk(TrackerEntities db, bool isAsync = false) + DateTime.Now.Subtract(start).ToString(@"hh\:mm\:ss\.ffffff")); Assert.True(db.ProductSummaries.Count() == n); + + /** Dealing with Identity Column **/ + var entities2 = new List(); + for (int i = 1; i <= n; i++) + { + entities2.Add(new ATable + { + A = r.NextDouble(), + B = Guid.NewGuid().ToString().Replace("-", "").PadRight(50).Substring(0, 50), + //C is an Identity column + D = DateTime.Now.AddDays(r.Next(-100, 100)), + E = r.Next() % 2 == 0, + }); + } + + if (isAsync) db.ATables.InsertAsync(entities2).Wait(); + else db.ATables.Insert(entities2); + Assert.True(db.ATables.Count() == n); } [Fact] From 3c4b02c1142b96320e87d620e1b4228407e68fad Mon Sep 17 00:00:00 2001 From: AT Mulyana Date: Thu, 12 Jul 2018 05:39:27 +0700 Subject: [PATCH 5/8] - --- License.txt | 1 + .../Batch/EntityDataReader.cs | 2 +- .../Batch/MySqlBatchRunner.cs | 2 +- .../EntityFramework.Extended.net40.csproj | 1 + .../EntityFramework.Extended.nuspec | 16 ++++++++-------- Source/GlobalAssemblyInfo.net40.cs | 12 ++++++------ Source/GlobalAssemblyInfo.net45.cs | 12 ++++++------ 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/License.txt b/License.txt index cf68ccd..f83fbed 100644 --- a/License.txt +++ b/License.txt @@ -1,4 +1,5 @@ Copyright (c) 2012, LoreSoft +Modified by AT Mulyana in 2016 All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Source/EntityFramework.Extended/Batch/EntityDataReader.cs b/Source/EntityFramework.Extended/Batch/EntityDataReader.cs index 569e300..f64545e 100644 --- a/Source/EntityFramework.Extended/Batch/EntityDataReader.cs +++ b/Source/EntityFramework.Extended/Batch/EntityDataReader.cs @@ -233,7 +233,7 @@ public bool Read() { bool isNext = _entityEnumerator.MoveNext(); _current = isNext ? _entityEnumerator.Current : null; - if (_current != null) for (int i=0; i<_propInfos.Length; i++) if (_propInfos[i] != null) _values[i] = _propInfos[i].GetValue(_current); + if (_current != null) for (int i=0; i<_propInfos.Length; i++) if (_propInfos[i] != null) _values[i] = _propInfos[i].GetValue(_current, null); return isNext; } } diff --git a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs index 29b1509..7e31ebd 100644 --- a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs @@ -370,6 +370,7 @@ public Task InsertAsync(IQueryable query, ObjectQuery /// Inserts a lof of rows into a database table. It must be much faster than executing `DbSet.AddRange` or @@ -390,7 +391,6 @@ public int Insert(ObjectContext objectContext, IEnumerable ent { throw new NotImplementedException(); } -#endif #if NET45 /// diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj b/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj index 900aa76..50da679 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.net40.csproj @@ -77,6 +77,7 @@ + diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec index 4fa97e0..b50d1a9 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec @@ -1,20 +1,20 @@ - EntityFramework.Extended - 6.1.0.0 - LoreSoft - LoreSoft - https://github.com/loresoft/EntityFramework.Extended - https://github.com/loresoft/EntityFramework.Extended + EntityFrameworkExtended + 6.2.0.0 + LoreSoft, AT Mulyana + LoreSoft, AT Mulyana + https://github.com/atmulyana/EntityFramework.Extended + https://github.com/atmulyana/EntityFramework.Extended false A library that extends the functionality of Entity Framework by adding batch update, future queries and audit logs. Entity Framework extensions library. - Copyright (c) 2014, LoreSoft + Copyright (c) 2014, LoreSoft, Modified by AT Mulyana in 2016 en-US EntityFramework - + diff --git a/Source/GlobalAssemblyInfo.net40.cs b/Source/GlobalAssemblyInfo.net40.cs index d5ec530..a004e10 100644 --- a/Source/GlobalAssemblyInfo.net40.cs +++ b/Source/GlobalAssemblyInfo.net40.cs @@ -11,10 +11,10 @@ [assembly: System.Reflection.AssemblyProduct("EntityFramework.Extended (.NET 4.0)")] [assembly: System.Reflection.AssemblyDescription("Entity Framework extensions library. Built for .NET 4.0")] [assembly: System.Reflection.AssemblyCompany("LoreSoft")] -[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft")] +[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.1.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.1.0.0")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.0")] @@ -26,13 +26,13 @@ internal sealed partial class ThisAssembly { internal const string AssemblyCompany = "LoreSoft"; - internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft"; + internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016"; internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.1.0.0"; + internal const string AssemblyFileVersion = "6.2.0.0"; - internal const string AssemblyInformationalVersion = "6.1.0.0"; + internal const string AssemblyInformationalVersion = "6.2.0.0"; private ThisAssembly() { } diff --git a/Source/GlobalAssemblyInfo.net45.cs b/Source/GlobalAssemblyInfo.net45.cs index 91700d4..2916f7a 100644 --- a/Source/GlobalAssemblyInfo.net45.cs +++ b/Source/GlobalAssemblyInfo.net45.cs @@ -11,10 +11,10 @@ [assembly: System.Reflection.AssemblyProduct("EntityFramework.Extended (.NET 4.5)")] [assembly: System.Reflection.AssemblyDescription("Entity Framework extensions library. Built for .NET 4.5")] [assembly: System.Reflection.AssemblyCompany("LoreSoft")] -[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft")] +[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.1.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.1.0.0")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.0")] @@ -26,13 +26,13 @@ internal sealed partial class ThisAssembly { internal const string AssemblyCompany = "LoreSoft"; - internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft"; + internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016"; internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.1.0.0"; + internal const string AssemblyFileVersion = "6.2.0.0"; - internal const string AssemblyInformationalVersion = "6.1.0.0"; + internal const string AssemblyInformationalVersion = "6.2.0.0"; private ThisAssembly() { } From 0271e44adca83363bc907b2071853cd902b3cebf Mon Sep 17 00:00:00 2001 From: AT Mulyana Date: Sun, 29 Jul 2018 22:36:48 +0700 Subject: [PATCH 6/8] fix bug: fields not ordered properly in statement 'INSERT INTO ... SELECT ...' --- .../Batch/IBatchRunner.cs | 5 + .../Batch/MySqlBatchRunner.cs | 7 +- .../Batch/QueryHelper.cs | 158 ++++++++++++++++-- .../Batch/SqlServerBatchRunner.cs | 7 +- .../EntityFramework.Extended.net45.csproj | 2 +- .../EntityFramework.Extended.nuspec | 2 +- Source/GlobalAssemblyInfo.net40.cs | 8 +- Source/GlobalAssemblyInfo.net45.cs | 8 +- 8 files changed, 175 insertions(+), 22 deletions(-) diff --git a/Source/EntityFramework.Extended/Batch/IBatchRunner.cs b/Source/EntityFramework.Extended/Batch/IBatchRunner.cs index 57acc0a..6c6e178 100644 --- a/Source/EntityFramework.Extended/Batch/IBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/IBatchRunner.cs @@ -27,6 +27,11 @@ public interface IBatchRunner /// string Quote(string identifier); + /// + /// The character to escape quote (') character in string + /// + char CharToEscapeQuote { get; } + /// /// Create and runs a batch delete statement. /// diff --git a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs index 7e31ebd..17b5e4c 100644 --- a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs @@ -39,6 +39,11 @@ public string Quote(string identifier) return "`" + identifier.Replace("`", "``") + "`"; } + /// + /// The character to escape quote (') character in string + /// + public char CharToEscapeQuote { get { return '\\'; } } + /// /// Create and run a batch delete statement. /// @@ -297,7 +302,7 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit string value = match.Groups["ColumnValue"].Value; string alias = match.Groups["TableAlias"].Value; - value = value.Replace(alias + ".", ""); + value = value.Replace(alias + ".", "j0."); foreach (ObjectParameter objectParameter in selectQuery.Parameters) { diff --git a/Source/EntityFramework.Extended/Batch/QueryHelper.cs b/Source/EntityFramework.Extended/Batch/QueryHelper.cs index 8c3f827..563964f 100644 --- a/Source/EntityFramework.Extended/Batch/QueryHelper.cs +++ b/Source/EntityFramework.Extended/Batch/QueryHelper.cs @@ -109,7 +109,7 @@ public static void CopyTo(this ObjectParameterCollection parameters, DbCommand c } public static string GetSelectSql(ObjectQuery query, IEnumerable selectedProperties, DbCommand command = null, - IBatchRunner runner = null) + IBatchRunner runner = null, IList fields = null) { if (selectedProperties == null || selectedProperties.Count() < 1) throw new ArgumentException("The selected properties must be defined.", "selectedProperties"); @@ -123,6 +123,22 @@ public static string GetSelectSql(ObjectQuery query, IEnumerable string selectSql = objectQuery.ToTraceString(); if (command != null) objectQuery.Parameters.CopyTo(command, runner: runner); + + if (fields != null) + { + fields.Clear(); + var maps = new NonPublicMember(objectQuery).GetProperty("QueryState").GetField("_cachedPlan").GetField("CommandDefinition") + .GetField("_columnMapGenerators").Idx(0).GetField("_columnMap").GetProperty("Element").GetProperty("Properties").Value as Array; + foreach (var m in maps) + { + var mm = new NonPublicMember(m); + int pos = (int)mm.GetProperty("ColumnPos").Value; + string name = (string)mm.GetProperty("Name").Value; + while (pos+1 > fields.Count) fields.Add(null); + fields[pos] = name; + } + } + return selectSql; } @@ -140,6 +156,7 @@ public static IEnumerable GetSelectedProperties(Expression queryExpressi var methodExp = queryExpression as MethodCallExpression; if (methodExp == null) return null; if (methodExp.Method.Name != "Select" && methodExp.Method.Name != "SelectMany" //From Select method we know the selected properties + && methodExp.Method.Name != "Join" && methodExp.Method.Name != "GroupBy" //It can also from Join or GroupBy method && methodExp.Method.Name != "MergeAs" /*MergeAs, if ends up at a dbContext.DbSet, without Select method */ ) return GetSelectedProperties(methodExp.Arguments[0], entityMap); @@ -185,19 +202,38 @@ internal static int InternalInsert(this IBatchRunner runner, IQueryable< if (selectedProperties == null) throw new ArgumentException("Cannot read the selected fields in the query", "sourceQuery"); - var selectSql = GetSelectSql(objectQuery, selectedProperties, db.Command, runner); - string quotedC1 = runner.Quote("C1").Insert(0, @"\").Insert(4, @"\"); //quote the string "C1" and escape the quote for regex. - selectSql = new Regex(@"^SELECT\s+1\s+AS\s+" + quotedC1 + @"\s*,", RegexOptions.IgnoreCase).Replace(selectSql, "SELECT "); - - var sqlBuilder = new StringBuilder(selectSql.Length * 2) - .Append("INSERT INTO ").Append(entityMap.TableName).Append(" (") + var insertFields = new List(); + var selectSql = GetSelectSql(objectQuery, selectedProperties, db.Command, runner, insertFields); + bool isThereUnusedField = false; + foreach (var f in insertFields) if (f == null) { isThereUnusedField = true; break; } + var selectFields = isThereUnusedField ? new SelectedFields(selectSql, runner.CharToEscapeQuote) : null; + var sqlBuilder = new StringBuilder(selectSql.Length * 2); + sqlBuilder.Append("INSERT INTO ").Append(entityMap.TableName).Append(" (") .Append(string.Join(", ", - from propName in selectedProperties + from propName in insertFields join map in entityMap.PropertyMaps on propName equals map.PropertyName + where propName != null select runner.Quote(map.ColumnName) )) - .Append(") ") - .Append(selectSql); + .Append(")") + .Append(Environment.NewLine); + if (isThereUnusedField) + { + sqlBuilder.Append("SELECT"); + string separator = " "; + for (int i = 0; i < insertFields.Count; i++) + { + if (insertFields[i] != null) + { + sqlBuilder.Append(separator).Append(selectFields[i]); + separator = ", "; + } + } + sqlBuilder.Append(Environment.NewLine); + sqlBuilder.Append(selectSql.Substring(selectFields.FromIndex)); + } + else + sqlBuilder.Append(selectSql); db.Command.CommandText = sqlBuilder.ToString(); @@ -215,5 +251,107 @@ select runner.Quote(map.ColumnName) return result; } } + + public class SelectedFields : List + { + public SelectedFields(string sql, char escapingQuoteChar) + { + if (!sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) return; + bool isEnd = false, isInStr = false; + var field = new List(); + int idx = "SELECT".Length; + while (idx < sql.Length) + { + if (field.Count < 1 /*The beginning of a field section*/) while (char.IsWhiteSpace(sql[idx])) idx++; + char ch = sql[idx]; + if (isInStr) + { + field.Add(ch); + int idx2 = idx + 1; + if (ch == escapingQuoteChar && idx2 < sql.Length && sql[idx2] == '\'') + { + field.Add(sql[++idx]); //Don't consider this quote character as the end of string + } + else if (ch == '\'') //The end of string + { + isInStr = false; + } + } + else + { + if (ch == ',') + { + this.Add(new string(field.ToArray()).TrimEnd()); + field.Clear(); + } + else + { + field.Add(ch); + if (ch == '\'') //The beginning of string. In the string, "FROM" and ',' will not be interpreted as a boundary + isInStr = true; + else if (field.Count >= 6 && char.IsWhiteSpace(field[field.Count-6]) + && "FROM".Equals(new string(field.GetRange(field.Count-5, 4).ToArray()), StringComparison.OrdinalIgnoreCase) + && char.IsWhiteSpace(field.Last())) //The beginning of FROM clause (the end of SELECT clause) + { + isEnd = true; + FromIndex = idx - 4; + this.Add(new string(field.GetRange(0, field.Count - 6).ToArray()).TrimEnd()); + break; + } + } + } + idx++; + } + if (!isEnd) this.Clear(); + } + + public int FromIndex { get; private set; } + } + } + + + public class NonPublicMember + { + private object _obj; + public NonPublicMember(object obj) + { + if (obj == null) throw new ArgumentNullException("obj"); + _obj = obj; + } + + public NonPublicMember GetProperty(string name) + { + var p = GetProperty(_obj, name); + if (p == null) throw new Exception("No property named " + name + " in type " + _obj.GetType().FullName); + object value = p.GetValue(_obj, null); + return new NonPublicMember(value); + } + + public static PropertyInfo GetProperty(object obj, string name) + { + return obj.GetType().GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance); + } + + public NonPublicMember GetField(string name) + { + var f = GetField(_obj, name); + if (f == null) throw new Exception("No field named " + name + " in type " + _obj.GetType().FullName); + object value = f.GetValue(_obj); + return new NonPublicMember(value); + } + + public static FieldInfo GetField(object obj, string name) + { + return obj.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Instance); + } + + public NonPublicMember Idx(int idx) + { + var objs = _obj as Array; + if (objs == null) throw new Exception(_obj?.GetType().FullName + " not an array"); + return new NonPublicMember(objs.GetValue(idx)); + } + + public object Value { get { return _obj; } } } } diff --git a/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs b/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs index 50df813..2f7fc19 100644 --- a/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs @@ -38,6 +38,11 @@ public string Quote(string identifier) return "[" + identifier.Replace("]", "]]") + "]"; } + /// + /// The character to escape quote (') character in string + /// + public char CharToEscapeQuote { get { return '\''; } } + /// /// Create and run a batch delete statement. /// @@ -279,7 +284,7 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit string value = match.Groups["ColumnValue"].Value; string alias = match.Groups["TableAlias"].Value; - value = value.Replace(alias + ".", ""); + value = value.Replace(alias + ".", "j0."); foreach (ObjectParameter objectParameter in selectQuery.Parameters) { diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj b/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj index 0da2c41..aee8ce8 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.net45.csproj @@ -1,7 +1,7 @@  - Debug + Release AnyCPU 8.0.30703 2.0 diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec index b50d1a9..c42e7bb 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec @@ -2,7 +2,7 @@ EntityFrameworkExtended - 6.2.0.0 + 6.2.0.2 LoreSoft, AT Mulyana LoreSoft, AT Mulyana https://github.com/atmulyana/EntityFramework.Extended diff --git a/Source/GlobalAssemblyInfo.net40.cs b/Source/GlobalAssemblyInfo.net40.cs index a004e10..71bba45 100644 --- a/Source/GlobalAssemblyInfo.net40.cs +++ b/Source/GlobalAssemblyInfo.net40.cs @@ -13,8 +13,8 @@ [assembly: System.Reflection.AssemblyCompany("LoreSoft")] [assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.2.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.0")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.2")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.2")] @@ -30,9 +30,9 @@ internal sealed partial class ThisAssembly { internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.2.0.0"; + internal const string AssemblyFileVersion = "6.2.0.2"; - internal const string AssemblyInformationalVersion = "6.2.0.0"; + internal const string AssemblyInformationalVersion = "6.2.0.2"; private ThisAssembly() { } diff --git a/Source/GlobalAssemblyInfo.net45.cs b/Source/GlobalAssemblyInfo.net45.cs index 2916f7a..394aa1a 100644 --- a/Source/GlobalAssemblyInfo.net45.cs +++ b/Source/GlobalAssemblyInfo.net45.cs @@ -13,8 +13,8 @@ [assembly: System.Reflection.AssemblyCompany("LoreSoft")] [assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.2.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.0")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.2")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.2")] @@ -30,9 +30,9 @@ internal sealed partial class ThisAssembly { internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.2.0.0"; + internal const string AssemblyFileVersion = "6.2.0.2"; - internal const string AssemblyInformationalVersion = "6.2.0.0"; + internal const string AssemblyInformationalVersion = "6.2.0.2"; private ThisAssembly() { } From 5ae6f292be2cb0d7151fad566a5d44a7bf86dd5d Mon Sep 17 00:00:00 2001 From: AT Mulyana Date: Sat, 17 Nov 2018 15:43:26 +0700 Subject: [PATCH 7/8] -SQL generated for non-tracking insert, update and delete can be logged -Update involving join table (SQL server only) --- Database/SqlServer/Tracker.sql | Bin 69608 -> 69656 bytes License.txt | 2 +- .../Batch/IBatchRunner.cs | 28 +++ .../Batch/MySqlBatchRunner.cs | 13 +- .../Batch/QueryHelper.cs | 175 ++++++++++++++++-- .../Batch/SqlServerBatchRunner.cs | 42 +++++ .../EntityFramework.Extended.nuspec | 4 +- .../Extensions/BatchExtensions.cs | 76 +++++++- Source/GlobalAssemblyInfo.net40.cs | 10 +- Source/GlobalAssemblyInfo.net45.cs | 10 +- .../Tracker.SqlServer.Test/ExtensionTest.cs | 1 + .../Tracker.Designer.cs | 2 +- .../net45/Tracker.MySql.Entities/app.config | 4 +- .../net45/Tracker.MySql.Test/ExtensionTest.cs | 4 +- .../Tracker.SqlServer.CodeFirst/App.config | 2 +- .../Tracker.SqlServer.Test/BatchDbContext.cs | 14 +- .../Tracker.SqlServer.Test/ExtensionTest.cs | 6 +- 17 files changed, 348 insertions(+), 45 deletions(-) diff --git a/Database/SqlServer/Tracker.sql b/Database/SqlServer/Tracker.sql index 636cab38698cf5459b324d0ed08477cb00778591..ac6ff9c3c10a12ead5ac1884026dd473d47675c1 100644 GIT binary patch delta 75 zcmV-R0JQ(;p9Gko1h8NMlaQzgqhJA?v*Mk)4wIk`6q9f+60@ePJ^}+R04 UpdateAsync(ObjectContext objectContext, EntityMap entityMap, where TEntity : class; #endif + /// + /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. + /// + /// The type item. + /// The query to create SELECT clause statement. + /// The query to create SELECT clause statement and it can also be used to get the information of db connection via + /// objectQuery.Context property. + /// The for entity type of the destination table (IDbSet). + /// + /// The number of rows inserted. + /// + int Update(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TEntity : class; + +#if NET45 + /// + /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. + /// + /// The type item. + /// The query to create SELECT clause statement. + /// The query to create SELECT clause statement and it can also be used to get the information of db connection via + /// objectQuery.Context property. + /// The for entity type of the destination table (IDbSet). + /// + /// The number of rows inserted. + /// + Task UpdateAsync(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TEntity : class; +#endif + /// /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. /// diff --git a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs index 17b5e4c..2adf71a 100644 --- a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs @@ -115,6 +115,7 @@ private int InternalDelete(ObjectContext objectContext, EntityMap entit sqlBuilder.Append(")"); db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); #if NET45 int result = async @@ -320,8 +321,8 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit wroteSet = true; } - db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); #if NET45 int result = async @@ -339,6 +340,16 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit } } + public int Update(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TModel : class + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TModel : class + { + throw new NotImplementedException(); + } + /// /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. /// diff --git a/Source/EntityFramework.Extended/Batch/QueryHelper.cs b/Source/EntityFramework.Extended/Batch/QueryHelper.cs index 563964f..f83b475 100644 --- a/Source/EntityFramework.Extended/Batch/QueryHelper.cs +++ b/Source/EntityFramework.Extended/Batch/QueryHelper.cs @@ -21,12 +21,24 @@ internal static class QueryHelper { public class Db : IDisposable { + public Db(ObjectContext context) + { + _loggers = context.InterceptionContext.DbContexts.Select(ctx => ctx.Database.Log).Where(log => log != null).ToArray(); + } + public DbConnection Connection; public DbTransaction Transaction; public DbCommand Command; public bool OwnConnection; public bool OwnTransaction; + private Action[] _loggers; + + public void Log(string log) + { + foreach (var logger in _loggers) logger(log); + } + public void Dispose() { if (Command != null) @@ -63,7 +75,7 @@ public static Tuple GetStore(ObjectContext objectCo public static Db GetDb(ObjectContext context) { - var db = new Db(); + var db = new Db(context); try { var store = GetStore(context); @@ -108,6 +120,24 @@ public static void CopyTo(this ObjectParameterCollection parameters, DbCommand c } } + public static IList GetPropertyPositions(IQueryable query, IList fields = null) + { + if (fields == null) fields = new List(); + fields.Clear(); + var objectQuery = query as ObjectQuery; + var maps = new NonPublicMember(objectQuery).GetProperty("QueryState").GetField("_cachedPlan").GetField("CommandDefinition") + .GetField("_columnMapGenerators").Idx(0).GetField("_columnMap").GetProperty("Element").GetProperty("Properties").Value as Array; + foreach (var m in maps) + { + var mm = new NonPublicMember(m); + int pos = (int)mm.GetProperty("ColumnPos").Value; + string name = (string)mm.GetProperty("Name").Value; + while (pos + 1 > fields.Count) fields.Add(null); //advance the size of [fields] if the pos index is not covered by it + fields[pos] = name; + } + return fields; + } + public static string GetSelectSql(ObjectQuery query, IEnumerable selectedProperties, DbCommand command = null, IBatchRunner runner = null, IList fields = null) { @@ -126,29 +156,25 @@ public static string GetSelectSql(ObjectQuery query, IEnumerable if (fields != null) { - fields.Clear(); - var maps = new NonPublicMember(objectQuery).GetProperty("QueryState").GetField("_cachedPlan").GetField("CommandDefinition") - .GetField("_columnMapGenerators").Idx(0).GetField("_columnMap").GetProperty("Element").GetProperty("Properties").Value as Array; - foreach (var m in maps) - { - var mm = new NonPublicMember(m); - int pos = (int)mm.GetProperty("ColumnPos").Value; - string name = (string)mm.GetProperty("Name").Value; - while (pos+1 > fields.Count) fields.Add(null); - fields[pos] = name; - } + GetPropertyPositions(selectQuery, fields); } return selectSql; } - public static string GetSelectKeySql(ObjectQuery query, EntityMap entityMap, DbCommand command, IBatchRunner runner) + public static string GetSelectKeySql(ObjectQuery query, EntityMap entityMap, DbCommand command, IBatchRunner runner, IList fields = null) where TEntity : class { // TODO change to esql? // changing query to only select keys - return GetSelectSql(query, entityMap.KeyMaps.Select(key => key.PropertyName), command, runner); + var idFields = entityMap.KeyMaps.Select(key => key.PropertyName); + string sql = GetSelectSql(query, idFields, command, runner, fields); + //if (fields != null) + //{ + // for (int i = 0; i < fields.Count; i++) if (!idFields.Contains(fields[i])) fields[i] = null; + //} + return sql; } public static IEnumerable GetSelectedProperties(Expression queryExpression, EntityMap entityMap) @@ -236,6 +262,112 @@ select runner.Quote(map.ColumnName) sqlBuilder.Append(selectSql); db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); + +#if NET45 + int result = async + ? await db.Command.ExecuteNonQueryAsync().ConfigureAwait(false) + : db.Command.ExecuteNonQuery(); +#else + int result = db.Command.ExecuteNonQuery(); +#endif + // only commit if created transaction + if (db.OwnTransaction) + db.Transaction.Commit(); + + return result; + } + } + +#if NET45 + internal static async Task InternalUpdate(this IBatchRunner runner, IQueryable query, ObjectQuery objectQuery, + EntityMap entityMap, bool async = false) + where TModel : class +#else + internal static int InternalUpdate(this IBatchRunner runner, IQueryable query, ObjectQuery objectQuery, + EntityMap entityMap, bool async = false) + where TModel : class +#endif + { + using (var db = GetDb(objectQuery.Context)) + { + string keySelect = null, aliasTableUpdate; + int i = 0; + var idFields = new List(); + try + { + keySelect = GetSelectKeySql(objectQuery, entityMap, null, runner, idFields); + } + catch (NotSupportedException ex) + { + //if (ex.HResult == -2146233067) + throw new ArgumentException("The select statement must include the key(s) of updated table. The keys themselves would not be updated.", "query"); + //throw ex; + } + var selectKeyFields = new SelectedFields(keySelect, runner.CharToEscapeQuote); + var idFieldsMap = new Dictionary(); + for (i = 0; i < idFields.Count; i++) + { + if (idFields[i] == null) continue; + string valueField = selectKeyFields[i]; + if (selectKeyFields.AliasIndexes[i] > 0) valueField = valueField.Substring(0, selectKeyFields.AliasIndexes[i]); + idFieldsMap[idFields[i]] = valueField; + } + aliasTableUpdate = idFieldsMap.First().Value.Split('.')[0]; + + var selectedProperties = GetSelectedProperties(query.Expression, entityMap); + if (selectedProperties == null || selectedProperties.Count() < 1) + throw new ArgumentException("Cannot read the selected fields in the query", "query"); + + var updateFields = new List(); + string selectSql = GetSelectSql(objectQuery, selectedProperties, db.Command, runner, updateFields); + var selectFields = new SelectedFields(selectSql, runner.CharToEscapeQuote); + + var sqlFrom = selectSql.Substring(selectFields.FromIndex); + string strRegex = @"\s+AS\s+" + Regex.Escape(aliasTableUpdate), + strRegex2 = Regex.Escape(entityMap.TableName) + strRegex; + if (!Regex.IsMatch(sqlFrom, strRegex2, RegexOptions.IgnoreCase)) + { + var match = Regex.Match(sqlFrom, strRegex, RegexOptions.IgnoreCase); + if (match != null && match.Success) + { + int nextLine = sqlFrom.IndexOf('\n', match.Index + match.Length); + if (nextLine < 0) nextLine = sqlFrom.Length; else nextLine++; + aliasTableUpdate = runner.Quote("__alias1"); + var joinUpdate = new StringBuilder("JOIN ").Append(entityMap.TableName).Append(" AS ").Append(aliasTableUpdate); + string conjunction = " ON "; + foreach (var kvp in idFieldsMap) + { + joinUpdate.Append(conjunction).Append(kvp.Value).Append(" = ") + .Append(aliasTableUpdate).Append(".") + .Append(entityMap.PropertyMaps.Where(p => p.PropertyName == kvp.Key).Select(p => runner.Quote(p.ColumnName)).First()); + conjunction = " AND "; + } + joinUpdate.AppendLine(); + sqlFrom = sqlFrom.Insert(nextLine, joinUpdate.ToString()); + } + else + { + throw new ArgumentException("Cannot read the updated table in the query", "query"); + } + } + + var sqlBuilder = new StringBuilder(selectSql.Length * 2); + sqlBuilder.Append("UPDATE ").Append(aliasTableUpdate).Append(" SET").Append(Environment.NewLine); + for (i=0; i < updateFields.Count; i++) + { + if (updateFields[i] == null || idFieldsMap.ContainsKey(updateFields[i])) continue; + string valueField = selectFields[i]; + if (selectFields.AliasIndexes[i] > 0) valueField = valueField.Substring(0, selectFields.AliasIndexes[i]); + sqlBuilder.Append(entityMap.PropertyMaps.Where(p => p.PropertyName == updateFields[i]).Select(p => runner.Quote(p.ColumnName)).First()) + .Append(" = ").Append(valueField); + if (i < updateFields.Count - 1) sqlBuilder.Append(","); + sqlBuilder.AppendLine(); + } + sqlBuilder.Append(sqlFrom); + + db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); #if NET45 int result = async @@ -258,6 +390,8 @@ public SelectedFields(string sql, char escapingQuoteChar) { if (!sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) return; bool isEnd = false, isInStr = false; + int beginAlias = 0; + List aliasIdxs = new List(); var field = new List(); int idx = "SELECT".Length; while (idx < sql.Length) @@ -270,7 +404,7 @@ public SelectedFields(string sql, char escapingQuoteChar) int idx2 = idx + 1; if (ch == escapingQuoteChar && idx2 < sql.Length && sql[idx2] == '\'') { - field.Add(sql[++idx]); //Don't consider this quote character as the end of string + field.Add(sql[++idx]); //Don't consider this quote character as the end of string } else if (ch == '\'') //The end of string { @@ -282,7 +416,9 @@ public SelectedFields(string sql, char escapingQuoteChar) if (ch == ',') { this.Add(new string(field.ToArray()).TrimEnd()); + aliasIdxs.Add(beginAlias); field.Clear(); + beginAlias = 0; } else { @@ -296,16 +432,25 @@ public SelectedFields(string sql, char escapingQuoteChar) isEnd = true; FromIndex = idx - 4; this.Add(new string(field.GetRange(0, field.Count - 6).ToArray()).TrimEnd()); + aliasIdxs.Add(beginAlias); break; } + else if (field.Count > 4 && char.IsWhiteSpace(field[field.Count - 4]) + && char.ToUpper(field[field.Count - 3]) == 'A' && char.ToUpper(field[field.Count - 2]) == 'S' + && char.IsWhiteSpace(field.Last())) //The beginning of FROM clause (the end of SELECT clause) + { + beginAlias = field.Count - 4; + } } } idx++; } if (!isEnd) this.Clear(); + else AliasIndexes = aliasIdxs.ToArray(); } public int FromIndex { get; private set; } + public int[] AliasIndexes { get; private set; } } } diff --git a/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs b/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs index 2f7fc19..cd18a5b 100644 --- a/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs @@ -115,6 +115,7 @@ private int InternalDelete(ObjectContext objectContext, EntityMap entit sqlBuilder.Append(")"); db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); #if NET45 int result = async @@ -320,6 +321,7 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit sqlBuilder.Append(")"); db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); #if NET45 int result = async @@ -336,6 +338,46 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit } } + /// + /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. + /// + /// The type item. + /// The query to create SELECT clause statement. + /// The query to create SELECT clause statement and it can also be used to get the information of db connection via + /// objectQuery.Context property. + /// The for entity type of the destination table (IDbSet). + /// + /// The number of rows inserted. + /// + public int Update(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) + where TModel : class + { +#if NET45 + return this.InternalUpdate(query, objectQuery, entityMap, false).Result; +#else + return this.InternalUpdate(query, objectQuery, entityMap); +#endif + } + +#if NET45 + /// + /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. + /// + /// The type item. + /// The query to create SELECT clause statement. + /// The query to create SELECT clause statement and it can also be used to get the information of db connection via + /// objectQuery.Context property. + /// The for entity type of the destination table (IDbSet). + /// + /// The number of rows inserted. + /// + public Task UpdateAsync(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) + where TModel : class + { + return this.InternalUpdate(query, objectQuery, entityMap, true); + } +#endif + /// /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. /// diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec index c42e7bb..11d6801 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec @@ -2,7 +2,7 @@ EntityFrameworkExtended - 6.2.0.2 + 6.2.0.3 LoreSoft, AT Mulyana LoreSoft, AT Mulyana https://github.com/atmulyana/EntityFramework.Extended @@ -10,7 +10,7 @@ false A library that extends the functionality of Entity Framework by adding batch update, future queries and audit logs. Entity Framework extensions library. - Copyright (c) 2014, LoreSoft, Modified by AT Mulyana in 2016 + Copyright (c) 2014, LoreSoft, Modified by AT Mulyana in 2016-2018 en-US EntityFramework diff --git a/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs b/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs index c14da5d..7700d80 100644 --- a/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs +++ b/Source/EntityFramework.Extended/Extensions/BatchExtensions.cs @@ -401,7 +401,7 @@ private static Tuple GetQueryObjects(IDbSet destQuery = dbSet.ToObjectQuery(); if (destQuery == null) throw new ArgumentException("The DbSet must be of type ObjectQuery or DbQuery.", "dbSet"); @@ -416,12 +416,12 @@ private static Tuple GetQueryObjects(IDbSet, EntityMap> CheckInsertParams(IDbSet destination, + private static Tuple, EntityMap> CheckDbQueryParams(IDbSet destination, IQueryable source) where TEntity : class where TModel : class { - EntityMap entityMap = GetQueryObjects(destination).Item2; + EntityMap entityMap = GetQueryObjects(destination).Item2; ObjectQuery sourceQuery = source.ToObjectQuery(); if (sourceQuery == null) @@ -434,6 +434,72 @@ private static Tuple, EntityMap> CheckInsertParams + /// Executes a statement `INSERT INTO [Table] (...) SELECT ...`. + /// + /// The type of the entity representing a record in database table. + /// The type of the query item. + /// The target table where the new data will be inserted into. + /// The query which retrieves the new data that will be inserted into the target table. + /// The number of row inserted. + /// Copy all items whose product id "K9-RT-02" from table item into item_2. + /// + /// + /// + /// When executing this method, the statement is immediately executed on the database provider + /// and is not part of the change tracking system. Also, changes will not be reflected on + /// any entities that have already been materialized in the current context. + /// + public static int Update( + this IDbSet destination, + IQueryable source) + where TEntity : class + where TModel : class + { + var p = CheckDbQueryParams(destination, source); + return ResolveRunner().Update(source, p.Item1, p.Item2); + } + +#if NET45 + /// + /// Executes a statement `INSERT INTO [Table] (...) SELECT ...` asynchronously. + /// + /// The type of the entity representing a record in database table. + /// The type of the query item. + /// The target table where the new data will be inserted into. + /// The query which retrieves the new data that will be inserted into the target table. + /// The number of row inserted. + /// Copy all items whose product id "K9-RT-02" from table item into item_2. + /// + /// + /// + /// When executing this method, the statement is immediately executed on the database provider + /// and is not part of the change tracking system. Also, changes will not be reflected on + /// any entities that have already been materialized in the current context. + /// + public static Task UpdateAsync( + this IDbSet destination, + IQueryable source) + where TEntity : class + where TModel : class + { + var p = CheckDbQueryParams(destination, source); + return ResolveRunner().UpdateAsync(source, p.Item1, p.Item2); + } +#endif + /// /// Executes a statement `INSERT INTO [Table] (...) SELECT ...`. /// @@ -462,7 +528,7 @@ public static int Insert( where TEntity : class where TModel : class { - var p = CheckInsertParams(destination, source); + var p = CheckDbQueryParams(destination, source); return ResolveRunner().Insert(source, p.Item1, p.Item2); } @@ -495,7 +561,7 @@ public static Task InsertAsync( where TEntity : class where TModel : class { - var p = CheckInsertParams(destination, source); + var p = CheckDbQueryParams(destination, source); return ResolveRunner().InsertAsync(source, p.Item1, p.Item2); } #endif diff --git a/Source/GlobalAssemblyInfo.net40.cs b/Source/GlobalAssemblyInfo.net40.cs index 71bba45..5b8dfb6 100644 --- a/Source/GlobalAssemblyInfo.net40.cs +++ b/Source/GlobalAssemblyInfo.net40.cs @@ -11,10 +11,10 @@ [assembly: System.Reflection.AssemblyProduct("EntityFramework.Extended (.NET 4.0)")] [assembly: System.Reflection.AssemblyDescription("Entity Framework extensions library. Built for .NET 4.0")] [assembly: System.Reflection.AssemblyCompany("LoreSoft")] -[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016")] +[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016-2018")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.2.0.2")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.2")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.3")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.3")] @@ -30,9 +30,9 @@ internal sealed partial class ThisAssembly { internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.2.0.2"; + internal const string AssemblyFileVersion = "6.2.0.3"; - internal const string AssemblyInformationalVersion = "6.2.0.2"; + internal const string AssemblyInformationalVersion = "6.2.0.3"; private ThisAssembly() { } diff --git a/Source/GlobalAssemblyInfo.net45.cs b/Source/GlobalAssemblyInfo.net45.cs index 394aa1a..60eb964 100644 --- a/Source/GlobalAssemblyInfo.net45.cs +++ b/Source/GlobalAssemblyInfo.net45.cs @@ -11,10 +11,10 @@ [assembly: System.Reflection.AssemblyProduct("EntityFramework.Extended (.NET 4.5)")] [assembly: System.Reflection.AssemblyDescription("Entity Framework extensions library. Built for .NET 4.5")] [assembly: System.Reflection.AssemblyCompany("LoreSoft")] -[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016")] +[assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016-2018")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.2.0.2")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.2")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.3")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.3")] @@ -30,9 +30,9 @@ internal sealed partial class ThisAssembly { internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.2.0.2"; + internal const string AssemblyFileVersion = "6.2.0.3"; - internal const string AssemblyInformationalVersion = "6.2.0.2"; + internal const string AssemblyInformationalVersion = "6.2.0.3"; private ThisAssembly() { } diff --git a/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs b/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs index adc14d6..44be2c0 100644 --- a/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs +++ b/Source/Samples/net40/Tracker.SqlServer.Test/ExtensionTest.cs @@ -73,6 +73,7 @@ public void TransactionScopeObjectContext() private void _Insert(TrackerEntities db) { + db.Database.Log = s => System.Diagnostics.Debug.WriteLine(s); db.ProductSummaries.Delete(); var query = from product in db.Products join item2 in ( diff --git a/Source/Samples/net45/Tracker.MySql.Entities/Tracker.Designer.cs b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.Designer.cs index 452bdfb..d3ef797 100644 --- a/Source/Samples/net45/Tracker.MySql.Entities/Tracker.Designer.cs +++ b/Source/Samples/net45/Tracker.MySql.Entities/Tracker.Designer.cs @@ -1,4 +1,4 @@ -// T4 code generation is enabled for model 'E:\Projects\EntityFramework.Extended\modif\Source\Samples\net45\Tracker.MySql.Entities\Tracker.edmx'. +// T4 code generation is enabled for model 'E:\Projects\EntityFramework.Extended\Source\Samples\net45\Tracker.MySql.Entities\Tracker.edmx'. // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model // is open in the designer. diff --git a/Source/Samples/net45/Tracker.MySql.Entities/app.config b/Source/Samples/net45/Tracker.MySql.Entities/app.config index 5686b67..255c75b 100644 --- a/Source/Samples/net45/Tracker.MySql.Entities/app.config +++ b/Source/Samples/net45/Tracker.MySql.Entities/app.config @@ -12,8 +12,8 @@ - - + + diff --git a/Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs b/Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs index 9a117c8..06bc028 100644 --- a/Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs +++ b/Source/Samples/net45/Tracker.MySql.Test/ExtensionTest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Transactions; using EntityFramework.Extensions; using Xunit; using Tracker.MySql.Entities; @@ -21,6 +20,7 @@ public void BeginTransactionObjectContext() using (var db = new TrackerEntities()) using (var tx = db.Database.BeginTransaction()) { + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = db.users @@ -48,6 +48,7 @@ public void NoTransactionObjectContext() { using (var db = new TrackerEntities()) { + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = db.users @@ -93,6 +94,7 @@ private void _Insert(TrackerEntities db, bool isAsync = false) Locator.Current.Register(() => new MySqlBatchRunner()); try { + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); db.productsummaries.Delete(); var query = from product in db.products join item2 in ( diff --git a/Source/Samples/net45/Tracker.SqlServer.CodeFirst/App.config b/Source/Samples/net45/Tracker.SqlServer.CodeFirst/App.config index f93390f..062780d 100644 --- a/Source/Samples/net45/Tracker.SqlServer.CodeFirst/App.config +++ b/Source/Samples/net45/Tracker.SqlServer.CodeFirst/App.config @@ -10,7 +10,7 @@ - + diff --git a/Source/Samples/net45/Tracker.SqlServer.Test/BatchDbContext.cs b/Source/Samples/net45/Tracker.SqlServer.Test/BatchDbContext.cs index 0fa818e..c75e390 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Test/BatchDbContext.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Test/BatchDbContext.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Data.Entity.Infrastructure; using System.Linq; -using System.Text; using EntityFramework.Extensions; using Xunit; using Tracker.SqlServer.CodeFirst; @@ -17,6 +14,7 @@ public class BatchDbContext public void Delete() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = db.Users .Where(u => u.EmailAddress.EndsWith(emailDomain)) @@ -26,6 +24,7 @@ public void Delete() public void DeleteWhere() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = db.Users @@ -37,6 +36,7 @@ public void DeleteWhere() public async void DeleteAsync() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = await db.Users @@ -48,6 +48,7 @@ public async void DeleteAsync() public void DeleteWhereWithExpressionContainingNullParameter() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; string optionalComparisonString = null; @@ -61,6 +62,7 @@ public void DeleteWhereWithExpressionContainingNullParameter() public void Update() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = db.Users .Where(u => u.EmailAddress.EndsWith(emailDomain)) @@ -71,6 +73,7 @@ public void Update() public async void UpdateAsync() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = await db.Users .Where(u => u.EmailAddress.EndsWith(emailDomain)) @@ -81,6 +84,7 @@ public async void UpdateAsync() public void UpdateAppend() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; string newComment = " New Comment"; @@ -94,6 +98,7 @@ public void UpdateAppend() public void UpdateAppendAndNull() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; string newComment = " New Comment"; @@ -112,6 +117,7 @@ public void UpdateAppendAndNull() public void UpdateJoin() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; string space = " "; @@ -124,6 +130,7 @@ public void UpdateJoin() public void UpdateCopy() { var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; int count = db.Users @@ -136,6 +143,7 @@ public void UpdateWithExpressionContainingNullParameter() { // This test verifies that the update is interpreted correctly when the where expression uses a parameter with a null parameter var db = new TrackerContext(); + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); string emailDomain = "@test.com"; string optionalComparisonString = null; diff --git a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs index 35e1a1e..9977111 100644 --- a/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs +++ b/Source/Samples/net45/Tracker.SqlServer.Test/ExtensionTest.cs @@ -1,12 +1,10 @@ using System; -using System.Text; using System.Collections.Generic; using System.Linq; using System.Transactions; using EntityFramework.Extensions; using Xunit; using Tracker.SqlServer.Entities; -using System.Threading.Tasks; namespace Tracker.SqlServer.Test { @@ -73,6 +71,7 @@ public void TransactionScopeObjectContext() private void _Insert(TrackerEntities db, bool isAsync=false) { + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); db.ProductSummaries.Delete(); var query = from product in db.Products join item2 in ( @@ -162,6 +161,7 @@ public void InsertAsync() private void _InsertBulk(TrackerEntities db, bool isAsync = false) { + db.Database.Log = s => System.Diagnostics.Trace.WriteLine(s); db.ProductSummaries.Delete(); db.Database.ExecuteSqlCommand("TRUNCATE TABLE ATable"); @@ -193,7 +193,7 @@ private void _InsertBulk(TrackerEntities db, bool isAsync = false) //db.ProductSummaries.AddRange(entities); //db.SaveChanges(); - System.Diagnostics.Debug.WriteLine("***** Executing bulk insert (" + n + " items) takes " + System.Diagnostics.Trace.WriteLine("***** Executing bulk insert (" + n + " items) takes " + DateTime.Now.Subtract(start).ToString(@"hh\:mm\:ss\.ffffff")); Assert.True(db.ProductSummaries.Count() == n); From c386e06a90ccf95ca80117b5328eaaa90425f9d8 Mon Sep 17 00:00:00 2001 From: AT Mulyana Date: Wed, 21 Nov 2018 03:24:16 +0700 Subject: [PATCH 8/8] update joined table (added for MySQL) --- .../Batch/MySqlBatchRunner.cs | 104 +++++++++++++- .../Batch/QueryHelper.cs | 128 +++++------------- .../Batch/SqlServerBatchRunner.cs | 104 +++++++++++--- .../EntityFramework.Extended.nuspec | 2 +- Source/GlobalAssemblyInfo.net40.cs | 10 +- Source/GlobalAssemblyInfo.net45.cs | 10 +- 6 files changed, 232 insertions(+), 126 deletions(-) diff --git a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs index 2adf71a..6593253 100644 --- a/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/MySqlBatchRunner.cs @@ -340,15 +340,115 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit } } + private int InternalUpdate(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap, Func updateCommand) where TModel : class + { + var param = this.UpdateJoinParam(query, objectQuery, entityMap); + using (var db = param.Item1) + { + string keySelect = param.Item2; + var selectKeyFields = param.Item3; + var idFieldsMap = param.Item4; + string selectSql = param.Item5; + var selectFields = param.Item6; + var updateFields = param.Item7; + + var reAs = new Regex(@"^\s*AS\s+", RegexOptions.IgnoreCase); + var sqlBuilder = new StringBuilder(selectSql.Length * 2); + sqlBuilder.Append("UPDATE ").Append(entityMap.TableName).Append(" j0 INNER JOIN (").AppendLine() + .Append(selectSql).AppendLine().Append(") j1 "); + string conjunction = " ON "; + foreach (var kvp in idFieldsMap) + { + string keyColName = entityMap.PropertyMaps.Where(p => p.PropertyName == kvp.Key).Select(p => Quote(p.ColumnName)).First(); + int i = 0; + for (; i < selectFields.Count; i++) if (selectFields[i].StartsWith(kvp.Value, StringComparison.OrdinalIgnoreCase)) break; + string keyAlias = selectFields[i]; + if (selectFields.AliasIndexes[i] > 0) + { + keyAlias = reAs.Replace(keyAlias.Substring(selectFields.AliasIndexes[i]), ""); + } + else + { + var fieldParts = keyAlias.Split(new char[] { '.' }, 2); + keyAlias = fieldParts.Length > 1 ? fieldParts[1] : fieldParts[0]; + } + sqlBuilder.Append(conjunction).Append("j0.").Append(keyColName).Append(" = j1.").Append(keyAlias); + conjunction = " AND "; + } + sqlBuilder.AppendLine().Append("SET").AppendLine(); + + for (int i = 0; i < updateFields.Count; i++) + { + if (updateFields[i] == null || idFieldsMap.ContainsKey(updateFields[i])) continue; + string valueField = selectFields[i]; + if (selectFields.AliasIndexes[i] > 0) + { + valueField = reAs.Replace(valueField.Substring(selectFields.AliasIndexes[i]), ""); + } + else + { + var fieldParts = valueField.Split(new char[] { '.' }, 2); + valueField = fieldParts.Length > 1 ? fieldParts[1] : fieldParts[0]; + } + sqlBuilder.Append("j0.").Append(entityMap.PropertyMaps.Where(p => p.PropertyName == updateFields[i]).Select(p => Quote(p.ColumnName)).First()) + .Append(" = j1.").Append(valueField); + if (i < updateFields.Count - 1) sqlBuilder.Append(","); + sqlBuilder.AppendLine(); + } + + db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); + int result = updateCommand(db); + + // only commit if created transaction + if (db.OwnTransaction) + db.Transaction.Commit(); + return result; + } + } + + /// + /// Execute statement `UPDATE Table1 {JOIN Table2 ... } SET ... `. + /// + /// The type item. + /// The SELECT query from which we determine what field to be set (updated). It must include primary key(s) field so that the update statement + /// will appropriately update the true records. The key field(s) will not updated. + /// The query to create SQL statement and it can also be used to get the information of db connection via + /// objectQuery.Context property. + /// The for entity type of the updated table (IDbSet). + /// + /// The number of rows updated. + /// public int Update(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TModel : class { - throw new NotImplementedException(); + return InternalUpdate(query, objectQuery, entityMap, + db => db.Command.ExecuteNonQuery()); } +#if NET45 + /// + /// Execute statement `UPDATE Table1 {JOIN Table2 ... } SET ... `. + /// + /// The type item. + /// The SELECT query from which we determine what field to be set (updated). It must include primary key(s) field so that the update statement + /// will appropriately update the true records. The key field(s) will not updated. + /// The query to create SQL statement and it can also be used to get the information of db connection via + /// objectQuery.Context property. + /// The for entity type of the updated table (IDbSet). + /// + /// The number of rows updated. + /// public Task UpdateAsync(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TModel : class { - throw new NotImplementedException(); + Func> update = async (QueryHelper.Db db) => + { + return await db.Command.ExecuteNonQueryAsync(); + }; + return new Task(() => + InternalUpdate(query, objectQuery, entityMap, db => update(db).Result) + ); } +#endif /// /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. diff --git a/Source/EntityFramework.Extended/Batch/QueryHelper.cs b/Source/EntityFramework.Extended/Batch/QueryHelper.cs index f83b475..6d609d5 100644 --- a/Source/EntityFramework.Extended/Batch/QueryHelper.cs +++ b/Source/EntityFramework.Extended/Batch/QueryHelper.cs @@ -279,109 +279,43 @@ select runner.Quote(map.ColumnName) } } -#if NET45 - internal static async Task InternalUpdate(this IBatchRunner runner, IQueryable query, ObjectQuery objectQuery, - EntityMap entityMap, bool async = false) + internal static Tuple, string, SelectedFields, List> UpdateJoinParam(this IBatchRunner runner, + IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TModel : class -#else - internal static int InternalUpdate(this IBatchRunner runner, IQueryable query, ObjectQuery objectQuery, - EntityMap entityMap, bool async = false) - where TModel : class -#endif { - using (var db = GetDb(objectQuery.Context)) + var db = GetDb(objectQuery.Context); + string keySelect = null; + int i = 0; + var idFields = new List(); + try { - string keySelect = null, aliasTableUpdate; - int i = 0; - var idFields = new List(); - try - { - keySelect = GetSelectKeySql(objectQuery, entityMap, null, runner, idFields); - } - catch (NotSupportedException ex) - { - //if (ex.HResult == -2146233067) - throw new ArgumentException("The select statement must include the key(s) of updated table. The keys themselves would not be updated.", "query"); - //throw ex; - } - var selectKeyFields = new SelectedFields(keySelect, runner.CharToEscapeQuote); - var idFieldsMap = new Dictionary(); - for (i = 0; i < idFields.Count; i++) - { - if (idFields[i] == null) continue; - string valueField = selectKeyFields[i]; - if (selectKeyFields.AliasIndexes[i] > 0) valueField = valueField.Substring(0, selectKeyFields.AliasIndexes[i]); - idFieldsMap[idFields[i]] = valueField; - } - aliasTableUpdate = idFieldsMap.First().Value.Split('.')[0]; - - var selectedProperties = GetSelectedProperties(query.Expression, entityMap); - if (selectedProperties == null || selectedProperties.Count() < 1) - throw new ArgumentException("Cannot read the selected fields in the query", "query"); - - var updateFields = new List(); - string selectSql = GetSelectSql(objectQuery, selectedProperties, db.Command, runner, updateFields); - var selectFields = new SelectedFields(selectSql, runner.CharToEscapeQuote); - - var sqlFrom = selectSql.Substring(selectFields.FromIndex); - string strRegex = @"\s+AS\s+" + Regex.Escape(aliasTableUpdate), - strRegex2 = Regex.Escape(entityMap.TableName) + strRegex; - if (!Regex.IsMatch(sqlFrom, strRegex2, RegexOptions.IgnoreCase)) - { - var match = Regex.Match(sqlFrom, strRegex, RegexOptions.IgnoreCase); - if (match != null && match.Success) - { - int nextLine = sqlFrom.IndexOf('\n', match.Index + match.Length); - if (nextLine < 0) nextLine = sqlFrom.Length; else nextLine++; - aliasTableUpdate = runner.Quote("__alias1"); - var joinUpdate = new StringBuilder("JOIN ").Append(entityMap.TableName).Append(" AS ").Append(aliasTableUpdate); - string conjunction = " ON "; - foreach (var kvp in idFieldsMap) - { - joinUpdate.Append(conjunction).Append(kvp.Value).Append(" = ") - .Append(aliasTableUpdate).Append(".") - .Append(entityMap.PropertyMaps.Where(p => p.PropertyName == kvp.Key).Select(p => runner.Quote(p.ColumnName)).First()); - conjunction = " AND "; - } - joinUpdate.AppendLine(); - sqlFrom = sqlFrom.Insert(nextLine, joinUpdate.ToString()); - } - else - { - throw new ArgumentException("Cannot read the updated table in the query", "query"); - } - } - - var sqlBuilder = new StringBuilder(selectSql.Length * 2); - sqlBuilder.Append("UPDATE ").Append(aliasTableUpdate).Append(" SET").Append(Environment.NewLine); - for (i=0; i < updateFields.Count; i++) - { - if (updateFields[i] == null || idFieldsMap.ContainsKey(updateFields[i])) continue; - string valueField = selectFields[i]; - if (selectFields.AliasIndexes[i] > 0) valueField = valueField.Substring(0, selectFields.AliasIndexes[i]); - sqlBuilder.Append(entityMap.PropertyMaps.Where(p => p.PropertyName == updateFields[i]).Select(p => runner.Quote(p.ColumnName)).First()) - .Append(" = ").Append(valueField); - if (i < updateFields.Count - 1) sqlBuilder.Append(","); - sqlBuilder.AppendLine(); - } - sqlBuilder.Append(sqlFrom); + keySelect = GetSelectKeySql(objectQuery, entityMap, null, runner, idFields); + } + catch (NotSupportedException ex) + { + //if (ex.HResult == -2146233067) + throw new ArgumentException("The select statement must include the key(s) of updated table. The keys themselves would not be updated.", "query"); + //throw ex; + } + var selectKeyFields = new SelectedFields(keySelect, runner.CharToEscapeQuote); + var idFieldsMap = new Dictionary(); + for (i = 0; i < idFields.Count; i++) + { + if (idFields[i] == null) continue; + string valueField = selectKeyFields[i]; + if (selectKeyFields.AliasIndexes[i] > 0) valueField = valueField.Substring(0, selectKeyFields.AliasIndexes[i]); + idFieldsMap[idFields[i]] = valueField; + } - db.Command.CommandText = sqlBuilder.ToString(); - db.Log(db.Command.CommandText); + var selectedProperties = GetSelectedProperties(query.Expression, entityMap); + if (selectedProperties == null || selectedProperties.Count() < 1) + throw new ArgumentException("Cannot read the selected fields in the query", "query"); -#if NET45 - int result = async - ? await db.Command.ExecuteNonQueryAsync().ConfigureAwait(false) - : db.Command.ExecuteNonQuery(); -#else - int result = db.Command.ExecuteNonQuery(); -#endif - // only commit if created transaction - if (db.OwnTransaction) - db.Transaction.Commit(); + var updateFields = new List(); + string selectSql = GetSelectSql(objectQuery, selectedProperties, db.Command, runner, updateFields); + var selectFields = new SelectedFields(selectSql, runner.CharToEscapeQuote); - return result; - } + return Tuple.Create(db, keySelect, selectKeyFields, idFieldsMap, selectSql, selectFields, updateFields); } public class SelectedFields : List diff --git a/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs b/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs index cd18a5b..e8a128d 100644 --- a/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs +++ b/Source/EntityFramework.Extended/Batch/SqlServerBatchRunner.cs @@ -338,43 +338,115 @@ private int InternalUpdate(ObjectContext objectContext, EntityMap entit } } + private int InternalUpdate(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap, Func updateCommand) where TModel : class + { + var param = this.UpdateJoinParam(query, objectQuery, entityMap); + using (var db = param.Item1) + { + string keySelect = param.Item2; + var selectKeyFields = param.Item3; + var idFieldsMap = param.Item4; + string selectSql = param.Item5; + var selectFields = param.Item6; + var updateFields = param.Item7; + + string aliasTableUpdate = idFieldsMap.First().Value.Split('.')[0]; + var sqlFrom = selectSql.Substring(selectFields.FromIndex); + string strRegex = @"\s+AS\s+" + Regex.Escape(aliasTableUpdate), + strRegex2 = Regex.Escape(entityMap.TableName) + strRegex; + if (!Regex.IsMatch(sqlFrom, strRegex2, RegexOptions.IgnoreCase)) + { + var match = Regex.Match(sqlFrom, strRegex, RegexOptions.IgnoreCase); + if (match != null && match.Success) + { + int nextLine = sqlFrom.IndexOf('\n', match.Index + match.Length); + if (nextLine < 0) nextLine = sqlFrom.Length; else nextLine++; + aliasTableUpdate = Quote("__alias1"); + var joinUpdate = new StringBuilder(" JOIN ").Append(entityMap.TableName).Append(" AS ").Append(aliasTableUpdate); + string conjunction = " ON "; + foreach (var kvp in idFieldsMap) + { + joinUpdate.Append(conjunction).Append(kvp.Value).Append(" = ") + .Append(aliasTableUpdate).Append(".") + .Append(entityMap.PropertyMaps.Where(p => p.PropertyName == kvp.Key).Select(p => Quote(p.ColumnName)).First()); + conjunction = " AND "; + } + joinUpdate.AppendLine(); + sqlFrom = sqlFrom.Insert(nextLine, joinUpdate.ToString()); + } + else + { + throw new ArgumentException("Cannot read the updated table in the query", "query"); + } + } + + var sqlBuilder = new StringBuilder(selectSql.Length * 2); + sqlBuilder.Append("UPDATE ").Append(aliasTableUpdate).Append(" SET").Append(Environment.NewLine); + for (int i = 0; i < updateFields.Count; i++) + { + if (updateFields[i] == null || idFieldsMap.ContainsKey(updateFields[i])) continue; + string valueField = selectFields[i]; + if (selectFields.AliasIndexes[i] > 0) valueField = valueField.Substring(0, selectFields.AliasIndexes[i]); + sqlBuilder.Append(entityMap.PropertyMaps.Where(p => p.PropertyName == updateFields[i]).Select(p => Quote(p.ColumnName)).First()) + .Append(" = ").Append(valueField); + if (i < updateFields.Count - 1) sqlBuilder.Append(","); + sqlBuilder.AppendLine(); + } + sqlBuilder.Append(sqlFrom); + + db.Command.CommandText = sqlBuilder.ToString(); + db.Log(db.Command.CommandText); + int result = updateCommand(db); + + // only commit if created transaction + if (db.OwnTransaction) + db.Transaction.Commit(); + return result; + } + } + /// - /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. + /// Execute statement `UPDATE [Table] SET ... FROM [Table] {JOIN Another Table ...} ...`. /// /// The type item. - /// The query to create SELECT clause statement. - /// The query to create SELECT clause statement and it can also be used to get the information of db connection via + /// The SELECT query from which we determine what field to be set (updated). It must include primary key(s) field so that the update statement + /// will appropriately update the true records. The key field(s) will not updated. + /// The query to create SQL statement and it can also be used to get the information of db connection via /// objectQuery.Context property. - /// The for entity type of the destination table (IDbSet). + /// The for entity type of the updated table (IDbSet). /// - /// The number of rows inserted. + /// The number of rows updated. /// public int Update(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TModel : class { -#if NET45 - return this.InternalUpdate(query, objectQuery, entityMap, false).Result; -#else - return this.InternalUpdate(query, objectQuery, entityMap); -#endif + return InternalUpdate(query, objectQuery, entityMap, + db => db.Command.ExecuteNonQuery()); } #if NET45 /// - /// Execute statement `INSERT INTO [Table] (...) SELECT ...`. + /// Execute statement `UPDATE [Table] SET ... FROM [Table] {JOIN Another Table ...} ...`. /// /// The type item. - /// The query to create SELECT clause statement. - /// The query to create SELECT clause statement and it can also be used to get the information of db connection via + /// The SELECT query from which we determine what field to be set (updated). It must include primary key(s) field so that the update statement + /// will appropriately update the true records. The key field(s) will not updated. + /// The query to create SQL statement and it can also be used to get the information of db connection via /// objectQuery.Context property. - /// The for entity type of the destination table (IDbSet). + /// The for entity type of the updated table (IDbSet). /// - /// The number of rows inserted. + /// The number of rows updated. /// public Task UpdateAsync(IQueryable query, ObjectQuery objectQuery, EntityMap entityMap) where TModel : class { - return this.InternalUpdate(query, objectQuery, entityMap, true); + Func> update = async (QueryHelper.Db db) => + { + return await db.Command.ExecuteNonQueryAsync(); + }; + return new Task( () => + InternalUpdate(query, objectQuery, entityMap, db => update(db).Result) + ); } #endif diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec index 11d6801..7cc3d69 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.nuspec @@ -2,7 +2,7 @@ EntityFrameworkExtended - 6.2.0.3 + 6.2.0.4 LoreSoft, AT Mulyana LoreSoft, AT Mulyana https://github.com/atmulyana/EntityFramework.Extended diff --git a/Source/GlobalAssemblyInfo.net40.cs b/Source/GlobalAssemblyInfo.net40.cs index 5b8dfb6..62fcfe6 100644 --- a/Source/GlobalAssemblyInfo.net40.cs +++ b/Source/GlobalAssemblyInfo.net40.cs @@ -13,8 +13,8 @@ [assembly: System.Reflection.AssemblyCompany("LoreSoft")] [assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016-2018")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.2.0.3")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.3")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.4")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.4")] @@ -26,13 +26,13 @@ internal sealed partial class ThisAssembly { internal const string AssemblyCompany = "LoreSoft"; - internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016"; + internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016-2018"; internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.2.0.3"; + internal const string AssemblyFileVersion = "6.2.0.4"; - internal const string AssemblyInformationalVersion = "6.2.0.3"; + internal const string AssemblyInformationalVersion = "6.2.0.4"; private ThisAssembly() { } diff --git a/Source/GlobalAssemblyInfo.net45.cs b/Source/GlobalAssemblyInfo.net45.cs index 60eb964..1cbe846 100644 --- a/Source/GlobalAssemblyInfo.net45.cs +++ b/Source/GlobalAssemblyInfo.net45.cs @@ -13,8 +13,8 @@ [assembly: System.Reflection.AssemblyCompany("LoreSoft")] [assembly: System.Reflection.AssemblyCopyright("Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016-2018")] [assembly: System.Reflection.AssemblyVersion("6.0.0.0")] -[assembly: System.Reflection.AssemblyFileVersion("6.2.0.3")] -[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.3")] +[assembly: System.Reflection.AssemblyFileVersion("6.2.0.4")] +[assembly: System.Reflection.AssemblyInformationalVersion("6.2.0.4")] @@ -26,13 +26,13 @@ internal sealed partial class ThisAssembly { internal const string AssemblyCompany = "LoreSoft"; - internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016"; + internal const string AssemblyCopyright = "Copyright © 2015 LoreSoft, Modified by AT Mulyana in 2016-2018"; internal const string AssemblyVersion = "6.0.0.0"; - internal const string AssemblyFileVersion = "6.2.0.3"; + internal const string AssemblyFileVersion = "6.2.0.4"; - internal const string AssemblyInformationalVersion = "6.2.0.3"; + internal const string AssemblyInformationalVersion = "6.2.0.4"; private ThisAssembly() { }