C# 实体框架来读取列,但阻止其更新
给定一个包含历史数据但不再填充的列的数据库表,在Entity Framework中是否有方法读取该列,但在使用相同的模型对象时防止其更新 例如,我有一个对象C# 实体框架来读取列,但阻止其更新,c#,entity-framework,C#,Entity Framework,给定一个包含历史数据但不再填充的列的数据库表,在Entity Framework中是否有方法读取该列,但在使用相同的模型对象时防止其更新 例如,我有一个对象 public class MyObject { public string CurrentDataColumnName { get; set; } public string HistoricDataColumnName { get; set; } } 从文档来看,我不相信我可以做以下任何一项,因为这将停止EF读取数据并保
public class MyObject
{
public string CurrentDataColumnName { get; set; }
public string HistoricDataColumnName { get; set; }
}
从文档来看,我不相信我可以做以下任何一项,因为这将停止EF读取数据并保持数据
(1) 使用以下属性装饰HistoricDataColumnName
属性
[NotMapped]
(2) 为MyObject
Ignore(x => x.HistoricDataColumnName)
您可以将该列标记为计算列,以防止实体框架更新/插入该列
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string HistoricDataColumnName { get; set; }
数据库的一个重要特性是能够计算 财产。如果要将代码优先类映射到 包含计算列,您不希望实体框架尝试 更新这些列。但您确实希望EF从中返回这些值 插入或更新数据后,数据库将被删除。你可以使用 DatabaseGenerated注释来标记类中的那些属性 以及计算的枚举。其他枚举为None和Identity
对于just on column,这是过分的,但一般来说,您可以在DbContext中重写SaveChanges,以便对更改有更多的控制 在您的模型中:
public override int SaveChanges()
{
var modifiedEntries = base.ChangeTracker.Entries<MyObject>()
.Where(e => e.State == EntityState.Modified).ToList();
foreach (var entry in modifiedEntries)
{
// Overwriting with the same value doesn't count as change.
entry.CurrentValues["HistoricDataColumnName"] = entry.OriginalValues["HistoricDataColumnName"];
}
return base.SaveChanges();
}
public override int SaveChanges()
{
var modifiedEntries=base.ChangeTracker.Entries()
.Where(e=>e.State==EntityState.Modified).ToList();
foreach(修改项中的var条目)
{
//使用相同的值进行覆盖不算作更改。
entry.CurrentValues[“HistoricalCDataColumnName”]=entry.OriginalValues[“HistoricalCDataColumnName”];
}
返回base.SaveChanges();
}
但也可以通过将状态从“已修改”更改为“未更改”来撤消所有修改
--更新--
有一件事让我担心。一旦开发人员拥有访问数据库的凭据,您就不能阻止他们做您不想做的事情。他们可以创建自己的模型或直接查询数据库
因此,我认为最重要的事情是在客户机的数据库中将字段设置为只读。但您可能无法锁定一列
即使这不是问题,我认为(对于设计而言)最好将所有历史数据移动到其他表中。使授予只读访问权限变得容易。您可以将这些表映射为1:1。使用实体框架,您仍然可以非常轻松地访问历史信息
但在这种情况下,您将不会遇到现在的问题,并将为您提供其他选项以防止其他人更改历史信息。代码方面,您可以简单地将setter设置为protected。EF使用反射来具体化您的模型。我认为现在隐藏的setter也向其他程序员表明,该字段不应该再被修改了 还添加了一个[Observe]-属性,其中包含更多信息,即为什么不能再从公共设置属性。
internal
access修饰符
您可以将setter更改为internal
public class MyObject
{
public string CurrentDataColumnName { get; internal set; }
public string HistoricDataColumnName { get; internal set; }
}
public class MyObject
{
public string CurrentDataColumnName { get; protected internal set; }
public string HistoricDataColumnName { get; protected internal set; }
}
这不会像其他选项那样施加太多限制,但根据您的需求,这可能非常有用
受保护
访问修改器
这可能是将EF中的属性设置为“只读”的最常见用法。它本质上只允许构造函数访问setter(和类中的其他方法,以及从类派生的类)
我认为受保护的
是您要找的
受保护的内部
访问修饰符
您也可以这样将两者结合起来,使其受保护
或内部
public class MyObject
{
public string CurrentDataColumnName { get; internal set; }
public string HistoricDataColumnName { get; internal set; }
}
public class MyObject
{
public string CurrentDataColumnName { get; protected internal set; }
public string HistoricDataColumnName { get; protected internal set; }
}
进修课程
成员只能在同一程序集中访问内部
成员可以在其类内以及通过派生类实例进行访问受保护的
- 可以从当前程序集或从包含类派生的类型访问受保护的内部成员
CREATE TRIGGER MyTable_UpdateTriggerPreventChange
ON dbo.Table1
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
if update(HistoricDataColumnName)
begin
raiserror (50001, 16, 10)
end
END
选项2-忽略更改
CREATE TRIGGER MyTable_UpdateTriggerIgnore
ON dbo.Table1
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
update dbo.Table1 set HistoricDataColumnName=inserted.HistoricDataColumnName
from inserted
where inserted.Id = dbo.Table1.Id
END
如果需要的话,您当然可以对insert执行类似的操作
或者使用“throw”
ALTER TRIGGER MyTable_UpdateTriggerPreventChange
ON dbo.Table1
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
if update(HistoricDataColumnName)
begin
throw 50002, 'You can''t change the historic data', 1
end
END
无论哪种方式,都会引发异常。这是使用LinqPad
您只需使用检查特定实体属性是否被修改,这样您仍然可以读取、插入和删除数据:
var item = context.MyObjects.Find(id);
item.CurrentDataColumnName = "ChangedCurrentDataColumnName";
item.HistoricDataColumnName = "ChangedHistoricDataColumnName";
context.Entry(item).Property(c => c.HistoricDataColumnName).IsModified = false;
context.SaveChanges();
通过使用IsModified=false
将HistoricDataColumnName
属性排除在更新之外,因此数据库中不会更新HistoricDataColumnName
列,但会更新其他属性
对于已修改的属性,将此值设置为false将通过将当前值设置为原始值来恢复更改。如果结果是该实体的任何属性都未标记为已修改,则该实体将标记为未更改。对于已添加、未更改或已删除实体的属性,将此值设置为false是不允许的
检查以下答案作为补充说明。这也可能有帮助:
问题是关于EF 6,但这在具有该属性的EF Core中很容易实现。感谢EF核心回购协议中的ajcvickers
modelBuilder
.实体()
.属性(e=>e.Bar)
.ValueGeneratedOnAddOrUpdate()
.Metadata.IsStoreGeneratedAlways=true;
为什么要在EF中这样做?Wh
public class MyObject
{
public string CurrentDataColumnName { get; private set; }
public string HistoricDataColumnName { get; private set; }
}