Skip to content
Merged
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
25 changes: 25 additions & 0 deletions EF6.PG.Tests/EntityFrameworkBasicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -889,5 +889,30 @@ public void Test_enum_composite_key()
Assert.That(result.TestLong, Is.EqualTo(TestLongEnum.Bar));
}
}

[Test]
public void Test_non_composable_function()
{
using (var context = new BloggingContext(ConnectionString))
{
context.Database.Log = Console.Out.WriteLine;

// Add some data and query it back using Stored Function
context.Blogs.Add(new Blog
{
Name = "Some blog1 name",
Posts = new List<Post>()
});
context.SaveChanges();

// Query back
var nameParameter = new ObjectParameter("Name", "blog1");
var blogs = ((IObjectContextAdapter)context).ObjectContext.ExecuteFunction<Blog>("GetBlogsByName2", nameParameter).ToArray();

Assert.AreEqual(1, blogs.Length);
Assert.AreEqual("Some blog1 name", blogs[0].Name);
}
}

}
}
64 changes: 64 additions & 0 deletions EF6.PG.Tests/Support/EntityFrameworkTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public abstract class EntityFrameworkTestBase : TestBase
createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"StoredAddFunction\"(integer, integer) RETURNS integer AS $$ SELECT $1 + $2; $$ LANGUAGE SQL;");
createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"StoredEchoFunction\"(integer) RETURNS integer AS $$ SELECT $1; $$ LANGUAGE SQL;");
createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"GetBlogsByName\"(text) RETURNS TABLE(\"BlogId\" int, \"Name\" text, \"IntComputedValue\" int) as $$ select \"BlogId\", \"Name\", \"IntComputedValue\" from \"dbo\".\"Blogs\" where \"Name\" ilike '%' || $1 || '%' $$ LANGUAGE SQL;");
createSequenceConn.ExecuteNonQuery("CREATE OR REPLACE FUNCTION \"dbo\".\"GetBlogsByName2\"(text) RETURNS TABLE(\"BlogId\" int, \"Name\" text, \"IntComputedValue\" int) as $$ select \"BlogId\", \"Name\", \"IntComputedValue\" from \"dbo\".\"Blogs\" where \"Name\" ilike '%' || $1 || '%' $$ LANGUAGE SQL;");
}
}

Expand Down Expand Up @@ -336,8 +337,71 @@ private static DbCompiledModel CreateModel(NpgsqlConnection connection)
new FunctionImportResultMapping(),
dbModel.ConceptualToStoreMapping));


var getBlogs2Func = EdmFunction.Create(
"GetBlogsByName2",
"BloggingContext",
DataSpace.SSpace,
new EdmFunctionPayload
{
ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion,
Schema = "dbo",
IsComposable = false,
IsNiladic = false,
IsBuiltIn = false,
IsAggregate = false,
StoreFunctionName = "GetBlogsByName2",
ReturnParameters = new[]
{
FunctionParameter.Create("ReturnType1", rowType.GetCollectionType(), ParameterMode.ReturnValue)
},
Parameters = new[]
{
FunctionParameter.Create("Name", stringStoreType, ParameterMode.In)
}
},
null);
dbModel.StoreModel.AddItem(getBlogs2Func);

EdmFunction getBlogs2FuncModel = EdmFunction.Create(
"GetBlogsByName2",
dbModel.ConceptualModel.Container.Name,
DataSpace.CSpace,
new EdmFunctionPayload
{
IsFunctionImport = true,
IsComposable = false,
Parameters = new[]
{
FunctionParameter.Create("Name", stringPrimitiveType, ParameterMode.In)
},
ReturnParameters = new[]
{
FunctionParameter.Create("ReturnType1", modelBlogConceptualType.GetCollectionType(), ParameterMode.ReturnValue)
},
EntitySets = new[]
{
dbModel.ConceptualModel.Container.EntitySets.First(x => x.ElementType == modelBlogConceptualType)
}
},
null);
dbModel.ConceptualModel.Container.AddFunctionImport(getBlogs2FuncModel);

dbModel.ConceptualToStoreMapping.AddFunctionImportMapping(new FunctionImportMappingNonComposable(
getBlogs2FuncModel,
getBlogs2Func,
new FunctionImportResultMapping[] { },
dbModel.ConceptualToStoreMapping));


var compiledModel = dbModel.Compile();
return compiledModel;
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//modelBuilder.Conventions.Add(new FunctionsConvention<MyContext>("dbo"));
base.OnModelCreating(modelBuilder);
}
}
}
3 changes: 3 additions & 0 deletions EF6.PG/NpgsqlServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ internal void TranslateCommandTree(Version serverVersion, DbCommandTree commandT
DbInsertCommandTree insert;
DbUpdateCommandTree update;
DbDeleteCommandTree delete;
DbFunctionCommandTree function;
if ((select = commandTree as DbQueryCommandTree) != null)
sqlGenerator = new SqlSelectGenerator(select);
else if ((insert = commandTree as DbInsertCommandTree) != null)
Expand All @@ -87,6 +88,8 @@ internal void TranslateCommandTree(Version serverVersion, DbCommandTree commandT
sqlGenerator = new SqlUpdateGenerator(update);
else if ((delete = commandTree as DbDeleteCommandTree) != null)
sqlGenerator = new SqlDeleteGenerator(delete);
else if ((function = commandTree as DbFunctionCommandTree) != null)
sqlGenerator = new SqlFunctionGenerator(function);
else
{
// TODO: get a message (unsupported DbCommandTree type)
Expand Down
22 changes: 22 additions & 0 deletions EF6.PG/SqlGenerators/SqlFunctionGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Linq;
using System.Data.Common;
using System.Data.Entity.Core.Common.CommandTrees;

namespace Npgsql.SqlGenerators
{
class SqlFunctionGenerator : SqlBaseGenerator
{
readonly DbFunctionCommandTree _commandTree;

public SqlFunctionGenerator(DbFunctionCommandTree commandTree)
{
_commandTree = commandTree;
}

public override void BuildCommand(DbCommand command)
{
var paramStr = string.Join(",", command.Parameters.OfType<DbParameter>().Select(x => "@" + x.ParameterName).ToArray());
command.CommandText = $"SELECT * FROM { QuoteIdentifier(_commandTree.EdmFunction.Schema) }.{ QuoteIdentifier(_commandTree.EdmFunction.Name) } ({paramStr})";
}
}
}