diff --git a/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.csproj b/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.csproj index 33cb4b9..4f119da 100644 --- a/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.csproj +++ b/Source/EntityFramework.Extended.Test/EntityFramework.Extended.Test.csproj @@ -80,6 +80,7 @@ + diff --git a/Source/EntityFramework.Extended.Test/InsertSqlGenerationTests.cs b/Source/EntityFramework.Extended.Test/InsertSqlGenerationTests.cs new file mode 100644 index 0000000..8e5ae4e --- /dev/null +++ b/Source/EntityFramework.Extended.Test/InsertSqlGenerationTests.cs @@ -0,0 +1,69 @@ +using System; +using System.Data.Entity; +using System.Linq; +using EntityFramework.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace EntityFramework.Test +{ + [TestClass] + public class InsertSqlGenerationTests + { + private IQueryable linqQuery; + + [TestInitialize] + public void TestFixtureSetUp() + { + Database.SetInitializer(null); + + linqQuery = new Repository().GetAll() + .Select(person => person); + } + + [TestMethod] + public void SelectIntoTempTableFromLinq() + { + string insertSql = linqQuery.SelectInsertSql("#tmp"); + Console.WriteLine(insertSql); + StringAssert.Contains(insertSql, " INTO #tmp FROM "); + } + + [TestMethod] + public void InsertSelectFromLinq() + { + string insertSql = linqQuery.InsertIntoSql("TableNameToInsert", "Id, FirstName, LastName"); + Console.WriteLine(insertSql); + StringAssert.Contains(insertSql, "INSERT INTO TableNameToInsert (Id, FirstName, LastName)\r\nSELECT"); + + Console.WriteLine(); + string insertSqlSimple = linqQuery.InsertIntoSql("TableNameToInsert"); + Console.WriteLine(insertSqlSimple); + StringAssert.Contains(insertSqlSimple, "INSERT INTO TableNameToInsert \r\nSELECT"); + } + } + + public class Person + { + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } + + public class Repository + where TAggregateRoot : class + { + private DbContext _dbContext = new TestContext(); + + public IQueryable GetAll() + { + return _dbContext.Set(); + } + } + + public class TestContext + : DbContext + { + public DbSet Persons { get; set; } + } + +} diff --git a/Source/EntityFramework.Extended/EntityFramework.Extended.csproj b/Source/EntityFramework.Extended/EntityFramework.Extended.csproj index 5d35fef..47f43df 100644 --- a/Source/EntityFramework.Extended/EntityFramework.Extended.csproj +++ b/Source/EntityFramework.Extended/EntityFramework.Extended.csproj @@ -86,6 +86,7 @@ + diff --git a/Source/EntityFramework.Extended/Extensions/ObjectQueryExtensions.cs b/Source/EntityFramework.Extended/Extensions/ObjectQueryExtensions.cs index 69c1670..9d2de65 100644 --- a/Source/EntityFramework.Extended/Extensions/ObjectQueryExtensions.cs +++ b/Source/EntityFramework.Extended/Extensions/ObjectQueryExtensions.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; -using System.Data.Common; +using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Objects; using System.Linq; using System.Linq.Expressions; -using System.Text; using EntityFramework.Reflection; namespace EntityFramework.Extensions @@ -124,5 +122,37 @@ public static ObjectContext GetContext(this IQueryable query) return null; } + /// + /// Captures SELECT ... FROM ... SQL from IDbSet, converts it into SELECT ... INTO ... T-SQL and executes it via context.ExecuteStoreCommand().
+ /// No objects are being brought into RAM / Context.
+ /// Only MS SQL Server and Sybase T-SQL RDBMS are supported.
+ /// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ). + ///
+ /// Entity type + /// DbSet of entities + /// Target table name to insert into + public static void SelectInsert(this IQueryable source, string tableName) + where TEntity : class + { + source.GetContext() + .ExecuteStoreCommand(source.SelectInsertSql(tableName)); + } + + /// + /// Captures SELECT ... FROM ... SQL from IDbSet, converts it into INSERT INTO ... SELECT FROM ... ANSI-SQL + /// and executes it via context.ExecuteStoreCommand().
+ /// No objects are being brought into RAM / Context.
+ /// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ). + ///
+ /// DbSet of entities + /// Target table name to insert into + /// Optional parameter for a list of columns to insert into + /// Entity type + public static void InsertInto(this IQueryable source, string tableName, string columnList = "") + where TEntity : class + { + source.GetContext() + .ExecuteStoreCommand(source.InsertIntoSql(tableName, columnList)); + } } } diff --git a/Source/EntityFramework.Extended/Extensions/QueryableExtensions.cs b/Source/EntityFramework.Extended/Extensions/QueryableExtensions.cs new file mode 100644 index 0000000..e753311 --- /dev/null +++ b/Source/EntityFramework.Extended/Extensions/QueryableExtensions.cs @@ -0,0 +1,58 @@ +using System.Linq; +using System.Text.RegularExpressions; + +namespace EntityFramework.Extensions +{ + /// + /// Extension methods for IQueryable to support INSERT ... and SELECT INSERT ... SQL statements without loading objects into RAM. + /// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ). + /// + public static class QueryableExtensions + { + private const string FromTableSqlExpression = @"\bFROM\b"; + + /// + /// Captures SELECT ... FROM ... SQL from IQueryable and converts it into SELECT ... INTO ... T-SQL.
+ /// No objects are being brought into RAM / Context.
+ /// Only MS SQL Server and Sybase T-SQL RDBMS are supported. + /// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ). + ///
+ /// Entity type + /// DbSet of entities (or any IQueryable that returns SQL from its ToString() implementation + /// Target table name to insert into + /// SELECT ... INSERT ... T-SQL statement + public static string SelectInsertSql(this IQueryable source, string tableName) + { + var regex = new Regex(FromTableSqlExpression); + string selectInsertSql = regex.Replace( + source.ToString() + , string.Format(" INTO {0} FROM ", tableName) + , 1); + + return selectInsertSql; + } + + /// + /// Captures SELECT ... FROM ... SQL from IQueryable and converts it into INSERT INTO ... SELECT FROM ... ANSI-SQL.
+ /// No objects are being brought into RAM / Context.
+ /// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ). + ///
+ /// Entity type + /// DbSet of entities (or any IQueryable that returns SQL from its ToString() implementation + /// Target table name to insert into + /// Optional parameter for a list of columns to insert into + /// INSERT INTO ... SELECT FROM ANSI-SQL statement + public static string InsertIntoSql(this IQueryable source, string tableName, string columnList = "") + { + string originalSql = source.ToString(); + if (! string.IsNullOrWhiteSpace(columnList)) + { + columnList = string.Format("({0})", columnList); + } + return string.Format("INSERT INTO {0} {1}\r\n{2}" + , tableName + , columnList + , originalSql); + } + } +} \ No newline at end of file diff --git a/Source/Samples/net40/Tracker.SqlServer.CodeFirst/Entity.csp b/Source/Samples/net40/Tracker.SqlServer.CodeFirst/Entity.csp index bc67dae..25ec03f 100644 --- a/Source/Samples/net40/Tracker.SqlServer.CodeFirst/Entity.csp +++ b/Source/Samples/net40/Tracker.SqlServer.CodeFirst/Entity.csp @@ -1,46 +1,46 @@ - - - - - - - - .\ - .\Entities - .\Mapping - Singular - Singular - Plural - Plural - - - sysdiagrams$ - - - False - - - ^(sp|tbl|udf|vw)_ - - - False - False - .\Queries - By - GetBy - Key - False - .\Mocks - True - Tracker.SqlServer.CodeFirst - - $(TrackerConnectionString) - SchemaExplorer.SqlSchemaProvider,SchemaExplorer.SqlSchemaProvider - - Tracker.SqlServer.CodeFirst.Mapping - Tracker.SqlServer.CodeFirst.Entities - Tracker.SqlServer.CodeFirst.Queries - Tracker.SqlServer.CodeFirst.Mocks - - + + + + + + + + .\ + .\Entities + .\Mapping + Singular + Singular + Plural + Plural + + + sysdiagrams$ + + + False + + + ^(sp|tbl|udf|vw)_ + + + False + False + .\Queries + By + GetBy + Key + False + .\Mocks + True + Tracker.SqlServer.CodeFirst + + $(TrackerConnectionString) + SchemaExplorer.SqlSchemaProvider,SchemaExplorer.SqlSchemaProvider + + Tracker.SqlServer.CodeFirst.Mapping + Tracker.SqlServer.CodeFirst.Entities + Tracker.SqlServer.CodeFirst.Queries + Tracker.SqlServer.CodeFirst.Mocks + + \ No newline at end of file