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

cSt*_*off 5 c# sql-server entity-framework user-defined-functions entity-framework-6

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

我的应用程序作为一个简化的例子。

数据库

SQL Server 2016 上的表用户

User
Id   (int, Identity, PK)
Name (nvarchar(255)
Run Code Online (Sandbox Code Playgroud)

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

User
Id   (int, Identity, PK)
Name (nvarchar(255)
Run Code Online (Sandbox Code Playgroud)

应用

我使用以下 nuget 包:

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

C#中的相关对象:

Install-Package EntityFramework.CodeFirstStoreFunctions
Run Code Online (Sandbox Code Playgroud)

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

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

    [Column("Name")]
    public string UserName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我的用户配置与列映射:

public DbSet<User> Users { get; set; } 
Run Code Online (Sandbox Code Playgroud)

我的存储过程(TVF):

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");
}
Run Code Online (Sandbox Code Playgroud)

OnModelCreating 方法:

[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);
    }
Run Code Online (Sandbox Code Playgroud)

问题

我的问题是以下几行:

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>());

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

我不想创建以下类型:

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

例外:无效的列名“用户名”。

堆栈跟踪:

   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)
Run Code Online (Sandbox Code Playgroud)

这怎么可能?

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

Yas*_*ngh -1

您好,花了 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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

我的 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; }


        }
Run Code Online (Sandbox Code Playgroud)

这是我的两次迁移。我首先与客户进行测试,然后将用户添加到我的数据库上下文中,这就是我进行 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]");
    
            }
    
    Run Code Online (Sandbox Code Playgroud)

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

       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] ");
            }
    
    Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

  • 这似乎没有回答如何将具有一个名称的列映射到具有另一个名称的 C# 属性的问题。 (2认同)