You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Razor视图表单中构建基于MyType的IMyConfig对象

Let's break this down into two main parts: dynamically switching form sections based on the selected MyType, and properly binding submitted data to the correct IMyConfig implementation. Here's a step-by-step solution:

1. Frontend: Dynamic Form Switching with jQuery

First, restructure your Razor view to wrap each config's input fields in a hidden container. We'll use jQuery to show/hide these containers when the dropdown changes.

Updated Razor View

@model MyModel

<form asp-action="ActivityCreateSubmit">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>

    <div class="form-group">
        <label asp-for="Id" class="control-label"></label>
        <input asp-for="Id" class="form-control" />
        <span asp-validation-for="Id" class="text-danger"></span>
    </div>

    <div class="form-group" style="display: none;">
        <label asp-for="Name" class="control-label"></label>
        <input asp-for="Name" class="form-control" value="@ViewBag.AppId" />
        <span asp-validation-for="Name" class="text-danger"></span>
    </div>

    <div class="form-group">
        <label asp-for="MyType" class="control-label"></label>
        <select id="selection" asp-for="MyType" asp-items="Html.GetEnumSelectList<MyType>()">
            <option value="">Select...</option>
        </select>
        <span asp-validation-for="MyType" class="text-danger"></span>
    </div>

    <!-- First Config Section -->
    <div id="FirstConfigForm" class="config-form" style="display:none;">
        <h4>First Configuration</h4>
        <div class="form-group">
            <label>Cat</label>
            <input name="MyConfig.cat" class="form-control" />
        </div>
        <div class="form-group">
            <label>Dog</label>
            <input name="MyConfig.dog" class="form-control" />
        </div>
        <div class="form-group">
            <label>Lion</label>
            <input name="MyConfig.lion" class="form-control" />
        </div>
    </div>

    <!-- Second Config Section -->
    <div id="SecondConfigForm" class="config-form" style="display:none;">
        <h4>Second Configuration</h4>
        <div class="form-group">
            <label>Bear</label>
            <input name="MyConfig.bear" class="form-control" />
        </div>
        <div class="form-group">
            <label>Pig</label>
            <input name="MyConfig.pig" type="number" class="form-control" />
        </div>
        <div class="form-group">
            <label>Fish</label>
            <input name="MyConfig.fish" class="form-control" />
        </div>
        <div class="form-group">
            <label>Shark</label>
            <input name="MyConfig.shark" class="form-control" />
        </div>
        <div class="form-group">
            <label>Dolphin</label>
            <input name="MyConfig.dolphin" class="form-control" />
        </div>
    </div>

    <!-- Third Config Section (mirror the pattern above) -->
    <div id="ThirdConfigForm" class="config-form" style="display:none;">
        <h4>Third Configuration</h4>
        <!-- Add your ThirdConfig fields here with name="MyConfig.[PropertyName]" -->
    </div>

    <div class="form-group">
        <input type="submit" value="Create" class="btn btn-default" />
    </div>
</form>

@section Scripts {
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        $(document).ready(function() {
            // Initialize form visibility if editing an existing model
            const initialType = $("#MyType").val();
            if (initialType) showConfigForm(initialType);

            // Listen for dropdown changes
            $("#selection").change(function() {
                const selectedType = $(this).val();
                showConfigForm(selectedType);
            });

            function showConfigForm(type) {
                // Hide all config sections
                $(".config-form").hide();
                // Show the selected section
                if (type) $("#" + type + "Form").show();
            }
        });
    </script>
}

Key Note: Use name="MyConfig.PropertyName" for input fields—this tells ASP.NET Core how to map values to the MyConfig property during binding.

2. Backend: Model Binding for IMyConfig

Since IMyConfig is an interface, ASP.NET Core's default model binder doesn't know which concrete class to instantiate. You have two solid options:

Create a binder that selects the correct IMyConfig implementation based on the selected MyType:

public class MyConfigModelBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        // Get the selected MyType from the request
        var myTypeValue = bindingContext.HttpContext.Request.Form["MyType"];
        if (!Enum.TryParse<MyType>(myTypeValue, out var myType))
        {
            bindingContext.ModelState.AddModelError("MyType", "Invalid configuration type selected");
            return;
        }

        // Initialize the correct config class
        IMyConfig config = myType switch
        {
            MyType.FirstConfig => new FirstConfig(),
            MyType.SecondConfig => new SecondConfig(),
            MyType.ThirdConfig => new ThirdConfig(), // Add your ThirdConfig here
            _ => null
        };

        if (config == null)
        {
            bindingContext.ModelState.AddModelError("MyType", "Unsupported configuration type");
            return;
        }

        // Bind the config properties using the default binder
        var metadataProvider = bindingContext.HttpContext.RequestServices.GetRequiredService<IModelMetadataProvider>();
        var configBindingContext = new ModelBindingContext(
            bindingContext.ActionContext,
            bindingContext.ValueProvider,
            metadataProvider.GetMetadataForType(config.GetType()),
            null,
            "MyConfig"
        );

        var defaultBinder = bindingContext.HttpContext.RequestServices.GetRequiredService<IModelBinder>();
        await defaultBinder.BindModelAsync(configBindingContext);

        if (configBindingContext.Result.IsModelSet)
        {
            bindingContext.Result = ModelBindingResult.Success(configBindingContext.Result.Model as IMyConfig);
        }
        else
        {
            bindingContext.ModelState.AddModelError("MyConfig", "Failed to bind configuration values");
        }
    }
}

Register the binder in your startup code (Program.cs/Startup.cs):

services.AddControllersWithViews(options =>
{
    options.ModelBinderProviders.Insert(0, new BinderTypeModelBinderProvider(typeof(IMyConfig), new MyConfigModelBinder()));
});

Or annotate the MyConfig property directly:

public class MyModel 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public MyType MyType { get; set; } 
    [ModelBinder(typeof(MyConfigModelBinder))]
    public IMyConfig MyConfig { get; set; } 
}

Option 2: Manual Binding in Controller (Simpler for Small Apps)

If you prefer a lighter approach, manually map form values to the correct config class in your controller:

public ActionResult ActivityCreateSubmit(MyModel model)
{
    if (!ModelState.IsValid)
        return View("ActivityCreate", model);

    // Create the correct config instance based on MyType
    IMyConfig config = model.MyType switch
    {
        MyType.FirstConfig => new FirstConfig
        {
            cat = Request.Form["MyConfig.cat"],
            dog = Request.Form["MyConfig.dog"],
            lion = Request.Form["MyConfig.lion"]
        },
        MyType.SecondConfig => new SecondConfig
        {
            bear = Request.Form["MyConfig.bear"],
            pig = int.TryParse(Request.Form["MyConfig.pig"], out var pig) ? pig : 0,
            fish = Request.Form["MyConfig.fish"],
            shark = Request.Form["MyConfig.shark"],
            dolphin = Request.Form["MyConfig.dolphin"]
        },
        MyType.ThirdConfig => new ThirdConfig
        {
            // Map your ThirdConfig fields here
        },
        _ => null
    };

    if (config == null)
    {
        ModelState.AddModelError("MyType", "Invalid configuration type");
        return View("ActivityCreate", model);
    }

    model.MyConfig = config;
    _activityTable.AddActivity(model); // Use your injected ActivityTable

    return RedirectToAction("Index", "Home");
}
3. Bonus: Add Validation

To validate config fields, add data annotations to your concrete config classes and use asp-for instead of manual name attributes. For example:

public class FirstConfig : IMyConfig 
{ 
    [Required]
    public string cat { get; set; } 
    [Required]
    public string dog { get; set; } 
    // ... rest of the class
}

Then update the input in the view:

<input asp-for="MyConfig.cat" class="form-control" />
<span asp-validation-for="MyConfig.cat" class="text-danger"></span>

内容的提问来源于stack exchange,提问作者asdasd2a43qaad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:33:28