我开始研究EF Code First方法并自动生成数据库。现在我想添加另一个映射到SQL视图而不是表的实体。我使用视图是因为数据(员工信息)位于单独的数据库中。我添加了视图并创建了一个实体,但我不断收到上下文发生变化的错误,我需要手动删除/更新数据库或调用SetInitializer方法。我认为问题在于我的模型定义或我对数据库更改的假设。
基本实体是AppUser(特定于应用程序的用户)和vEmp(查看员工数据)。对AppUser实体来说并不多:
public class AppUser
{
public AppUser()
{
}
[Key]
public int AppUserId { get; set; }
[MaxLength(75)]
public string ntUserDomainId { get; set; }
public bool isActive { get; set; }
public virtual vEmp vEmp { get; set; } // this was commented out at first to generate tables
}
vEmp实体定义如下:
public class vEmp
{
[Key, ForeignKey("AppUser")]
public int AppUserId { get; set; }
public string empName { get; set; }
public string empStatus { get; set; }
public string orgid { get; set; }
public string email { get; set; }
public string ntUserDomainId { get; set; }
public virtual AppUser AppUser { get; set; }
}
为了完成这项工作,我在AppUser中注释掉了导航属性,因此EF会生成表格。然后我取消注释了导航属性并遇到了错误。如果我尝试保留导航属性,那么EF假设一个表并创建" vEmp"作为一张桌子。视图实际上匹配用户NT ID以形成查询结果 - 我们使用Windows身份验证,因此每个用户在AppUser表中都有其NT ID。
据我所知,[Key,ForeignKey(" AppUser")]行应该告诉EF如何管理这种关系。我唯一的猜测是EF希望创建一个交叉引用表来映射两者之间。
我也想知道是否更容易的方法是首先使用DB并使用EF从DB生成模型。我使用自动生成的EDMX与一个类似的vEmp实体和一个AppUser实体做了一个测试项目,并且在我手动建立关联后似乎工作。我们有很多共享数据库,所以使用视图会很常见,我想知道在这种情况下代码是否太难以使用。
更新
我想我有证据证明EF想要创建一张桌子。我玩弄了迁移,这就是我所看到的:
public override void Up()
{
CreateTable(
"dbo.vEmps",
c => new
{
AppUserId = c.Int(nullable: false),
empName = c.String(),
empStatus = c.String(),
orgid = c.String(),
email = c.String(),
ntUserDomainId = c.String(),
})
.PrimaryKey(t => t.AppUserId)
.ForeignKey("dbo.AppUsers", t => t.AppUserId)
.Index(t => t.AppUserId);
}
如果我理解正确的话,EF似乎没有看到名为" vEmp"的SQL视图。而是想要创建一个表。这是否意味着代码首先不适用于SQL视图?
更新2 在尝试使代码首次语法工作失败后,我从数据库中创建了一个edmx模型,因为它存在以测试数据库的第一个进程。我添加了使用我的代码第一个逻辑和我添加的SQL视图创建的所有表。然后我刚刚从表AppUser到视图vEmp创建了与建模工具的关联。建模工具似乎已创建此代码:
<Association Name="vEmpAppUser">
<End Type="JobControlModel.vEmp" Role="vEmp" Multiplicity="1" />
<End Type="JobControlModel.AppUser" Role="AppUser" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="AppUser">
<PropertyRef Name="AppUserId" />
</Principal>
<Dependent Role="vEmp">
<PropertyRef Name="AppUserId" />
</Dependent>
</ReferentialConstraint>
</Association>
其他所有(实体)看起来都一样。我使用控制台应用程序进行了快速测试,看起来效果很好。
我不确定为什么这似乎适用于数据库第一种方法而不是代码优先。实体定义相同。唯一的问题是尝试重新创建数据库首先调用&#34;关联&#34;首先使用代码。
答案 0 :(得分:0)
每次应用程序运行时,EF都会首先获取上次迁移历史记录。如果您使用分析器进行检查,您将获得类似
的内容exec sp_executesql N'SELECT TOP (1)
[Project1].[C1] AS [C1],
[Project1].[MigrationId] AS [MigrationId],
[Project1].[Model] AS [Model],
[Project1].[ProductVersion] AS [ProductVersion]
FROM ( SELECT
[Extent1].[MigrationId] AS [MigrationId],
[Extent1].[Model] AS [Model],
[Extent1].[ProductVersion] AS [ProductVersion],
1 AS [C1]
FROM [dbo].[__MigrationHistory] AS [Extent1]
WHERE [Extent1].[ContextKey] = @p__linq__0
) AS [Project1]
ORDER BY [Project1].[MigrationId] DESC',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'TheProjectName.TheContextName'
go
在迁移历史记录表中,有一种校验和存储有关上下文的信息,它可能会尝试与应用程序中的上下文进行比较。
如果您从EF生成数据库,那么您手动修改了数据库并修改了类/实体中的相同更改,您还需要运行迁移以更新迁移历史记录表。
您可以查看this article以获取进一步说明。
您可以运行空迁移以保存当前的DbContext
状态。
例如,您在数据库中添加了此视图
create view Bars
as
select Id, Name from Foos
您已经创建了一个与视图匹配的类和配置(类型和任何约束)。
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
}
public DbSet<Bar> Bars {get;set;}
modelBuilder.Entity<Bar>().ToTable("Bars");
您需要做的是
添加迁移
PM> Enable-MIgrations
PM> Add-Migration AddBarsView
打开AddBarsView
,清空Up
和Down
方法,然后运行迁移
PM> Update-Database
您的视图已准备好使用。