locked
DynamicData Web App: Custom Update Method on Edit.aspx page RRS feed

  • Question

  • User-236004499 posted

    I am using VS2010 RTM; Ado.Net Entity Frame work 4.0.

    Since its Dynamic Data Web Application project, i have not write any single line of code as everything done by Dynamic Data web application; I just add Tables to Model.edmx file and run project.

    On Edit.aspx page on click of "Update" Button entity framework, does savechanges automatically. What i want is to execute/run "Custom Update Method" which runs my Stored procedure on database instead default SaveChanges process done by Entity FrameWork.

    Appreciate your help

    Pratik

    Tuesday, August 23, 2011 9:27 PM

Answers

  • User-236004499 posted

    I work around it. I don't know is this the right way or not. Please correct me if I am wrong or better way to doit. I am now able to get CurrentValue and OriginalValue of related Objects. Since OriginalValue means which not yet changed so i queried to object and gets original value.
    Below is the code i am using.

    public partial class TestDDEntities
    {
    partial void OnContextCreated()
    {
    this.SavingChanges += new EventHandler(context_SavingChanges);
    }

    private static void context_SavingChanges(object sender, EventArgs e)
    {
    var objects = ((ObjectContext)sender).ObjectStateManager;
    // get new objects
    foreach (ObjectStateEntry entry in objects.GetObjectStateEntries(EntityState.Modified | EntityState.Added))
    {
         if (entry.Entity is Book)
           
    {
               string categoryname;
              
    // Getting new value
              
    foreach (var item in (entry.Entity as Book).Categories)
                  {
                       categoryname = item.category_name;
                  }
                // process for getting old value
              
    TestDDEntities test = new TestDDEntities();
               var cat = test.Books.Include("Categories").Where(b => b.BookId == 45).ToList();
                  foreach (var item in cat)
              
    {
                       //categoryname = item.Categories.FirstOrDefault().category_name.ToString();
                  
    var  cats = item.Categories.ToList();
                  
    foreach (var item1 in cats)
                  
    {
                           categoryname = item1.category_name;
                       }
                  }
              // Execute stored procedure by passing old and new value to it.
            
    }
            }
         }
    }

    Well whichever book edited by user (removing old categories selection and assigning new categories), stored procedure creates new Book and its related new categories, then after i delete old book and categories (which was being edited; because now user do not want to see this details in here as new book generated with new categories referenced). Well here DynamicData throws error because the record being edited is deleted by stored procedure. In this case, i want to refresh this entity; so new bound happened on List.aspx. (I am currently doing "Response.RedirectTo("List.aspx")" to avoid error and also it rebounds with existing data i.e. deleted book is not showing here). I am thinking there must be right way to doing by refreshing entity; or detach then attach entity to refresh ????? not sure.

    Appreciate your help

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, August 26, 2011 12:15 AM

All replies

  • User1980140492 posted

    Heya,

    First you should create your own partial entities class to "extend" your entities model in which you define the context_SavingChanges method.
    In this method you can add whatever logic you think is needed. I'm using it in this example to add some validations and a last change and creation dates, but you can add whatever you want.

    Here's the part of my code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Data.Objects;
    using System.Data;
    using System.ComponentModel.DataAnnotations;
    using NotAClue.ComponentModel.DataAnnotations;
    using ChangingAware;
    using System.ComponentModel;
    
    namespace AM
    {
        public partial class AM_Model_Entities
        {
    
            partial void OnContextCreated()
            {
                this.SavingChanges += new EventHandler(context_SavingChanges);
            }
    
            private static void context_SavingChanges(object sender, EventArgs e)
            {
                foreach (ObjectStateEntry entry in
                ((ObjectContext)sender).ObjectStateManager.GetObjectStateEntries(
                EntityState.Added | EntityState.Modified))
                {
                    // Find an object state entry for a Products object. 
                    if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(AM_ARTICLE)))
                    {
                        AM_ARTICLE CurrentEntity = (AM_ARTICLE)entry.Entity;
                        using(AM_Model_Entities Entities = new AM_Model_Entities())
                        {
                            AM_ARTICLE LookUp = Entities.AM_ARTICLE.FirstOrDefault(
                                c => c.AM_SITE.ID == CurrentEntity.AM_SITE.ID
                                && c.ArticleNumber.Equals(CurrentEntity.ArticleNumber)
                                && c.ID != CurrentEntity.ID);
                            if (LookUp == null)
                            {
                                if (CurrentEntity.AM_MAINGROUP == null)
                                {
                                    throw new ValidationException("Please provide a maingroup.");
                                }
                                else
                                {
                                    if (!CurrentEntity.AM_SITE.SiteName.Equals(CurrentEntity.AM_MAINGROUP.AM_SITE.SiteName) ||
                                        !CurrentEntity.AM_SITE.SiteName.Equals(CurrentEntity.AM_SUBGROUP.AM_SITE.SiteName) ||
                                        !CurrentEntity.AM_SITE.SiteName.Equals(CurrentEntity.AM_SUPPLIER.AM_SITE.SiteName))
                                    {
                                        throw new ValidationException("The chosen references need to be from the chosen site.");
                                    }
                                    else
                                    {
                                        int DateModified_Ordinal = entry.CurrentValues.GetOrdinal("Date_Modified_AM");
                                        int DateCreated_Ordinal = entry.CurrentValues.GetOrdinal("Date_Creation_AM");
                                        if (DateCreated_Ordinal > 0 && entry.CurrentValues.GetValue(DateCreated_Ordinal) == DBNull.Value)
                                        {
                                            entry.CurrentValues.SetValue(DateCreated_Ordinal, DateTime.Now);
                                        }
                                        else
                                        {
                                            if (DateModified_Ordinal > 0)
                                            {
                                                entry.CurrentValues.SetValue(DateModified_Ordinal, DateTime.Now);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                throw new ValidationException("There already exists an article with the given articlenumber in the chosen site.");
                            }
                        }
                    }
                 }
            }
        }
        

    Hope it helps.

    Regards,
    - Yannick

    Wednesday, August 24, 2011 4:50 AM
  • User-236004499 posted

    Hi Yannic,

    Thanks for reply, I have few queries.

    (1) I created a class "MetaDataClass.cs" where i am controling columns behaviour of all entities. I assume this is where i need to write your mentioned code. Please look at "public partial class LRM_Book" at "OnContextCreate()" method "this.SavingChanges" comes with red underline saying i am missing assembly or directive?

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.ComponentModel;
    using System.Web.DynamicData;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.Design;
    using LawAccessAdminTest2;
    using NotAClue.Web.DynamicData;
    using NotAClue.ComponentModel.DataAnnotations;
    using System.Data.Objects;
    using System.Data;
    using LawAccessAdminTest2.DynamicData;
    namespace LawAccessAdminTest2.DynamicData
    {
         // Hide Old Category from menu
         [MetadataType(typeof(OldCategoryMetadata))]
         
    public partial class Category{}
         [
    ScaffoldTable(false)]
        
    public class OldCategoryMetadata{}
         // Hide New Category from menu as this is just used to data structuring
        
        
    [MetadataType(typeof(NewCategoryMetadata))]
         public partial class CategoryNew { }
         [
    ScaffoldTable(false)]
        
    public class NewCategoryMetadata { }
         //r
    ename of the LRM_Book on menu to Old Book
        
    [MetadataType(typeof(OldLRMBookMetadata))]

           [DisplayName("Old Book")]
          public partial class LRM_Book
          {
              partial void OnContextCreated()
              {
               this.SavingChanges += new EventHandler  (context_SavingChanges);
              }
          private static void context_SavingChanges(object sender, EventArgs e)
          {
              // Here I want to execute stored procedure which accepts 4 parameter
              // So i want to call this method from Edit.aspx page (for "LRM_Book" Entity), by passing parameter value.
             
    LawAccessEntities LawAccess = new LawAccessEntities();
              LawAccess.uspV1_CreateBookSecCategoryByBookID(bookid, mainCategoryID, subCategoryID);
          }

          internal partial class OldLRMBookMetadata
          {
             [
    ScaffoldColumn(false)]
             public object DateCreated { get; set; }          
             [
    ScaffoldColumn(false)]
             
    public object DateUploaded { get; set; }
          
    }
        }
    }

    -------

    (2) Also i have mention on "Context_SavingChanges" method i want to execute Stored procedure. the code i write is that the correct way to execute stored procedure???
    (3) How can i call "Context_SavingChanges" method from Edit.aspx page (only for LRM_Entities not for other entities); also how can i pass parameter values to stored procedure.

    Thanks
    Pratik

    Wednesday, August 24, 2011 4:57 PM
  • User1980140492 posted

    You have to do this in the partial class of your model. Not of a metadata class.

    If you unfold your model, you'll see a designer.cs class, open this one and you'll see someting like this:

    public partial class AM_Model_Entities : ObjectContext

    The name of that class is the one you need to use in which you define the logic I was talking about earlyer.

    Thursday, August 25, 2011 2:28 AM
  • User-236004499 posted

    Hi Yannick,

    I tried Model.desinger.cs file to write your suggested code but gives error on very beginging line "partial void OnContextCreated()" gives error on "void" as intellisense not give me void (in dropdownlist). I have code structure below in model.designer.cs (I am using ADO.Net Entity Framework 4.0). So can you suggest where to write it. And when i open model.Designer.cs class; first 3 comment line is written by microsoft, Manual changes to this file may cause unexpected behavior in your application.
    Manual changes to this file will be overwritten if the code is regenerated. Since project on development I need to do neccessary changes on DB if i add my custome code in here then everytime i do "Update Model From Database.." On model then my custom code will overwrite, so everytime i have to write it, so is this good idea to write here custome code?

    namespace test.DynamicData
    {
    #region Contexts
          public partial class testEntities : ObjectContext
          {
                // i HAVE 5 REGION HERE
                (1) Constructors
                (2) Partial Methods
                (3) OjectSet Properties
                (4) AddTo Methods
                (5) Function Imports
           }
    #endregion

    #region Entitties
    {
       // here i have all my tables class (entities class)
       // I think here you want me to write code.
       // below is the entity for which i want to execute custom saving process. code generated as below

    [EdmEntityTypeAttribute(NamespaceName="Test_Model", Name="Book")]
    [Serializable()]
    [DataContractAttribute(IsReference=true)]
    public partial classBook : EntityObject
    {
         // This class also has 3 regions as below
         (1) Factory Method
         (2) Primitive Properties
         (3) Navigation Properties
         // I tried to add below your suggested code but not allow me 
         // SavingChanges (directive /assembley missing error). entitySate.modified -->an object reference is required for non static field, method or properites.

    }

     }
    #endregion

     

     

    Thursday, August 25, 2011 6:19 PM
  • User3866881 posted

    Hello:)

    Also i have mention on "Context_SavingChanges" method i want to execute Stored procedure. the code i write is that the correct way to execute stored procedure???

    Yes, I think what you do is right. if you re-write the partial method to handle the savechanging event, it will directly go where you've defined.

    How can i call "Context_SavingChanges" method from Edit.aspx page (only for LRM_Entities not for other entities); also how can i pass parameter values to stored procedure.

    I think since "LRM_Book" is the partial of the auto-gererated model class, and the whole EF model classes have all been bound to the GridView with the help of dynamic data. I think you can directly use the public property of the "LRM_Book" as parameters to pass them into the stored procdure.

    Thursday, August 25, 2011 9:20 PM
  • User-236004499 posted

    Hi Decker,

    I can able to do stored procedure mapping to LRM_Book entity in mode design at "Update Function". By doing--> On Model Design --> Right click on "LRM_Book" entity --> select "Stored Procedure Mapping"-->horizontal window opens bottom of screen "Mapping Details" At "<Select Update Function>" i selected my stored procedure, now EF also tells me to add parameter value straight here, aparently i am getting values from the code behind (Getting multiple selected new/old items from checkboxList; this multiple value makes string of XML) and pass it to the stored procedure. In EF, if i mapped SP to an entity then i have to have mapped/bind parameter value fields from the property of the Entity; in my case i don't want this as i am getting multiple old and new value. Therefore, i want seperate SaveChanges mathod where i can call this SP independently and pass parameter value whatever i want.

    Well just now i tried on MetaDataClass.cs, update calls "context_SaveChanges" method. I am doing on Test project.
    I create "public partial class TestDDEntities" same name of the model entity. and it start execute custome saving method.

    I am checking that entity. If "Book" entity then execute stored procedure. but now still question is how i will supply multiple selected new/old value of the checkboxlist to the stored procedure. I can able to get the current value by using your code (Deckers), well again i can get original value of Book entity but not the categories (manyTomay), yes i can get current new value of the categories (manyTomany).

    I use below code.

    public partial class TestDDEntities
    {
    partial void OnContextCreated()
    {
         this.SavingChanges += new EventHandler(context_SavingChanges);
    }
    private static void context_SavingChanges(object sender, EventArgs e)
    {
        var objects = ((ObjectContext)sender).ObjectStateManager;
        foreach (ObjectStateEntry entry in objects.GetObjectStateEntries(EntityState.Modified))
        
    {
             if (entry.Entity is Book)
             
    {
                    string categoryname;
                 foreach (var item in (entry.Entity as Book).Categories)
                     {
                       // Gets New Category value
                        categoryname = item.category_name;

                        // If i can able to get Original value of the category_name, then my whole proble will sove 
                     }
                  }
                  // Below is my SP i want to execute i can able to generate "newCategoryXML using above code. but not for the Original Category value
                  uspV1_CreateBookSecCategoryByBookID(GetBookId, newCateogryXML, OldCategoryXML)


          }
    }
    }

    Above code is the combination of "Yannic" and "Decker". Please read out my comments in bold where i want to get original category value so my whole problem is resolve or if i can't get here old value then I already getting value on edit.aspx page (using Deckers) code i just need to pass that OldCategoryXML value in here, then my issue will resolve.

    Appreciate your help
    Pratik

    Thursday, August 25, 2011 10:47 PM
  • User-236004499 posted

    I work around it. I don't know is this the right way or not. Please correct me if I am wrong or better way to doit. I am now able to get CurrentValue and OriginalValue of related Objects. Since OriginalValue means which not yet changed so i queried to object and gets original value.
    Below is the code i am using.

    public partial class TestDDEntities
    {
    partial void OnContextCreated()
    {
    this.SavingChanges += new EventHandler(context_SavingChanges);
    }

    private static void context_SavingChanges(object sender, EventArgs e)
    {
    var objects = ((ObjectContext)sender).ObjectStateManager;
    // get new objects
    foreach (ObjectStateEntry entry in objects.GetObjectStateEntries(EntityState.Modified | EntityState.Added))
    {
         if (entry.Entity is Book)
           
    {
               string categoryname;
              
    // Getting new value
              
    foreach (var item in (entry.Entity as Book).Categories)
                  {
                       categoryname = item.category_name;
                  }
                // process for getting old value
              
    TestDDEntities test = new TestDDEntities();
               var cat = test.Books.Include("Categories").Where(b => b.BookId == 45).ToList();
                  foreach (var item in cat)
              
    {
                       //categoryname = item.Categories.FirstOrDefault().category_name.ToString();
                  
    var  cats = item.Categories.ToList();
                  
    foreach (var item1 in cats)
                  
    {
                           categoryname = item1.category_name;
                       }
                  }
              // Execute stored procedure by passing old and new value to it.
            
    }
            }
         }
    }

    Well whichever book edited by user (removing old categories selection and assigning new categories), stored procedure creates new Book and its related new categories, then after i delete old book and categories (which was being edited; because now user do not want to see this details in here as new book generated with new categories referenced). Well here DynamicData throws error because the record being edited is deleted by stored procedure. In this case, i want to refresh this entity; so new bound happened on List.aspx. (I am currently doing "Response.RedirectTo("List.aspx")" to avoid error and also it rebounds with existing data i.e. deleted book is not showing here). I am thinking there must be right way to doing by refreshing entity; or detach then attach entity to refresh ????? not sure.

    Appreciate your help

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, August 26, 2011 12:15 AM