GVKun编程网logo

实体框架/ SQL2008-如何自动更新实体的LastModified字段?

20

对于实体框架/SQL2008-如何自动更新实体的LastModified字段?感兴趣的读者,本文将提供您所需要的所有信息,并且为您提供关于@CreatedBy和@LastModifiedBy设置实际实

对于实体框架/ SQL2008-如何自动更新实体的LastModified字段?感兴趣的读者,本文将提供您所需要的所有信息,并且为您提供关于@CreatedBy和@LastModifiedBy设置实际实体而不是id、android – 使用SQLite触发器来更新“LastModified”字段、asp.net-mvc – 如何更新实体框架连接字符串、asp.net-mvc – 实体框架更新实体以及子实体(必要时添加/更新)的宝贵知识。

本文目录一览:

实体框架/ SQL2008-如何自动更新实体的LastModified字段?

实体框架/ SQL2008-如何自动更新实体的LastModified字段?

如果我有以下实体:

public class PocoWithDates{   public string PocoName { get; set; }   public DateTime CreatedOn { get; set; }   public DateTime LastModified { get; set; }}

对应于具有相同名称/属性的SQL Server 2008表…

我如何 自动

  1. 将记录的CreatedOn / LastModified字段设置为 现在 (执行INSERT时)
  2. 将记录的LastModified字段设置为 现在 (执行UPDATE时)

当我 自动 说时,我的意思是我希望能够做到这一点:

poco.Name = "Changing the name";repository.Save();

不是这个:

poco.Name = "Changing the name";poco.LastModified = DateTime.Now;repository.Save();

在幕后,“某物”应自动更新日期时间字段。那是什么“东西”?

我正在使用Entity Framework 4.0-EF是否可以为我自动执行此操作?(也许是EDMX中的特殊设置?)

从SQL Server方面,我可以使用 DefaultValue ,但这仅适用于 INSERT (而不是UPDATE)。

同样,我可以在POCO上使用构造函数设置默认值,但是同样,这仅在实例化对象时有效。

当然,我 可以 使用触发器,但这并不理想。

因为我使用的是Entity Framework,所以我可以在这里挂入 SavingChanges 事件并更新日期字段,但是问题是我需要“意识到”
POCO(目前,我的存储库已使用泛型实现)。我将需要进行某种OO欺骗(例如使我的POCO实现一个接口,并在该接口上调用一个方法)。我对此并不反对,但是如果我必须这样做,我宁愿手动设置字段。

我基本上是在寻找SQL Server 2008或Entity Framework 4.0解决方案。(或智能.NET方式)

有任何想法吗?

编辑

感谢@marc_s的回答,但是我选择了一个对我的场景更好的解决方案。

答案1

小编典典

由于我在我的控制器(即使用ASP.NET MVC)和我的存储库之间进行中介的服务层,因此我决定在此处自动设置字段。

另外,我的POCO没有关系/抽象,它们是完全独立的。我想保持这种方式,而不要标记任何虚拟属性或创建基类。

所以我创建了一个接口IAutoGenerateDateFields:

public interface IAutoGenerateDateFields{   DateTime LastModified { get;set; }   DateTime CreatedOn { get;set; }}

对于任何希望自动生成这些字段的POCO,我都实现了此接口。

使用我的问题中的示例:

public class PocoWithDates : IAutoGenerateDateFields{   public string PocoName { get; set; }   public DateTime CreatedOn { get; set; }   public DateTime LastModified { get; set; }}

现在,在我的服务层中,检查具体对象是否实现了接口:

public void Add(SomePoco poco){   var autoDateFieldsPoco = poco as IAutoGenerateDateFields; // returns null if it''s not.   if (autoDateFieldsPoco != null) // if it implements interface   {      autoDateFieldsPoco.LastModified = DateTime.Now;      autoDateFieldsPoco.CreatedOn = DateTime.Now;   }   // ..go on about other persistence work.}

稍后,我可能会在“添加到帮助器/扩展程序”方法中破坏该代码。

但是我认为这对我的情况来说是一个不错的解决方案,因为我不想在“保存”中使用虚拟机(因为我正在使用工作单元,存储库和Pure POCO),并且不想使用触发器。

如果您有任何想法/建议,请告诉我。

@CreatedBy和@LastModifiedBy设置实际实体而不是id

@CreatedBy和@LastModifiedBy设置实际实体而不是id

我有一个看起来像的实体:

@Audited@Data@MappedSuperclass@EntityListeners(AuditingEntityListener.class)public abstract class BaseEntity {  public static final long UNSAVED = 0;  @Id  @GeneratedValue  private long id;  @CreatedDate  @Column(name = "created_at", updatable = false)  private ZonedDateTime createdAt;  @CreatedBy  @OneToOne(fetch = FetchType.EAGER)  @JoinColumn(name = "created_by")  private User createdBy;  @LastModifiedDate  private ZonedDateTime updatedAt;  @OneToOne(fetch = FetchType.EAGER)  @JoinColumn(name = "updated_by")  @LastModifiedBy  private User updatedBy;}

我想要@LastModifiedBy和@CreatedBy,以便它们设置相应的用户。但是,当我尝试保存实体时,出现异常:

java.lang.ClassCastException: Cannot cast java.lang.Long to com.intranet.users.Users

因此在我看来,它尝试设置的不是实际用户,而是ID。有什么方法可以使spring set成为实体上的实际User,而不仅仅是其id?

谢谢

答案1

小编典典

这似乎可以由文档直接回答:

如果您使用@CreatedBy或@LastModifiedBy,则审计基础结构需要以某种方式了解当前主体。为此,我们提供了AuditorAware
SPI接口,您必须实现该接口以告知基础结构与应用程序交互的当前用户或系统是谁。通用类型T定义必须使用@CreatedBy或@LastModifiedBy注释的属性的类型。

以下示例显示了使用Spring Security的Authentication对象的接口的实现:

例子104.基于Spring Security的AuditorAware的实现

class SpringSecurityAuditorAware implements AuditorAware<User> {  public Optional<User> getCurrentAuditor() {    return Optional.ofNullable(SecurityContextHolder.getContext())        .map(SecurityContext::getAuthentication)        .filter(Authentication::isAuthenticated)        .map(Authentication::getPrincipal)        .map(User.class::cast);    }}

该实现访问Spring
Security提供的Authentication对象,并查找您在UserDetailsS​​ervice实现中创建的自定义UserDetails实例。我们在这里假设您通过UserDetails实现公开域用户,但是根据找到的身份验证,您还可以从任何地方查找它。

android – 使用SQLite触发器来更新“LastModified”字段

android – 使用SQLite触发器来更新“LastModified”字段

这可能更多的是一个设计问题,但是在这里.我正在编写一个 Android应用程序,该应用程序使用与MysqL数据库同步的本地sqlite数据库(具有多个表).我只想更新数据库中的修改行.为了做到这一点,我在每一行中添加一列“last_modified”,这些列表示该行被添加/更新/替换/等等的时间.

我是数据库操作的新手,但是我看到触发器可能是最好的方法.我有几个有关触发器,sqlite和Android的问题.

我已经阅读了这个链接:on update current_timestamp with SQLite它基本上说我正在使用正确的方法.我的问题是:

>我应该在哪里放db.execsql(“CREATE TRIGGER …”)语句?在创建表之前或之后?
>我可以为我的数据库中的每个表使用相同的Trigger吗?即,触发器可以自动检测哪些表和行正被更新/插入/替换等.并通知设置该行的“last_modified”字段,或者我必须为每个表创建一个单独的触发器?
>由于我对数据库操作非常新鲜,您能否提供一个执行上述行为的示例Android Trigger语句,或者为示例提供资源?

或者如果触发器是一个坏主意,有没有更好的选择?

谢谢.

解决方法

一个简短而甜美的答案给你:

>之后,所以触发器有一个有效的表来引用.
>您需要为您想要影响的每个表/列组合执行CREATE TRIGGER.数据库不会假设,因为另一个表有一个last_modified列,你希望这个行为相同…
>您的链接中的触发器是可执行的(我自己使用它),只需更改表/列名称.

最后,使用这样的触发器是我知道维护last_modified或last_accessed时间戳的最简单的方法.

我的触发器(以java形式):

private static final String UPDATE_TIME_TRIGGER =
    "CREATE TRIGGER update_time_trigger" + 
    "  AFTER UPDATE ON " + TABLE_NAME + " FOR EACH ROW" +
    "  BEGIN " +
        "UPDATE " + TABLE_NAME + 
        "  SET " + TIME + " = current_timestamp" +
        "  WHERE " + ID + " = old." + ID + ";" +
    "  END";

加成

根据SQLite website,您需要为每种类型的操作创建一个触发器.换句话说,你不能使用:

CREATE TRIGGER trigger_name 
  AFTER UPDATE,INSERT ...

从您最近的评论中,您可能已经想出了处理INSERT语句的最佳方法:

CREATE TABLE foo (
  _id INTEGER PRIMARY KEY,last_modified TIMESTAMP NOT NULL DEFAULT current_timstamp);

在此表中,您不需要为INSERT语句创建时间戳触发器,因为已经完成. (有趣的是:INTEGER PRIMARY KEY隐式地将AUTOINCREMENT NOT NULL以及默认的增量值添加到我们的_id列.)

asp.net-mvc – 如何更新实体框架连接字符串

asp.net-mvc – 如何更新实体框架连接字符串

我修改了web.config连接字符串.但是,在调试期间,我仍然看到旧的连接字符串.

所以,我已经评论(并删除)了旧的连接字符串,但是,并通过服务器资源管理器添加了一个新的连接资源.在服务器资源管理器的左侧面板上通过向导测试连接时,它表示已连接.

在关注此向导后,当我访问web.config时,我没有看到新的连接字符串.

问题:我怀疑,我没有按照添加连接字符串的步骤 –
如何从设计器添加或更新连接字符串(在设计器属性面板中,编辑是灰色的,输出类型是构建到程序集,右键单击只提供添加实体等选项,删除字符串和运行应用程序,不提示连接字符串向导)

下面是字符串 –

<connectionStrings><add name="MaintRecordsDB_v1" connectionString="Metadata=res://*/Models.DB.Model.csdl|res://*/Models.DB.Model.ssdl|res://*/Models.DB.Model.msl;provider=System.Data.sqlClient;provider connection string=&quot;data source=xxx.sample.net;initial catalog=MainDB;user id=maintRoot;password=hidden;multipleactiveresultsets=True;application name=EntityFramework&quot;" providerName="System.Data.EntityClient" /><add name="MainDBentities" connectionString="Metadata=res://*/Models.DB.Model.csdl|res://*/Models.DB.Model.ssdl|res://*/Models.DB.Model.msl;provider=System.Data.sqlClient;provider connection string=&quot;data source=windflower.arvixe.com;initial catalog=MX_Dashboard;user id=maintRoot;password=hidden;multipleactiveresultsets=True;application name=EntityFramework&quot;" providerName="System.Data.EntityClient" /></connectionStrings>

编辑问题2:如何为例如添加另一个EF连接字符串MaintDB2使用设计器,我在哪里手动更新.

解决方法

在不知道你的上下文类是什么样的情况下,比如你的DbContext类,如果它是生成的并且假设它是部分的,你可以尝试使用一个以命名连接字符串作为参数的构造函数向它添加另一个部分类部分.

首先在app.config / web.config中添加一个命名连接:

<connectionStrings>
...
<add name="MyOtherConnection" connectionString="Metadata=res://*/blahblahblah;provider=System.Data.sqlClient;provider connection string=&quot;Data Source=ABunchOfOtherStuff;"
  providerName="System.Data.EntityClient" />
</connectionStrings>

然后使用构造函数在另一个(非生成的)文件中添加匹配的部分类以获取连接字符串名称:

// the class name must match the name of your existing context
public partial class MyContext : DbContext
{
    public MyContext(string connectionStringName) : base("name=" + connectionStringName)
    {
    }
}

然后通过传递连接字符串的名称来使用您的上下文,由一些无用的代码演示:

// ...
using (var context = new MyContext("MyOtherConnection"))
{
   var id = 1;
   var collection = context.MyEntities.Where(a => a.ID == id).ToList();
}

asp.net-mvc – 实体框架更新实体以及子实体(必要时添加/更新)

asp.net-mvc – 实体框架更新实体以及子实体(必要时添加/更新)

我的EF上下文中的问题和范围之间存在多对多的关系.在ASP.NET MVC中,我提出一个允许用户编辑特定问题的编辑表单.在表单的底部,是一个允许他们选择适用于此问题的范围的复选框列表.编辑问题时,可能会始终有一些与之相关联的范围 – 这些框将被检查.但是,用户有机会检查更多的范围或删除当前检查的一些范围.我的代码看起来像这样只能保存问题:
using (var edmx = new MayflyEntities())
            {
                Issue issue = new Issue { IssueID = id,TSColumn = formIssue.TSColumn };
                edmx.Issues.Attach(issue);

                UpdateModel(issue);

                if (ModelState.IsValid)
                {
                    //if (edmx.SaveChanges() != 1) throw new Exception("UnkNown error. Please try again.");
                    edmx.SaveChanges();
                    TempData["message"] = string.Format("Issue #{0} successfully modified.",id);
                }
            }

所以,当我尝试添加逻辑来保存关联的范围时,我尝试了几件事情,但最终,这对我来说是最有意义的:

using (var edmx = new MayflyEntities())
            {
                Issue issue = new Issue { IssueID = id,TSColumn = formIssue.TSColumn };
                edmx.Issues.Attach(issue);

                UpdateModel(issue);

                foreach (int scopeID in formIssue.ScopeIDs)
                {
                    var thisScope = new Scope { ID = scopeID };
                    edmx.Scopes.Attach(thisScope);
                    thisScope.ProjectID = formIssue.ProjectID;
                    if (issue.Scopes.Contains(thisScope))
                    {
                        issue.Scopes.Attach(thisScope); //the scope already exists
                    }
                    else
                    {
                        issue.Scopes.Add(thisScope); // the scope needs to be added
                    }
                }

                if (ModelState.IsValid)
                {
                    //if (edmx.SaveChanges() != 1) throw new Exception("UnkNown error. Please try again.");
                    edmx.SaveChanges();
                    TempData["message"] = string.Format("Issue #{0} successfully modified.",id);
                }
            }

但是,不幸的是,只是抛出了以下异常:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

我究竟做错了什么?

解决方法

存根通常仅对1- *关系有效.
* – *关系引入了一系列不同的挑战.

也就是说,当你连接两端 – 不像1- * – 你仍然不知道关系是否已经存在.

所以这意味着这段代码:

if (issue.Scopes.Contains(thisScope))

每次都可能会返回错误.

我会做的是这样的:

edmx.Issues.Attach(issue);
UpdateModel(issue);
// or ctx.LoadProperty(issue,"Scopes") if it is a POCO class.
issue.Scopes.Load(); // hit the database to load the current state.

现在你需要找出你需要添加的从问题中删除.
您可以通过基于ID的比较来做到这一点.

即如果您有一组要与问题相关的范围ID(relevantScopes)

然后这个代码解决了要添加的内容以及要删除的内容.

int[] toAdd = relatedScopes.Except(issue.Scopes.Select(s => s.ID)).ToArray();
int[] toRemove = issue.Scopes.Select(s => s.ID).Except(relatedScopes).ToArray();

现在要添加你这样做:

foreach(int id in toAdd)
{
   var scope = new Scope{Id = id};
   edmx.Scopes.Attach(scope);
   issue.Scopes.Add(scope);
}

对于每个范围,您需要删除

foreach(int id in toRemove)
{
   issue.Scopes.Remove(issue.Scopes.Single(s => s.ID == id));
}

现在应该形成正确的关系.

希望这可以帮助

Alex

微软

我们今天的关于实体框架/ SQL2008-如何自动更新实体的LastModified字段?的分享就到这里,谢谢您的阅读,如果想了解更多关于@CreatedBy和@LastModifiedBy设置实际实体而不是id、android – 使用SQLite触发器来更新“LastModified”字段、asp.net-mvc – 如何更新实体框架连接字符串、asp.net-mvc – 实体框架更新实体以及子实体(必要时添加/更新)的相关信息,可以在本站进行搜索。

本文标签: