4

this is my first post, hopefully I'm following the rules!

After a few weeks of banging my head against brick walls and endless hours of internet searches, I have decided I need to make my own post and hopefully find the answers and guidance from all you experts.

I am building an ASP.NET application (in Visual Studio 2010) which uses an SQL 2008 database and Entity Framework 4 to join the two parts together. I designed the database as a database project first, then built the Entity Model as a class project which is then referenced in the main ASP.NET project. I believe this is called Database First in Entity Framwork terms?

I'm fairly new to Web Apps (I mainly develop WinForms apps) and this is my first attempt at using Entity Framwork.

I can just about understand C# so any code samples or snippets you might supply would be preferred in VB if possible.

To give some background on where I am so far. I built a regsitration page called register.aspx and created a wizard style form using ASP:Wizard with ASP:TextBox, ASP:DropdownList and ASP:CheckBoxList it's all laid out inside a HTML table and uses CSS for some of the formatting. Once the user gets the the last page in the wizard and presses "Finish" I execute some code in the VB code behind file as follows:

Protected Sub wizardRegister_FinishButtonClick(sender As Object, e As System.Web.UI.WebControls.WizardNavigationEventArgs) Handles wizardRegister.FinishButtonClick
    Dim context As New CPMModel.CPMEntities
    Dim person As New CPMModel.Person
    Dim employee As New CPMModel.Employee

    Dim newID As Guid = Guid.NewGuid

    With person
        .ID = newID
        .UserID = txbx_UserName.Text
        .Title = ddl_Title.SelectedValue
        .FirstName = txbx_FirstName.Text
        .MiddleInitial = txbx_MiddleInitial.Text
        .FamilyName = txbx_FamilyName.Text
        .Gender = ddl_Gender.SelectedValue
        .DOB = txbx_DOB.Text
        .RegistrationDate = Date.Now
        .RegistrationMethod = "W"
        .ContactMethodID = New Guid(ddl_ContactMethodList.SelectedValue)
        .UserName = txbx_UserName.Text
        If Not (My.Settings.AuthenticationUsingAD) Then
            .Password = txbx_Password.Text ' [todo] write call to salted password hash function
        End If
        .IsRedundant = False
        .IsLocked = False
    End With

    context.AddToPeople(person)

    With employee
        .ID = newID
        .PayrollNumber = txbx_PayrollNumber.Text
        .JobTitle = txbx_JobTitle.Text
        .DepartmentID = ddl_DepartmentList.SelectedValue
        .OfficeNumber = txbx_OfficeNumber.Text
        .HomeNumber = txbx_HomeNumber.Text
        .MobileNumber = txbx_MobileNumber.Text
        .BleepNumber = txbx_BleepNumber.Text
        .OfficeEmail = txbx_OfficeEmail.Text
        .HomeEmail = txbx_HomeEmail.Text
        .IsRedundant = False
        .RedundantDate = Nothing

         '--------------------------
        .FiledBy = Threading.Thread.CurrentPrincipal.Identity.Name
        .FiledLocation = My.Computer.Name
        .FiledDateTimeStamp = Date.Now
        '----------------------------
    End With

    context.AddToEmployees(employee)
    context.SaveChanges()
End Sub

The above works fine, seemed to be a sensible way of doing it (remember I'm new to Entity Framework) and gave me the results I extpected.

I have another page called manage.aspx on this page is a tab control, each tab page contains a asp:DetailsView which is bound to an asp:EntityDataSource, I have enabled Update on all the Entity Data Sources and on some I have also enabled Insert, none of the have delete enabled.

If I build and run the app at this stage everything works fine, you can press the "edit" link make changes and then press "update" and sure enough those updates are displayed on the screen instantly and the database does have the correct values in.

Here's the problem, I need to intercept the update in the above code (the bold bit) notice there is a column in my database called FiledBy, FiledLocation, and FiledDateTimeStamp. I don't want to show these columns to the user viewing the ASP.NET page but I want to update them when the user presses update (if there were any changes made). I have tried converting the ASP:DetailsView into a template and then coding the HTML side with things like;

<@# Eval(User.Name) #>

I did manage to get it to put the correct values in the textboxes when in edit mode but I had the following problems;

  1. It didn't save to the database
  2. I have to show the textboxes which I don't want to do

I have read on several other posts on here and other websites that you can override the SaveChanges part of the Entity Framwork model one example of this is in the code below;

public override int SaveChanges() 
{
var entities = ChangeTracker.Entries<YourEntityType>() 
                            .Where(e => e.State == EntityState.Added) 
                            .Select(e => e.Entity); 
var currentDate = DateTime.Now; 
foreach(var entity in entities) 
{ 
    entity.Date = currentDate; 
} 

return base.SaveChanges(); 
} 

The problem is, that whilst I understand the idea, and can just about transalate it to VB I have no idea how to implement this. Where do I put the code? How do I call the code? etc.

I would ideally like to find a solution that meets the following requirements:

  • Doesn't get overwritten if I was to regenerate / update the Entity Model
  • Doesn't need to be copy and pasted for every table in my model (most but not all have the audit columns in)
  • Can be used for other columns, for example, if a user make a selection in a check list I may want to write some value into another (not exposed to the user) column.

Am I taking the right approach to displaying data in may webpages I certainly find the DataView and GridView limiting, I did think about creating a form like the registration form in table tags but have no Idea how to populate the textboxes etc with values from the database nor how to implement paging as many of the tables have one to many relationships, although saving changes would be easy if I did populate textboxes on a table.

Thank you all in advance to anyone who offers there support.

Kevin.

Ok so I have now got closer, but still not got it working correctly. I've used the following code to edit the value in the DetailsView on the Databound event. If writes the correct timestamp into the read only textbox but does not right this value back to the database when you press the Update button on the DetailsView control.

Protected Sub dv_Employee_DataBound(sender As Object, e As EventArgs) Handles dv_Employee.DataBound
    If dv_Employee.CurrentMode = DetailsViewMode.Edit Then
        For Each row In dv_Employee.Rows
            If row.Cells(0).Text = "FiledDateTimeStamp" Then
                row.Cells(1).Text = Date.Now()
            End If
        Next
    End If
End Sub
4

2 回答 2

1

我喜欢覆盖保存更改方法的想法。您的上下文应该是一个部分类,因此您只需要在同一命名空间中定义另一个标记为具有相同名称的部分类并将覆盖添加到它。

Namespace CPMModel
  Partial Public Class CPMEntities
   ...Your override here
End Class

由于这是对默认 SaveChanges 方法的覆盖,因此您无需对其调用方式进行任何更改。您将需要找到一种访问实体级别属性的方法。

由于这是为所有实体提交更改的方法,因此您将无法像在示例中那样访问属性,因为实体类没有定义您的具体属性。因此,您需要为您的实体创建部分类,并让它们都实现一个接口,该接口定义了您想要与之交互的属性。

创建接口:

Interface IDate
   Property Date() as DateTime
End Interface

为实现 IDate 接口的每个实体创建部分类。(相同的规则适用于这些部分类,它们必须标记为部分,与它们扩展的类具有相同的名称并位于同一命名空间中)然后在您的 SaveChanges 覆盖

public override int SaveChanges() 
{
         var entities = ChangeTracker.Entries<Entity>() 
                        .Where(e => e.State == EntityState.Added) 
                        .Select(e => e.Entity); 
        var currentDate = DateTime.Now; 
        foreach(var entity in entities) 
        { 
                ***Now cast entity to type of IDate and set your value ***
                entity.Date = currentDate; 
        } 

        return base.SaveChanges(); 
} 

我不是用 Vb 写的,所以我把语法留在了 C# 中。

那应该满足您的所有要求。

于 2012-10-22T15:59:13.170 回答
0

好的,所以这并不是我想象的那样,但它实际上满足了我的需求。有点儿!

我所做的是在 aspx 文件的代码隐藏文件中编写一个受保护的子。

Protected Sub CustomEntityUpdate_Employee(sender As Object, e As  
EntityDataSourceChangingEventArgs)
    Dim emp As CPMModel.Employee = e.Entity
    ' Check if the entity state is modified and update auditing columns
    If emp.EntityState = EntityState.Modified Then
        emp.FiledDateTimeStamp = Date.Now()
        emp.FiledBy = Threading.Thread.CurrentPrincipal.Identity.Name
        emp.FiledLocation = My.Computer.Name
    End If
End Sub

然后在aspx页面中我编辑了Entity数据源代码来调用上面的子例程

#nUpdating="CustomEntityUpdate_Employee"

<asp:EntityDataSource 
    ID="eds_Employee" 
    runat="server" 
    ConnectionString="name=CPMEntities" 
    DefaultContainerName="CPMEntities" 
    EnableFlattening="False" 
    EnableUpdate="True" 
    EntitySetName="Employees" 
    AutoGenerateWhereClause="True" 
    Where="" 
    EntityTypeFilter="Employee" 
    **OnUpdating="CustomEntityUpdate_Employee">**
    <WhereParameters>
        <asp:SessionParameter 
            Name="ID" 
            SessionField="UserID" />
    </WhereParameters>
</asp:EntityDataSource>

受保护的子,检查实体是否已被修改,以及它是否使用我想要的值更新了实体审计列。

它可以工作,但肯定会产生更多代码,理想情况下,我希望有一种方法可以将它打包到一个类中,然后通过 OnUpdating 事件从各种 aspx 页面调用 sub 并让类找出哪个实体是拨打电话并处理那里的所有逻辑。

于 2012-10-21T02:49:12.330 回答