Pergunta

Se eu tiver a seguinte visão fortemente tipada:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<XXX.DomainModel.Core.Locations.Location>" %>

Onde Localização é uma classe abstrata.

E eu tenho o seguinte controlador, que aceita um modelo fortemente tado por meio de um PUBLICAR:

[HttpPost]
public ActionResult Index(Location model)

Eu recebo um erro de tempo de execução informando "Não é possível criar classe abstrata

O que obviamente faz sentido. No entanto - não tenho certeza de qual é a melhor solução aqui.

Eu tenho muitos tipos de concreto (cerca de 8), e esta é uma visão em que você pode editar apenas propriedades da classe abstrata.

O que eu tenho tentou Para fazer, é criar sobrecargas para todos os diferentes tipos de concreto e executar minha lógica em um método comum.

[HttpPost]
public ActionResult Index(City model)
{
   UpdateLocationModel(model);
   return View(model);
}

[HttpPost]
public ActionResult Index(State model)
{
   UpdateLocationModel(model);
   return View(model);
}

etc etc

E depois:

[NonAction]
private void UpdateLocationModel (Location model)
{
   // ..snip - update model
}

Mas isso também não funciona, MVC reclama que os métodos de ação são ambíguos (também faz sentido).

O que nós fazemos? Simplesmente não podemos nos ligar a um modelo abstrato?

Foi útil?

Solução

Que tal escrever um fichário de modelo personalizado para esta classe abstrata:

public class CustomBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        // TODO: based on some request parameter choose the proper child type
        // to instantiate here
        return new Child();
    }
}

Isso faz sentido apenas se você tiver um formulário em que os elementos de entrada sejam inseridos dinamicamente com base em alguma ação do usuário. Nesse caso, você precisa passar por algum parâmetro adicional para indicar qual classe de concreto você precisa. Caso contrário, eu seguiria os modelos de visão de concreto como parâmetros de ação.

Outras dicas

Você também pode criar um Modelbinder genérico que funciona para todos os seus modelos abstratos. Minha solução exige que você adicione um campo oculto à sua visão chamada 'ModelTyPename'Com o valor definido com o nome do tipo de concreto que você deseja. No entanto, deve ser possível tornar essa coisa mais inteligente e escolher um tipo de concreto, combinando propriedades do tipo para campos na visualização.

Na tua Global.asax.cs arquivo em Application_start ():

ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

Custommodelbinder:

public class CustomModelBinder2 : DefaultModelBinder 
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var modelType = bindingContext.ModelType;
        if (modelType.IsAbstract)
        {
            var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ModelTypeName");
            if (modelTypeValue == null)
                throw new Exception("View does not contain ModelTypeName");

            var modelTypeName = modelTypeValue.AttemptedValue;

            var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);

            if (type != null)
            {
                var instance= bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
                bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
            }
        }
        return base.BindModel(controllerContext, bindingContext);
    }
}

Só para jogá -lo lá fora - estou muito interessado no que os outros podem responder, mas foi isso que acabei fazendo no caso em que tive uma situação semelhante;

Basicamente, eu não usei a classe modelo como um parâmetro no método de ação, em vez de passar FormCollection e testando alguns discriminadores conhecidos para descobrir qual tipo de criar/editar, depois usado TryUpdateModel de lá.

Parecia que poderia haver uma maneira melhor, mas nunca consegui pensar mais nisso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top