diff --git a/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs b/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs index d92252da4f5..bf46846261c 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs @@ -44,7 +44,7 @@ public StoreFunction([NotNull] DbFunction dbFunction, [NotNull] RelationalModel /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual SortedDictionary DbFunctions { get; } + public virtual SortedDictionary DbFunctions { get; } /// public virtual bool IsBuiltIn { get; } diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 24215d82b11..c978f2f8920 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -129,8 +129,7 @@ protected override Expression VisitExtension(Expression extensionExpression) } var entityType = tableValuedFunctionQueryRootExpression.EntityType; - var alias = (entityType.GetViewOrTableMappings().SingleOrDefault()?.Table.Name - ?? entityType.ShortName()).Substring(0, 1).ToLower(); + var alias = entityType.ShortName().Substring(0, 1).ToLower(); var translation = new TableValuedFunctionExpression(alias, function.Schema, function.Name, arguments); var queryExpression = _sqlExpressionFactory.Select(entityType, translation); diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 3853994b059..aa78003cefb 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -119,8 +119,34 @@ internal SelectExpression(SqlExpression projection) } internal SelectExpression(IEntityType entityType) - : this(entityType, new TableExpression(entityType.GetViewOrTableMappings().Single().Table)) + : base(null) { + TableExpressionBase tableExpression; + var functionMappings = entityType.GetFunctionMappings(); + if (functionMappings.Any()) + { + var storeFunction = functionMappings.Single(e => e.IsDefaultFunctionMapping).Table; + var alias = entityType.ShortName().Substring(0, 1).ToLower(); + tableExpression = new TableValuedFunctionExpression( + alias, storeFunction.Schema, storeFunction.Name, Array.Empty()); + } + else + { + tableExpression = new TableExpression(entityType.GetViewOrTableMappings().Single().Table); + } + + _tables.Add(tableExpression); + + var entityProjection = new EntityProjectionExpression(entityType, tableExpression, false); + _projectionMapping[new ProjectionMember()] = entityProjection; + + if (entityType.FindPrimaryKey() != null) + { + foreach (var property in entityType.FindPrimaryKey().Properties) + { + _identifier.Add((entityProjection.BindProperty(property), property.GetKeyValueComparer())); + } + } } internal SelectExpression(IEntityType entityType, TableExpressionBase tableExpression) diff --git a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs index 89a5bad1923..96a08e4a9f1 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/UdfDbFunctionTestBase.cs @@ -73,6 +73,31 @@ public class Address public Customer Customer { get; set; } } + public class OrderByYear + { + public int? CustomerId { get; set; } + public int? Count { get; set; } + public int? Year { get; set; } + } + + public class MultProductOrders + { + public int OrderId { get; set; } + + public Customer Customer { get; set; } + public int CustomerId { get; set; } + + public DateTime OrderDate { get; set; } + } + + public class TopSellingProduct + { + public Product Product { get; set; } + public int? ProductId { get; set; } + + public int? AmountSold { get; set; } + } + protected class UDFSqlContext : PoolableDbContext { #region DbSets @@ -152,36 +177,11 @@ public int AddValues(Expression> a, int b) #region Queryable Functions - public class OrderByYear - { - public int? CustomerId { get; set; } - public int? Count { get; set; } - public int? Year { get; set; } - } - - public class MultProductOrders - { - public int OrderId { get; set; } - - public Customer Customer { get; set; } - public int CustomerId { get; set; } - - public DateTime OrderDate { get; set; } - } - public IQueryable GetCustomerOrderCountByYear(int customerId) { return FromExpression(() => GetCustomerOrderCountByYear(customerId)); } - public class TopSellingProduct - { - public Product Product { get; set; } - public int? ProductId { get; set; } - - public int? AmountSold { get; set; } - } - public IQueryable GetTopTwoSellingProducts() { return FromExpression(() => GetTopTwoSellingProducts()); @@ -255,7 +255,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(GetOrdersWithMultipleProducts))); modelBuilder.Entity().HasNoKey(); - modelBuilder.Entity().HasNoKey(); + modelBuilder.Entity().HasNoKey().ToFunction("GetTopTwoSellingProducts"); } } @@ -1905,6 +1905,23 @@ orderby c.Id } } + [ConditionalFact] + public virtual void DbSet_mapped_to_function() + { + using (var context = CreateContext()) + { + var products = (from t in context.Set() + orderby t.ProductId + select t).ToList(); + + Assert.Equal(2, products.Count); + Assert.Equal(3, products[0].ProductId); + Assert.Equal(249, products[0].AmountSold); + Assert.Equal(4, products[1].ProductId); + Assert.Equal(184, products[1].AmountSold); + } + } + #endregion private void AssertTranslationFailed(Action testCode) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs index c3cf4752b2a..9d522fb49f4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs @@ -752,6 +752,16 @@ FROM [dbo].[GetOrdersWithMultipleProducts]([c].[Id]) AS [m] ORDER BY [c].[Id], [t].[OrderId], [t].[Id]"); } + public override void DbSet_mapped_to_function() + { + base.DbSet_mapped_to_function(); + + AssertSql( + @"SELECT [t].[AmountSold], [t].[ProductId] +FROM [dbo].[GetTopTwoSellingProducts]() AS [t] +ORDER BY [t].[ProductId]"); + } + #endregion public class SqlServer : UdfFixtureBase