可以在不使用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手动映射不会有问题。 感谢
答案 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] ");
}
控制台应用程序的代码和输出:我在两个表中手动添加了几条记录来测试
答案 1 :(得分:0)
您不想修改您的对象,也不能修改数据库,但是您仍然可以修改查询。尝试在表值函数的结果中命名列:
return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<User>(
"SELECT Id, Name AS UserName FROM [GetUser](@ID)", idParameter
);