views:

219

answers:

4

I've got standard Create() Edit() and Delete() methods on my controllers, and I am using the EF4 Self-tracking entities.

When the edit is posted back, the model.ChangeTracker.ChangeTracking = false, and model.ChangeTracker.State = ObjectState.Added, even though I made sure those are set when retrieving the record initially.

Are the self-tracking entities not persisting the ChangeTracker class when the form is submitted? If so, how do I fix that?

public virtual ActionResult Edit(int personId)
{
    IContext context = ContextFactory.GetContext();
    EntityRepo Repo = new EntityRepo(context);
    Person d = Repo.Person.GetById(PersonId);
    d.ChangeTracker.ChangeTrackingEnabled = true;
    return View(d);
}

[HttpPost]
public virtual ActionResult Edit(int personId, Person item)
{
    try
    {
        if (ModelState.IsValid)
        {
            IContext context = ContextFactory.GetContext();
            EntityRepo Repo = new EntityRepo(context);

            // the item is returning these properties that are wrong
            //item.ChangeTracker.ChangeTrackingEnabled = false;
            //item.ChangeTracker.State = ObjectState.Added;

            Repo.Person.Update(item);
            Repo.Person.SaveChanges();

            return RedirectToAction("Index");
        }
    }
    catch
    {
    }
    return View();
}
+1  A: 

STE has one very big drawback. You have to store them in session or view state (WebForms). So it is nothing more than "new version of dataset". If you don't store STE you will have one instance for getting data and different for posting = no change tracking.

Ladislav Mrnka
+1  A: 

U should keep original STE in some hidden field. It's like your custom ViewState. In submit method u must merge original STE and new values.

Use ActionFilterAttribute for it.

Like

    public class SerializeOriginalModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var viewResult = filterContext.Result as ViewResult;

        if (viewResult == null)
            return;

        var viewModel = viewResult.ViewData.Model as ViewModel;

        if (viewModel == null || viewModel.SteObject == null)
            return;

        byte[] bytes;
        using (var stream = new MemoryStream())
        {
            var serializer = new DataContractSerializer(viewModel.SteObject.GetType());
            serializer.WriteObject(stream, viewModel.SteObject);
            bytes = stream.ToArray();
        }

        var compressed = GZipHelper.Compress(bytes);
        viewModel.SerializedSteObject = Convert.ToBase64String(compressed);
    }
}





        public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.ActionParameters == null || filterContext.ActionParameters.Count == 0)
            return;

        var viewModel  = filterContext.ActionParameters.First().Value as ViewModel;
        var serialized = filterContext.HttpContext.Request.Form["SerializedSteObject"];

        if (viewModel == null || String.IsNullOrEmpty(serialized))
            return;

        var type = filterContext.ActionParameters.First().Value.GetType().BaseType.GetGenericArguments()[0];

        var bytes = GZipHelper.Decompress(Convert.FromBase64String(serialized));
        using (var stream = new MemoryStream(bytes))
        {
            var serializer = new DataContractSerializer(type);
            viewModel.SteObject = serializer.ReadObject(stream);
        }
    }
}
Brian J. Hakim
I disagree. I don't think he should be using STEs at all.
Craig Stuntz
While he may disagree, I think the code is a clever idea...
Rick Ratayczak
It's a clever way of solving something which isn't actually a problem. Even if you use STEs, the web browser can't change their state, so tracking the state gives you no benefit.
Craig Stuntz
+3  A: 

Let's start at the beginning.

What are Self-Tracking Entities, exactly?

A Self-Tracking Entity is an entity which can do change tracking even when it is not connected to a ObjectContext. They are useful in times when you must change the entity, but cannot have it connected to an ObjectContext.

So when would I want one, really?

Mostly, when you must have distributed objects. For example, one use case is when you are making a web service which talks to a Silverlight client. However, other tools, like RIA Services may be a better fit here. Another possible use case is for a long-running task. Since an ObjectContext is intended to be a unit of work and should typically not be long-lived, having a disconnected entity might make sense here.

Do they make any sense for MVC?

Not really, no.

Let's look at this a little deeper, and examine what happens when you update an entity in MVC. The general process is like this:

  1. The browser issues a GET request for an update page.
  2. The MVC app fetches an entity, and uses it to build an update HTML page. The page is served to the browser, and most C# objects, including your entity, are disposed. At this point, you can restart the Web server, and the browser will never know the difference.
  3. The browser issues a POST request to update the entity.
  4. The MVC framework uses the data in the POST in order to materialize an instance of an edit model which is passed to the update action. This might happen to be the same type as the entity, but it is a new instance.
  5. The MVC app can update the entity and pass those changes back to the database.

Now, you could make self-tracking entities work by also including the full state of the STE in the HTML form and POSTing that back to the MVC app along with the scalar values on the entity. Then the Self-Tracking Entity might at least work.

But what benefit does this give you? The browser obviously cannot deal with your entity as a C# object. So it cannot make any changes to the entity worth tracking in terms that a Self-Tracking Entity would understand.

Craig Stuntz
It just so happens that the repo T4 Template I am generating uses STE. That's why it was chosen, not from a design standpoint.
Rick Ratayczak
I am trying to fit my T4 template into as many situations as possible the "right" way, so it does work fine for WPF/Silverlight, and can be used for asp.net mvc too, but obviously you lose the change tracking feature.
Rick Ratayczak
+1  A: 

I think you are missing the idea of Repository. You should not have an Update method in the Repository. After submitting, you should get the item again, apply the modifications and then Save.

I prefer having a service layer between client and Repository. We can always change the strategy with which we merge.

And yes, if you need to persist your STE's between requests, use session or viewstate.

Cesar