开发者

Subclass controller with subclass model

开发者 https://www.devze.com 2023-03-18 15:53 出处:网络
I\'m working on an ASP.Net MVC 3 project where a lot of really simple entities just need basic CRUD support from the Administrative interface. All of these entities have a simple domain object with Id

I'm working on an ASP.Net MVC 3 project where a lot of really simple entities just need basic CRUD support from the Administrative interface. All of these entities have a simple domain object with Id, Name and there is an abstract base class NamedEntity for that purpose.

I have a basic controller, NamedEntityController<T> where T : NamedEntity that can handle simple crud operations, with all of its methods virtual. There's a nice little NamedEntityCreateOrUpdateModel<T> which is used to pass data back & forth to the views.

Now I have a particular subclass of NamedEntity called Topic that has additional properties and in particular, has a Parent/Child relationship with other Topics, so we need to capture the integer Id of the Parent which would have no meaning for other NamedEntity operations. To do that, I subclassed to TopicsController: NamedEntityController<Topic> and TopicCreateOrUpdateModel : NamedEntityCreateOrUpdateModel<Topic>

(Before anyone bites my head off, all the real work is done in a Tasks layer, I'm just simplifying the problem description here.)

The base controller defines

[HttpPost]
public virtual ActionResult Edit(NamedEntityCreateOrEditModel<T> Model)
    { ... }

The subclass defines

[HttpPost]
public override ActionResult Edit(NamedEntityCreateOrEditModel<Topic> Model)
{
TopicCreateOrEditModel tm = Model as TopicCreateOrEditModel;
...
}

(Not shown for brevity: The "Get" version of Edit for each correctly sets up either the basic NamedEntityCreateOrEditModel or the Topic-specific subclass of such and returns a View() on that Model.)

I can clearly see from debugging breakpoints, that the subclass's Edit (post) method is being called. But the cast shown above always result in null, thus defeating the point of the subclass.

If I try to create

[HttpPost开发者_StackOverflow]
public override ActionResult Edit(TopicCreateOrEditModel Model) { ... }

MVC complains that the action is ambiguous between that new method and the base class method.

Is there an easy solution to this problem? I could bypass the base class controller / model completely in this case, and basically say 'if you need additional fields beyond the simple ones, don't inherit' but that seems very wrong, especially since the domain objects ARE inheriting.


Explicitly using TryUpdateModel in the Action, you can remove the parameter and make the signature the same. This allows the use of the override keyword, like so:

public class A { }
public class B : A { }

public class AController {

    [HttpPost]
    public virtual ActionResult Edit() {
        A a = new A();
        TryUpdateModel<A>(a);

        if (ModelState.IsValid)
            a.save();
    }
}

public class BController : AController {

    [HttpPost]
    public override ActionResult Edit() {
        B b = new B();
        TryUpdateModel<B>(b);

        if (ModelState.IsValid)
            b.save();
    }
}

This fixed the AmbiguousMethodException for me, and calls to both Edit actions routed correctly. There may, however, be security concerns that I'm not aware of.


When your subclass' Edit is called, is it being passed NamedEntityCreateOrEditModel<Topic> or TopicCreateOrEditModel? The former would fail the cast (needless to say, you can cast a derived class to its base but not vice versa).


Apparently MVC passes an instance of NamedEntityCreateOrEditModel<Topic> instead of TopicCreateOrEditModel. Since I can't see the value of polymorphism for Edit in this case, I suppose explicitly mark it as new should do fine (yes, it's a hack).

[HttpPost]
public new ActionResult Edit(TopicCreateOrEditModel Model) { ... }

A more orthodox approach probably is writing a custom model binder for NamedEntityCreateOrEditModel<Topic> type to build an instance of TopicCreateOrEditModel if the current controller is TopicsController.

Or, knowing nothing about your current use of inheritance, simply remove the Edit (and similar CRUD methods) in NamedEntityController<T> as I can't see its value. If you're having good reason for its existence, ignore this suggestion.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号