EntityFramework拒绝忘记旧列

11

我正在使用EntityFramework 6.1.3,基于数据库的方法。 我现在希望我之前选择了代码优先...

我有一些表格的数据库。我以前建立了我的edmx。然后,我更改了一些列的类型并添加了一些列。例如,将一个bit列更改为一个int列。

我尝试使用右键单击->从数据库更新模型从数据库更新我的模型。

无论我做什么,EF似乎始终只会选择我创建edmx时的数据库状态。 我尝试过的事情:

  1. 重新打开Visual Studio
  2. 重建项目
  3. 删除并重新添加实体(这是大多数人应该有效的方法)
  4. 在关闭Visual Studio的情况下,在整个项目中搜索xml或C#的文本引用到我的列,并替换它们。(这起初似乎有效,但如果我再次尝试从数据库更新,则会覆盖它们)
  5. 重新启动SQL服务
  6. 重新启动机器
  7. 对所有.tt文件运行“Run Custom Tool”(不应该有区别,但是怎么样呢)

当我右键单击实体并选择“表映射”时,它总是显示旧的bit列在左侧。

mapping problem

这是我的数据库表设计:

sql table design

值得注意的是,实体正在运行一个视图,而不是直接从表中获取。 但是,正如我所调查的问题一样,该视图只是选择*表,并且我已通过PowerShell确认视图返回的类型为int:

PS> $conn = new-object data.sqlclient.SqlConnection("data source=localhost;initial catalog=dbname;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework")
PS> $conn.open()
PS> $query = $conn.createcommand()
PS> $query.commandtext = "select * from [dbname].[LocalCustom].[viewname]"
PS> $reader = $query.executereader()
PS> $reader.getschematable().rows[19]

ColumnName                      : ActiveMember
ColumnOrdinal                   : 19
ColumnSize                      : 4
NumericPrecision                : 10
NumericScale                    : 255
IsUnique                        : False
IsKey                           :
BaseServerName                  :
BaseCatalogName                 :
BaseColumnName                  : ActiveMember
BaseSchemaName                  :
BaseTableName                   :
DataType                        : System.Int32
AllowDBNull                     : False
ProviderType                    : 8
IsAliased                       :
IsExpression                    :
IsIdentity                      : False
IsAutoIncrement                 : False
IsRowVersion                    : False
IsHidden                        :
IsLong                          : False
IsReadOnly                      : False
ProviderSpecificDataType        : System.Data.SqlTypes.SqlInt32
DataTypeName                    : int
XmlSchemaCollectionDatabase     :
XmlSchemaCollectionOwningSchema :
XmlSchemaCollectionName         :
UdtAssemblyQualifiedName        :
NonVersionedProviderType        : 8
IsColumnSet                     : False

我现在的主要问题是... EF怎么知道它曾经是一个bit类型?它把这些数据存储在哪里? 当然,我也想知道如何在UI中正确更新模型,而不必删除和重新添加实体或者其他需要做的事情以进行更新。

我对EF感到相当沮丧 :(


删除您的 edmx 并创建一个新的。您最终可以手动修改 CSDL \ SSDL - Amit Kumar Ghosh
1
EDMX 工具在重命名或删除列时非常挑剔。请在 XML 中找到该列并将其删除。 - CodeCaster
@hopeless: 我同意。现在你可以执行相同的操作,其中你的实体映射到表的“SELECT *”视图。当表更新时,EF不会更改实体,因为视图已经更新。 - xum59
1
@hopeless:我刚刚仔细阅读了一下 ;) “值得注意的是,实体是基于视图而不是直接基于表进行操作的”。一些专家可能会缩小搜索范围,但我认为这与主数据库中的_sys_对象有关。 - xum59
1
@Nacht:我不同意,以数据库为先的方法是正确的。正如我更新的答案所证明的那样,你的问题出在数据库层面上。完全没有必要删除/重建实体。 - xum59
显示剩余8条评论
2个回答

15

在使用Entity Framework database-first时,我经常会遇到这种情况。当你从数据库中进行了一些更改以更新EF时,你需要:

  1. 删除.edmx中已修改的表
  2. 通过右键单击 -> 从数据库更新模型 -> 选中该表,重新添加该表到你的.edmx中

1
这是我目前所做的,但在我的大型模式中速度非常慢,希望能有更快的方法。 - Marquez

5

好的,只需编辑/更新您的视图即可,即使没有更改。EF不会更新未修改的项目。

只要您没有更改/更新视图但更改了基础表格,EF就无法检测到需要更改任何内容。我确信这种行为旨在防止每次进行完全重建模型。


由于OP希望了解“为什么”,因此我进行了一些测试。

首先,我创建了一个表和一个“SELECT *”视图:

create table TableToChange(rowKey bigint IDENTITY(1,1) not null, myBitFlag bit null, myIntFlag int)
go
create view SelectStarOnChangingTable as SELECT * FROM TableToChange
go

然后我使用以下方式对创建的sys对象和列进行了一些检查:

select * 
from sys.all_objects AO 
    join sys.all_columns AC on AC.object_id=AO.object_id
where AO.object_id in (--insert your objet_ids here--)

对表格对象进行一些小的修改:

alter table TableToChange
drop column myBitFlag
go
alter table TableToChange
add myBitFlag int
go

如果您再次运行模式查询,您会注意到更新列在USER_TABLE行和VIEW行中不具有相同的类型(56 vs 104)。
即使没有更改,修改SelectStarOnChangingTable视图也会强制SQL Server进行更新。
现在我们找到了罪魁祸首:SQL Server存储视图的列类型,阻止EF更新其模型,即使实体从头开始重建。

我会尝试这样做,但它并没有回答我的问题,它是如何知道先前表格的样子的! - Nacht
1
EDMX 文件包含一个 Store Model,用于描述数据库对象。然后将它们映射到实体。如果 EF 没有检测到底层数据库对象中的任何更改,则 Store Model 不会得到更新,最终你将得到“旧”的类型。我不是 EF 的厨师专家,因此我不会在这个方向上进行进一步扩展 ;) - xum59
好像已经成功了1!!!!!!你得到了+1,但是听起来你在说“ActiveMember”字段以前的状态是存储在edmx文件中的“bit”,但我检查了一下,整个文件中没有任何“bit”这个词的实例。事实上,虽然我的实体被删除了,但它并没有包含任何关于它们的信息。 - Nacht
是的,这有一定的道理。但同时,如果删除了所有对旧数据库模式的引用,仍然看到它出现可能会让人感到疯狂。我相信最终答案会很无聊,但我想知道如何在将来强制刷新。如果未来一两天内没有人回答这个问题,我可能会将您的答案标记为正确。 - Nacht
我所查询的是 select * from sys.all_objects AO join sys.all_columns AC on AC.object_id = AO.object_id where AC.name = 'ActiveEmployer',因为我不知道如何找到 object_ids。 - Nacht
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接