如何使用不同的列名映射EF6 Code First中的表值函数?

时间:2017-01-23 10:08:00

标签: c# sql-server entity-framework entity-framework-6 user-defined-functions

可以在不使用edmx的情况下在EF6中映射表值函数吗?问题是数据库中的列的命名方式与应用程序中的列不同。

我的申请是一个简化的例子。

数据库

SQL Server 2016上的表用户

User
Id   (int, Identity, PK)
Name (nvarchar(255)

SQL Server 2016上的表值函数 GetUser(id)

CREATE FUNCTION [dbo].[GetUser](@ID int)
RETURNS TABLE
AS
RETURN 
(
    SELECT *
    FROM dbo.User
    WHERE Id = @ID
);

应用

我使用以下nuget包:

Install-Package EntityFramework.CodeFirstStoreFunctions

C#中的相关对象:

public class User
{
    [Key]
    public int Id { get; set; }

    [Column("Name")]
    public string UserName { get; set; }
}

我的DbContext类中设置的数据库:

public DbSet<User> Users { get; set; } 

我的用户配置列映射:

private void ConfigureUser(EntityTypeConfiguration<User> configuration)
{
    configuration.ToTable("User");

    configuration.HasKey(user => user.Id);
    configuration.Property(user => user.Id).HasColumnName("Id").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    configuration.Property(user => user.UserName).HasColumnName("Name");
}

我的存储过程(TVF):

[DbFunction(nameof(ApplicationDb), "GetUser")]
    public virtual IQueryable<User> GetUser(Nullable<int> id)
    {   
        var idParameter = id.HasValue ?
        new ObjectParameter("ID", id) :
        new ObjectParameter("ID", typeof(int));
        return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<User>("[GetUser](@ID)", idParameter);
    }

OnModelCreating方法:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{
    if (modelBuilder == null)
    {
        throw new ArgumentNullException(nameof(modelBuilder));
    }

    base.OnModelCreating(modelBuilder);

    modelBuilder.Conventions.Add(new CodeFirstStoreFunctions.FunctionsConvention<MyDbContext>("dbo"));

    this.ConfigureUser(modelBuilder.Entity<User>());

    // ...
}

问题

我的问题是以下一行:

return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<User>("[GetUser](@ID)", idParameter);

表值函数需要一个类型为table column的类型。正确的拼写是必要的,所以我需要一个映射。它是一个外部数据库,因此我无法更改SQL Server中的列。该项目不允许使用edmx模型。

我不想创建以下类型:

public class GetUserResult
{
    public int Id { get; set; }

    public string Name { get; set; }
}

例外:列名称无效&#39;用户名&#39;。

堆栈跟踪:

   System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
   System.Data.SqlClient.SqlDataReader.get_MetaData()
   System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext)
   System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)

怎么可能?

表列名称和实体属性名称之间的centrales手动映射不会有问题。 感谢

2 个答案:

答案 0 :(得分:0)

嗨,这需要30分钟,但我能够将结果推向类型用户。我按照这里提到的相同步骤在GetUser DBFunction中进行了一次更改。以下是我的所有课程和结果。我希望这就是你想要的。

首先是我的上下文类:请检查getuser函数我添加了gettype.Name函数,该函数在你的代码中不存在。

public class SampleDbContext : DbContext
        {
            public SampleDbContext()
                : base("name=SampleDBConnection")
            {
                this.Configuration.LazyLoadingEnabled = true;
                this.Configuration.ProxyCreationEnabled = true;
            }

            public DbSet<Customer> Customers { get; set; }
            public DbSet<User> Users { get; set; }


            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {

                modelBuilder.Conventions.Add(new FunctionsConvention<SampleDbContext>("dbo"));



            }

            [DbFunction("SampleDbContext", "CustomersByZipCode")]
            public IQueryable<Customer> CustomersByZipCode(string zipCode)
            {
                var zipCodeParameter = zipCode != null ?
                    new ObjectParameter("ZipCode", zipCode) :
                    new ObjectParameter("ZipCode", typeof(string));

                return ((IObjectContextAdapter)this).ObjectContext
                    .CreateQuery<Customer>(
                        string.Format("[{0}].{1}", GetType().Name,
                            "[CustomersByZipCode](@ZipCode)"), zipCodeParameter);
            }


            [DbFunction("SampleDbContext", "GetUser")]
            public virtual IQueryable<User> GetUser(int? id)
            {
                var idParameter = id.HasValue ?
                new ObjectParameter("ID", id) :
                new ObjectParameter("ID", typeof(int));
                return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<User>(
                        string.Format("[{0}].{1}", GetType().Name,
                            "[GetUser](@ID)"), idParameter);
            }
        }

我的EF模型类

        public class Customer
        {
            public int Id { get; set; }

            public string Name { get; set; }

            public string ZipCode { get; set; }
        }

        public class User
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public int Id { get; set; }

            [Column("Name")]
            public string Name { get; set; }


        }

这是我的两次迁移。我首先与客户测试,然后将用户添加到我的数据库上下文中,这就是为什么我有2次迁移。

  • 在上下文中添加客户时的迁移

     public override void Up()
            {
                CreateTable(
                    "dbo.Customers",
                    c => new
                        {
                            Id = c.Int(nullable: false, identity: true),
                            Name = c.String(),
                            ZipCode = c.String(),
                        })
                    .PrimaryKey(t => t.Id);
                Sql(@"CREATE FUNCTION [dbo].[CustomersByZipCode](@ZipCode nchar(5)) 
                RETURNS TABLE 
                RETURN 
                SELECT [Id], [Name], [ZipCode] 
                FROM [dbo].[Customers]  
                WHERE [ZipCode] = @ZipCode");
    
            }
    
            public override void Down()
            {
                DropTable("dbo.Customers");
                Sql(@"Drop FUNCTION [dbo].[CustomersByZipCode]");
    
            }
    

    在上下文中添加用户时的第二次迁移

       public override void Up()
            {
                CreateTable(
                    "dbo.Users",
                    c => new
                        {
                            Id = c.Int(nullable: false, identity: true),
                            Name = c.String(),
                        })
                    .PrimaryKey(t => t.Id);
                Sql(@" CREATE FUNCTION [dbo].[GetUser](@ID int)
                    RETURNS TABLE
                    AS
                    RETURN 
                    (
                        SELECT *
                        FROM dbo.Users
                        WHERE Id = @ID
                    )");
    
            }
    
            public override void Down()
            {
                DropTable("dbo.Users");
                Sql(@" drop FUNCTION [dbo].[GetUser] ");
            }
    

控制台应用程序的代码和输出:我在两个表中手动添加了几条记录来测试

enter image description here

答案 1 :(得分:0)

您不想修改您的对象,也不能修改数据库,但是您仍然可以修改查询。尝试在表值函数的结果中命名列:

return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<User>(
    "SELECT Id, Name AS UserName FROM [GetUser](@ID)", idParameter
);