【问题标题】:Entity Framework 6 EntityDataSource not calling SaveChanges in DbContext实体框架 6 EntityDataSource 未在 DbContext 中调用 SaveChanges
【发布时间】:2016-01-30 20:31:47
【问题描述】:

我正在开发一个 asp.net 网站项目。它没有非常一致的数据层,所以我正在尝试实现代码优先的实体框架。

我最近发现制作实体框架的人已经创建了一个新版本的 EntityDataSource,它可以与 Entity Framework 6 一起使用。

http://blogs.msdn.com/b/webdev/archive/2014/02/28/announcing-the-release-of-dynamic-data-provider-and-entitydatasource-control-for-entity-framework-6.aspx

这个新数据源看起来非常好,它会更新数据库。它似乎没有调用我的 DbContext 类上的任何方法。

作为一些基本更改跟踪要求的一部分,我重写了 DbContext SaveChanges() 方法并放入了一些代码来更新每条记录上的跟踪字段(CreatedBy、CreatedDate、ModifiedBy、ModifiedDate)。奇怪的是,当我直接使用 DBContect 时,会调用 SaveChanges() 方法。但是当我使用这个 EntityDataSource 时,它​​不会调用 SaveChanges()。

上面的实体框架 EntityDataSource 如何更新 dbset?

有什么方法可以让这个 EntityDataSource 调用 DbContext SaveChanges() 方法?

是否可以替代覆盖 DBContext SaveChanges 方法?

这是我的实体框架EntityDataSource控件定义的示例

<asp:ListView ID="FormView" runat="server" DataKeyNames="RecordId" DataSourceID="ApplicantDataSource" DefaultMode="Edit">
    <EditItemTemplate>
        <asp:TextBox runat="server" ID="SomeField" Text='<%# Bind("SomeField")%>' ></asp:TextBox>
    </EditItemTemplate>
</asp:ListView>

<ef:EntityDataSource runat="server" 
                     ID="ApplicantDataSource"
                     ContextTypeName="Organisation.Application.MyDbContext"
                     EntitySetName="Applicants"
                     Where="it.RecordId=@RecordId"
                     EnableUpdate="true">
    <WhereParameters>
        <asp:QueryStringParameter Name="RecordId" QueryStringField="RecordId" DbType="String" DefaultValue=""/>
    </WhereParameters>
</ef:EntityDataSource>

这是我的 DbContext(删减)。在 EntityDataSource 上调用 update 时,它​​不会通过 SaveChanges()。它甚至不调用申请人属性中的 getter 来访问申请人的 DBSet。它仍然设法以某种方式保存信息!

Public Class MyDbContext
    Inherits DbContext

    Public Shared Sub MyDbContext()
        Database.SetInitializer(Of MyDbContext)(Nothing)
    End Sub

    Public Sub New()
        MyBase.New("Name=ConnectionString")
    End Sub

    Public Property Applicants() As DbSet(Of Applicant)

    Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)

        modelBuilder.Conventions.Remove(Of PluralizingTableNameConvention)()

        MyBase.OnModelCreating(modelBuilder)
    End Sub

    Public Overrides Function SaveChanges() As Integer
        Try
            Dim entities = Me.ChangeTracker.Entries().Where(Function(x) TypeOf x.Entity Is MyBase AndAlso (x.State = EntityState.Added OrElse x.State = EntityState.Modified))
            Dim currentUsername As String

            If HttpContext.Current IsNot Nothing And HttpContext.Current.User IsNot Nothing Then
                currentUsername = HttpContext.Current.User.Identity.Name
            Else
                currentUsername = "System"
            End If

            For Each entity In entities
                If entity.State = EntityState.Added Then
                    DirectCast(entity.Entity, MyBase).CreatedDate = DateTime.Now
                    DirectCast(entity.Entity, MyBase).CreatedBy = currentUsername
                ElseIf entity.State = EntityState.Modified Then
                    entity.Property("CreatedBy").IsModified = False
                    entity.Property("CreatedDate").IsModified = False
                End If

                DirectCast(entity.Entity, MyBase).ModifiedDate = DateTime.Now
                DirectCast(entity.Entity, MyBase).ModifiedBy = currentUsername
            Next

            Return MyBase.SaveChanges()
        Catch ex As DbEntityValidationException
            ' Retrieve the error messages as a list of strings.
            Dim errorMessages = ex.EntityValidationErrors.SelectMany(Function(x) x.ValidationErrors).[Select](Function(x) x.ErrorMessage)

            ' Join the list to a single string.
            Dim fullErrorMessage = String.Join("; ", errorMessages)

            ' Combine the original exception message with the new one.
            Dim exceptionMessage = String.Concat(ex.Message, " The validation errors are: ", fullErrorMessage)

            ' Throw a new DbEntityValidationException with the improved exception message.
            Throw New DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors)
        End Try
    End Function

End Class

【问题讨论】:

  • 我没有看到你的 MyDbContext 继承自 DbContext,我认为这是必不可少的。
  • 这在我的代码中,它并没有以某种方式进入问题文本。我刚刚更新了这个问题。当我粘贴示例时,我一定是过度简化了!
  • 能否包含调用 DbContext 的(示例)代码?我正在寻找您如何执行更新或创建(取决于这两个(或两者)中的哪一个失败)。
  • 我通过链接 aspx 页面中的不同控件以声明方式进行更新。有一个 ListView 或 GridView 控制 EntityDataSource。代码隐藏页面中没有代码。当绑定控件上的更新命令被命中时,它只是将它传递给实体数据源的任何内容传递给它,就像魔术一样,它使用 DBContext 并更新数据库。
  • 好的,我明白了。并且您从NuGet 安装了最新版本?另外,SO answer 是否适用于您?

标签: asp.net .net vb.net entity-framework


【解决方案1】:

上面的实体框架EntityDataSource是如何更新数据库的?

EntityDataSource 使用底层 ObjectContext,而不是 DbContext。这就是为什么您永远不会看到任何东西直接通过您的 DbContext 上的 SaveChanges() 重载。

有什么办法可以让这个 EntityDataSource 调用 DbContext SaveChanges() 方法?

是也不是,您可以重载Updating 方法,通过EntityDataSourceChangingEventArgs 您可以访问DbContext 然后调用它。不过,这对于您的所有观点都是必要的,但我看不出有一种方法可以轻松地集中这一点及其糟糕的设计(因此没有)。

protected void x_Updating(object sender, EntityDataSourceChangingEventArgs e) {
        e.Cancel = true;
        DbContext dbc = new DbContext(e.Context, true);
        new YourDbContext(dbc).SaveChanges();
    }

是否有替代方法覆盖 DBContext SaveChanges 方法?

只有我上面描述的。您可以挂接到ObjectContext 并获得传递的事件,但是当context is created 时您需要它的一个实例(请参阅该事件)。然后,您可以订阅 SavingChanges 事件作为您的钩子,尽管这比 DbContext 上的 SaveChanges 更受限制。如果你只想做

这是一个 Vb.Net 代码示例,说明如何连接。如果语法不正确,请不要开枪,自从我用 Vb 写任何东西以来已经很久了。您可能需要在设计器中注册onContextCreated(aspx 代码)。

免责声明 - 我没有测试下面的代码。它部分基于您的代码和SavingChanges 文档中的示例代码。

Protected Sub onContextCreated(ByVal sender As Object, ByVal args As EntityDataSourceContextCreatedEventArgs)
   AddHandler args.Context.SavingChanges, AddressOf context_SavingChanges
End Sub

Private Sub context_SavingChanges(ByVal sender As Object, ByVal e As EventArgs)
    Dim context As ObjectContext = TryCast(sender, ObjectContext)

    If context IsNot Nothing Then
        If HttpContext.Current IsNot Nothing And HttpContext.Current.User IsNot Nothing Then
            currentUsername = HttpContext.Current.User.Identity.Name
        Else
            currentUsername = "System"
        End If

        For Each entity As ObjectStateEntry In context.ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
            If entity.State = EntityState.Added Then
                DirectCast(entity.Entity, MyBase).CreatedDate = DateTime.Now
                DirectCast(entity.Entity, MyBase).CreatedBy = currentUsername
            ElseIf entity.State = EntityState.Modified Then
                entity.Property("CreatedBy").IsModified = False
                entity.Property("CreatedDate").IsModified = False
            End If

            DirectCast(entity.Entity, MyBase).ModifiedDate = DateTime.Now
            DirectCast(entity.Entity, MyBase).ModifiedBy = currentUsername
        Next
    End If
    
End Sub

部分答案来源:http://w3stack.org/question/how-can-i-override-dbcontext-savechanges-when-using-entitydatasource-and-database-generated-model-edmx-ef-5/

【讨论】:

  • 干杯伊戈尔!如果没有其他问题,我将对此进行研究并在明天再次弹出以将其设置为答案。不过,这似乎是一个非常明确的答案!
  • 酷。我什至添加了几行 Vb 代码,好久没有这样做了 :) 祝你好运!
  • @Alex - 我根据你想要做的事情添加了额外的代码。希望这对你有用。
  • 好东西!如果我能找到一种方法将其划分为单独的班级,那将是赢家。我喜欢它没有成为 dbcontext 的工作方式。目前,我正在简要了解将数据源继承到新类中,并找到一种方法将事件处理程序每​​次都直接填充到对象中。然后我就把你的那块塞进去然后离开。不确定这是否真的可行。我正在突破我的 oop 知识的界限。
  • 知道了!我将 EntityDataSource 继承到一个新类中并在那里添加了一个事件处理程序,我认为我从这里开始有了一个可靠的解决方案。感谢您的所有帮助,我几乎放弃了 Igor! 50 Rep 好赚 :D
猜你喜欢
  • 1970-01-01
  • 2014-11-08
  • 1970-01-01
  • 2012-03-24
  • 2013-12-26
  • 1970-01-01
  • 2020-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多