Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SELECT .. INSERT .. and INSERT INTO .. SELECT .. support #67

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
<Compile Include="ExceptionAssert.cs" />
<Compile Include="IoCTest.cs" />
<Compile Include="Caching\MemoryCacheProviderTest.cs" />
<Compile Include="InsertSqlGenerationTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Reflection\DynamicProxyTest.cs" />
<Compile Include="Reflection\DelegateFactoryTest.cs" />
Expand Down
69 changes: 69 additions & 0 deletions Source/EntityFramework.Extended.Test/InsertSqlGenerationTests.cs
Original file line number Diff line number Diff line change
@@ -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<Person> linqQuery;

[TestInitialize]
public void TestFixtureSetUp()
{
Database.SetInitializer<TestContext>(null);

linqQuery = new Repository<Person>().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<TAggregateRoot>
where TAggregateRoot : class
{
private DbContext _dbContext = new TestContext();

public IQueryable<TAggregateRoot> GetAll()
{
return _dbContext.Set<TAggregateRoot>();
}
}

public class TestContext
: DbContext
{
public DbSet<Person> Persons { get; set; }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<Compile Include="Caching\Query\LocalCollectionExpander.cs" />
<Compile Include="Caching\Query\QueryCache.cs" />
<Compile Include="Caching\Query\Utility.cs" />
<Compile Include="Extensions\QueryableExtensions.cs" />
<Compile Include="Future\IFutureRunner.cs" />
<Compile Include="Locator.cs" />
<Compile Include="Container.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -124,5 +122,37 @@ public static ObjectContext GetContext<TEntity>(this IQueryable<TEntity> query)
return null;
}

/// <summary>
/// Captures SELECT ... FROM ... SQL from IDbSet, converts it into SELECT ... INTO ... T-SQL and executes it via context.ExecuteStoreCommand().<br/>
/// No objects are being brought into RAM / Context. <br/>
/// Only MS SQL Server and Sybase T-SQL RDBMS are supported. <br/>
/// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ).
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="source">DbSet of entities</param>
/// <param name="tableName">Target table name to insert into</param>
public static void SelectInsert<TEntity>(this IQueryable<TEntity> source, string tableName)
where TEntity : class
{
source.GetContext()
.ExecuteStoreCommand(source.SelectInsertSql(tableName));
}

/// <summary>
/// Captures SELECT ... FROM ... SQL from IDbSet, converts it into INSERT INTO ... SELECT FROM ... ANSI-SQL
/// and executes it via context.ExecuteStoreCommand(). <br/>
/// No objects are being brought into RAM / Context. <br/>
/// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ).
/// </summary>
/// <param name="source">DbSet of entities</param>
/// <param name="tableName">Target table name to insert into</param>
/// <param name="columnList">Optional parameter for a list of columns to insert into</param>
/// <typeparam name="TEntity">Entity type</typeparam>
public static void InsertInto<TEntity>(this IQueryable<TEntity> source, string tableName, string columnList = "")
where TEntity : class
{
source.GetContext()
.ExecuteStoreCommand(source.InsertIntoSql(tableName, columnList));
}
}
}
58 changes: 58 additions & 0 deletions Source/EntityFramework.Extended/Extensions/QueryableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Linq;
using System.Text.RegularExpressions;

namespace EntityFramework.Extensions
{
/// <summary>
/// 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/ ).
/// </summary>
public static class QueryableExtensions
{
private const string FromTableSqlExpression = @"\bFROM\b";

/// <summary>
/// Captures SELECT ... FROM ... SQL from IQueryable and converts it into SELECT ... INTO ... T-SQL. <br/>
/// No objects are being brought into RAM / Context. <br/>
/// Only MS SQL Server and Sybase T-SQL RDBMS are supported.
/// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ).
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="source">DbSet of entities (or any IQueryable that returns SQL from its ToString() implementation</param>
/// <param name="tableName">Target table name to insert into</param>
/// <returns>SELECT ... INSERT ... T-SQL statement</returns>
public static string SelectInsertSql<TEntity>(this IQueryable<TEntity> source, string tableName)
{
var regex = new Regex(FromTableSqlExpression);
string selectInsertSql = regex.Replace(
source.ToString()
, string.Format(" INTO {0} FROM ", tableName)
, 1);

return selectInsertSql;
}

/// <summary>
/// Captures SELECT ... FROM ... SQL from IQueryable and converts it into INSERT INTO ... SELECT FROM ... ANSI-SQL. <br/>
/// No objects are being brought into RAM / Context. <br/>
/// Contributed by Agile Design LLC ( http://agiledesignllc.com/ ).
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="source">DbSet of entities (or any IQueryable that returns SQL from its ToString() implementation</param>
/// <param name="tableName">Target table name to insert into</param>
/// <param name="columnList">Optional parameter for a list of columns to insert into</param>
/// <returns>INSERT INTO ... SELECT FROM ANSI-SQL statement</returns>
public static string InsertIntoSql<TEntity>(this IQueryable<TEntity> 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);
}
}
}
90 changes: 45 additions & 45 deletions Source/Samples/net40/Tracker.SqlServer.CodeFirst/Entity.csp
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<codeSmith useRelativePaths="false" xmlns="http://www.codesmithtools.com/schema/csp.xsd">
<variables>
<add key="TrackerConnectionString" value="Data Source=(local);Initial Catalog=Tracker;Integrated Security=True" />
</variables>
<propertySets>
<propertySet name="Entity" output="Entity.txt" template="..\..\..\packages\PLINQO.EntityFramework.DbContext.1.0.37\tools\CSharp\Entity.cst">
<property name="ContextDirectory">.\</property>
<property name="EntityDirectory">.\Entities</property>
<property name="MappingDirectory">.\Mapping</property>
<property name="TableNaming">Singular</property>
<property name="EntityNaming">Singular</property>
<property name="RelationshipNaming">Plural</property>
<property name="ContextNaming">Plural</property>
<property name="IgnoreList">
<stringList>
<string>sysdiagrams$</string>
</stringList>
</property>
<property name="InclusionMode">False</property>
<property name="CleanExpressions">
<stringList>
<string>^(sp|tbl|udf|vw)_</string>
</stringList>
</property>
<property name="InterfaceMode">False</property>
<property name="IncludeQuery">False</property>
<property name="QueryDirectory">.\Queries</property>
<property name="MethodPrefix">By</property>
<property name="UniqueMethodPrefix">GetBy</property>
<property name="MethodKeySuffix">Key</property>
<property name="IncludeMock">False</property>
<property name="MockDirectory">.\Mocks</property>
<property name="Internalize">True</property>
<property name="ContextNamespace">Tracker.SqlServer.CodeFirst</property>
<property name="SourceDatabase">
<connectionString>$(TrackerConnectionString)</connectionString>
<providerType>SchemaExplorer.SqlSchemaProvider,SchemaExplorer.SqlSchemaProvider</providerType>
</property>
<property name="MappingNamespace">Tracker.SqlServer.CodeFirst.Mapping</property>
<property name="EntityNamespace">Tracker.SqlServer.CodeFirst.Entities</property>
<property name="QueryNamespace">Tracker.SqlServer.CodeFirst.Queries</property>
<property name="MockNamespace">Tracker.SqlServer.CodeFirst.Mocks</property>
</propertySet>
</propertySets>
<?xml version="1.0" encoding="utf-8"?>
<codeSmith useRelativePaths="false" xmlns="http://www.codesmithtools.com/schema/csp.xsd">
<variables>
<add key="TrackerConnectionString" value="Data Source=(local);Initial Catalog=Tracker;Integrated Security=True" />
</variables>
<propertySets>
<propertySet name="Entity" output="Entity.txt" template="..\..\..\packages\PLINQO.EntityFramework.DbContext.1.0.37\tools\CSharp\Entity.cst">
<property name="ContextDirectory">.\</property>
<property name="EntityDirectory">.\Entities</property>
<property name="MappingDirectory">.\Mapping</property>
<property name="TableNaming">Singular</property>
<property name="EntityNaming">Singular</property>
<property name="RelationshipNaming">Plural</property>
<property name="ContextNaming">Plural</property>
<property name="IgnoreList">
<stringList>
<string>sysdiagrams$</string>
</stringList>
</property>
<property name="InclusionMode">False</property>
<property name="CleanExpressions">
<stringList>
<string>^(sp|tbl|udf|vw)_</string>
</stringList>
</property>
<property name="InterfaceMode">False</property>
<property name="IncludeQuery">False</property>
<property name="QueryDirectory">.\Queries</property>
<property name="MethodPrefix">By</property>
<property name="UniqueMethodPrefix">GetBy</property>
<property name="MethodKeySuffix">Key</property>
<property name="IncludeMock">False</property>
<property name="MockDirectory">.\Mocks</property>
<property name="Internalize">True</property>
<property name="ContextNamespace">Tracker.SqlServer.CodeFirst</property>
<property name="SourceDatabase">
<connectionString>$(TrackerConnectionString)</connectionString>
<providerType>SchemaExplorer.SqlSchemaProvider,SchemaExplorer.SqlSchemaProvider</providerType>
</property>
<property name="MappingNamespace">Tracker.SqlServer.CodeFirst.Mapping</property>
<property name="EntityNamespace">Tracker.SqlServer.CodeFirst.Entities</property>
<property name="QueryNamespace">Tracker.SqlServer.CodeFirst.Queries</property>
<property name="MockNamespace">Tracker.SqlServer.CodeFirst.Mocks</property>
</propertySet>
</propertySets>
</codeSmith>