Using MVC2 to update an Entity Framework v4 object using foreign keys

Using the following simple relational database structure: an order has one or more OrderItems, and each OrderItem has one OrderItemStatus.

The Order, OrderItem, and OrderItemStatus tables associated with the foreign key relationships http://www.mindthe.net/images/OrdersDB.jpg Entity Framework v4 are used to communicate with the database, and entities were created from this schema. In the example, the Entities connection is called EnumTestEntities.

The truncated version of the Order Repository class is as follows:

public class OrderRepository
{
  private EnumTestEntities entities = new EnumTestEntities();

  // Query Methods
  public Order Get(int id)
  {
    return entities.Orders.SingleOrDefault(d => d.OrderID == id);
  }

  // Persistence
  public void Save()
  {
     entities.SaveChanges();
  }
}

MVC2 Entity Framework. EditorFor MVC2 "".

POSTing , :

[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
  // Get the current Order out of the database by ID
  Order order = orderRepository.Get(id);
  var orderItems = order.OrderItems;

  try
  {
    // Update the Order from the values posted from the View
    UpdateModel(order, "");
    // Without the ValueProvider suffix it does not attempt to update the order items
    UpdateModel(order.OrderItems, "OrderItems.OrderItems");

    // All the Save() does is call SaveChanges() on the database context
    orderRepository.Save();

    return RedirectToAction("Details", new { id = order.OrderID });
  }
  catch (Exception e)
  {
    return View(order); // Inserted while debugging
  }
}

UpdateModel ValueProvider, , MVC2 OrderItems .

SaveChanges() OrderItems UpdateModel :

"The operation failed: The relationship could not be changed because one or more 
of the foreign-key properties is non-nullable. When a change is made to a 
relationship, the related foreign-key property is set to a null value. If the 
foreign-key does not support null values, a new relationship must be defined, 
the foreign-key property must be assigned another non-null value, or the 
unrelated object must be deleted."

, EntityKeys , , , . , . Getting SaveChanges, .

, ? , EF4 , - , EF4 MVC2, . . , DisplayFor , .

+3
3

, OrderItemStatus. , formValues ​​ OrderItem, UpdateModel . , null OrderItemStatus .

ObjectStateEntries , , :

var items = context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added | System.Data.EntityState.Modified);
foreach (var item in items)
{
  ...

ASP.NET MVC2, UpdateModel, , .

+2

, , , Order GET?

Dto/Resource/ViewModel/< Insert , > .

, UpdateModel -, ( , , )

, , UpdateModel, , .

, ;)

+1

automapper-esque, , . , - . , ( , ), , . : http://kendoll.net/entity_framework_to_mvc2_model_conversion

/// <summary>
/// This is a class with a couple of static methods that make it easy to copy
/// an MVC2 model property values to an Entity-Framework entity.
/// </summary>
/// <typeparam name="T">The type of the model declared in your MVC2 project.</typeparam>
/// <typeparam name="t">The type of the entity declared in your Entity Framework project.</typeparam>
public class EntityModeler<T, t>
    where T : EntityModel<t>
    where t : System.Data.Objects.DataClasses.EntityObject
{
    /// <summary>
    /// A new model of type T is created whose properties will have the same
    /// value as the entity parameter respective of the property name.
    /// </summary>
    /// <param name="entity">
    /// The entity whose property values should be copied to the model.
    /// </param>
    /// <returns>The new model object of type T.</returns>
    public static T ModelEntity(t entity)
    {
        // create an object of type model and populate its properties
        // with the value of the entity properties. Then return the
        // model.
        var model = System.Activator.CreateInstance<T>();
        model.UpdateModel(entity);
        return model;
    }

    /// <summary>
    /// A new IEnumerable set of models is created whose properties have the
    /// same value as the entities' properties in the parameter.
    /// </summary>
    /// <param name="entities">
    /// The entities whose properties should be copied to the models.
    /// </param>
    /// <returns>An IEnumerable set of models of type T.</returns>
    public static IEnumerable<T> ModelEntities(IEnumerable<t> entities)
    {
        // Loop through all the entities in the entity list.
        // Create a model type for each entity. This model will have its
        // UpdateModel method called to copy the entity property values to
        // the model. Then just add the model to a list and return the
        // list.
        var list = new List<T>();
        foreach (var entity in entities)
            list.Add(EntityModeler<T, t>.ModelEntity(entity));
        return list;
    }
}

/// <summary>
/// This is the base class for all MVC2 models that can be converted to an entity defined
/// by a class in an entity framework. Each child class should define its entity type
/// using the "T" type parameter. This class provides methods to update its model properties
/// based on an entity and vice versa.
/// </summary>
/// <typeparam name="T">
/// The type of the entity that corresponds to the model that inherits this base class. This
/// object must descend from the base EntityObject type.
/// </typeparam>
public abstract class EntityModel<T> 
    where T : System.Data.Objects.DataClasses.EntityObject
{
    /// <summary>
    /// This method updates the inheriting model properties to correspond with the 
    /// properties of an entity. The entity must be of the type defined by the inheriting
    /// model. Reflection is used to get the public, readable properties from the entity
    /// and the public, writable properties from the model. Then, we just write the entity's
    /// value to the model for each property that shares the same name.
    /// </summary>
    /// <remarks>
    /// Please note that this method will only copy an entity public properties. Fields
    /// are ignored (though they could easily be added). Also, this method will only perform
    /// a shallow copy of the entity properties.
    /// </remarks>
    /// <param name="entity">
    /// The entity object whose properties should be copied to this model.
    /// </param>
    public void UpdateModel(T entity)
    {
        // get all the public properties of this model in an array
        var myProperties = this.GetType().GetProperties(
            BindingFlags.Instance |
            BindingFlags.Public
            );
        // now get the type of the entity
        var entityType = entity.GetType();
        // loop through the properties
        foreach (var myProp in myProperties)
        {
            // try to get the property with the same name from the entity.
            // If we can read from the entity property and write to this
            // object property, then set this object property according
            // to the value of the entity property. But first, check if the
            // value is null. If the value is null, then the corresponding
            // property in this object must be of type Nullable.
            var entityProp = entityType.GetProperty(myProp.Name);
            if (entityProp != null && entityProp.CanRead && myProp.CanWrite)
            {
                var val = entityProp.GetValue(entity, null);
                if (val == null)
                    if (myProp.PropertyType != typeof(Nullable))
                        continue;
                myProp.SetValue(this, entityProp.GetValue(entity, null), null);
            }
        }
    }

    /// <summary>
    /// This method updates the properties of an entity based on the values of
    /// this model properties. Reflection is used to get the properties of the
    /// entity and the properties of this model, then the values of the model's
    /// properties are copied to those of the entity that share the same name.
    /// </summary>
    /// <remarks>
    /// Please note that this method only works on public properties. Fields are
    /// ignored (though they could easily be added). Also, this method will only
    /// perform a shallow copy of the model properties.
    /// </remarks>
    /// <param name="entity">
    /// This is the entity object whose properties should be updated. It must be
    /// of the type defined by the inheriting class.
    /// </param>
    /// <param name="includeProperties">
    /// This is an array of string values that represent the names of the properties
    /// that the method should copy. If a property name does not exist in this
    /// array, then its value will not be copied.
    /// </param>
    public void UpdateEntity(ref T entity, params string[] includeProperties)
    {
        // get all the public properties of this model in an array
        var propertyInfo = this.GetType().GetProperties(
            BindingFlags.Instance |
            BindingFlags.Public
            );
        // now get the type of the entity.
        var entityType = entity.GetType();
        // loop through each of the properties in the property array for the model type
        foreach (var myProp in propertyInfo)
        {
            // check if this property is in the list of properties to include
            if (includeProperties.Contains(myProp.Name))
            {
                // now try to get the property of the entity with the same name as the
                // property of this type.
                var entityProp = entityType.GetProperty(myProp.Name);
                // check that the entity property exists and that we can write to it.
                if (entityProp != null && entityProp.CanWrite && myProp.CanRead)
                {
                    // get the current value of this property in the model object.
                    // If this property can't be read, the value will be set to null.
                    var val = myProp.GetValue(this, null);
                    // if the value is null, make sure the corresponding property of the
                    // entity is nullable. If it isn't, do not try to set the value. Just
                    // go to the next value in the array.
                    // TODO: We need a better method to determine if an entity property
                    // is nullable. This method won't allow NULL to be written to nullable
                    // columns.
                    if (val == null)
                        if (entityProp.PropertyType != typeof(Nullable))
                            continue;
                    // we're here, so the value should be ok to set. Set the value of the
                    // entity object to the value of the model object.
                    entityProp.SetValue(entity, val, null);
                }
            }
        }
    }

    /// <summary>
    /// This method works similarly to the one above, only it creates a new entity
    /// instead of updating an existing one. The entity will be of the type T which
    /// was declared by the inheriting class.
    /// </summary>
    /// <param name="includeProperties">
    /// This is an array of string values that represent the names of the properties
    /// that the method should copy. If a property name does not exist in this
    /// array, then its value will not be copied. 
    /// </param>
    /// <returns>
    /// The return value is the new entity with its properties set equal to
    /// the respective properties of this model (well, at least those that were
    /// specified in the parameter).
    /// </returns>
    public T CreateEntity(params string[] includeProperties)
    {
        var entity = System.Activator.CreateInstance<T>();
        this.UpdateEntity(ref entity, includeProperties);
        return entity as T;
    }
}

, :

    // Create your model with any properties you choose. You won't
    // have to worry about people hacking your forms, because you'll
    // define the properties that are allowed to be inserted/updated
    // as you convert the model to an entity.
    public class PersonModel : EntityModel<PersonEntity>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Address { get; set; }
        public string SocialSecurity { get; set; }
    }

    // Create your MVC2 controller with an Index and Edit page.
    public class PersonController : Controller
    {
        public ActionResult Index()
        {
            // Create a list of models based on every Person entity in your
            // data set.
            var models =
                EntityModeler<PersonModel, PersonEntity>.ModelEntities(
                    YourEntities.PersonSet
                    );
            return View("Index", models);
        }

        [HttpPost]
        public ActionResult Edit(PersonModel model)
        {
            if (!ModelState.IsValid)
                return View(model);
            // Create an Person entity based on the Person model submitted
            // by a user, allowing only the FirstName, LastName, and Address
            // properties to be copied. This will protect the properties in
            // your entity that shouldn't be editable by users (for example,
            // a SocialSecurity property).
            var entity = model.CreateEntity(
                "FirstName",
                "LastName",
                "Address"
                );

            // The entity is ready to be used by your Entity Framework project,
            // so you can add your UPDATE logic and error checking here.
            // YourEntities.AddToPersonSet(entity);
            // ...
            // ...

            return RedirectToAction("Details", new { id = entity.Id });
        }
    }
+1

Source: https://habr.com/ru/post/1746274/


All Articles