এএসপিএন এমভিসি-তে মাল্টি-স্টেপ রেজিস্ট্রেশন প্রসেসের সমস্যাগুলি (স্প্লিট ভিউ মডেলস, একক মডেল)


117

আমার কাছে মাল্টি-স্টেপ রেজিস্ট্রেশন প্রক্রিয়া রয়েছে , ডোমেন স্তরের একক অবজেক্টের ব্যাকড রয়েছে , যার বৈশিষ্ট্যগুলিতে সংজ্ঞা দেওয়া আছে valid

যখন ডোমেনটি বহু দর্শনতে বিভক্ত হয়ে যায় তখন কীভাবে আমার ডোমেন অবজেক্টটি বৈধতা দেওয়া উচিত এবং পোস্ট করার সময় আমাকে প্রথম দর্শনে আংশিকভাবে সংরক্ষণ করতে হবে?

আমি সেশনগুলি ব্যবহার করার বিষয়ে ভেবেছিলাম কিন্তু এটি সম্ভব নয় কারণ প্রক্রিয়াটি দীর্ঘ এবং ডেটার পরিমাণ বেশি, সুতরাং আমি সেশনটি ব্যবহার করতে চাই না।

আমি সমস্ত ডেটা রিলেশনাল ইন-মেমোরি ডিবিতে (মূল স্ক্রিনের সাথে একই স্কিমার সাথে) সংরক্ষণ করার এবং তারপরে সেই ডেটাটি প্রধান ডিবিতে ফ্লাশ করে ফেলার বিষয়ে চিন্তা করি তবে আমার যে পরিষেবাগুলির (মতামতগুলিতে অনুরোধ করা হয়েছে) যারা কাজ করে তাদের মধ্যে রুট করা উচিত বলে আমি মনে করি প্রধান ডিবি এবং মেমরি ডিবি।

আমি একটি মার্জিত এবং পরিষ্কার সমাধান খুঁজছি (আরও সুনির্দিষ্টভাবে একটি সেরা অনুশীলন)।

আপডেট এবং স্পষ্টকরণ:

@ ডারিন আপনার চিন্তাশীল জবাবের জন্য আপনাকে ধন্যবাদ, আমি এখন অবধি ঠিক এটিই করেছি। তবে ঘটনাক্রমে আমার একটি অনুরোধ রয়েছে যার এতে অনেক সংযুক্তি রয়েছে, আমি একটি নকশা তৈরি করেছি Step2Viewযাতে কোন ব্যবহারকারী এটিতে অবিচ্ছিন্নভাবে নথিগুলি আপলোড করতে পারে তবে সেই সংযুক্তিগুলি অন্য টেবিলের সাথে রেফারেন্সিয়াল সম্পর্কযুক্ত একটি টেবিলে সংরক্ষণ করা উচিত যা আগে সংরক্ষণ করা উচিত ছিল those Step1View

সুতরাং আমার ডোমেন অবজেক্টটি Step1(আংশিকভাবে) তে সংরক্ষণ করা উচিত , তবে আমি পারি না, ব্যাকড কোর ডোমেন অবজেক্টটি আংশিকভাবে ম্যাপ করা স্টেপ 1 এর ভিউমোডেলকে রূপান্তরিত হওয়া প্রপস ছাড়া সংরক্ষণ করা যায় না Step2ViewModel


@ জনি, আপনি কি এর আপলোড টুকরোটি খুঁজে পেয়েছেন? আমি আপনার মস্তিষ্ক বাছাই করতে চাই আমি এই সঠিক বিষয়ে কাজ করছি।
ডগ চেম্বারলাইন

1
এই ব্লগে সমাধানটি বেশ সহজ এবং সোজা এগিয়ে। এটি তাদের দৃশ্যমানতা এবং স্ববিরোধী jquery বৈধতা পৃথক করে ডিভগুলি "পদক্ষেপ" হিসাবে ব্যবহার করে।
দিমিত্রি এফিমেনকো

উত্তর:


229

প্রথমে আপনার ভিউগুলিতে কোনও ডোমেন অবজেক্ট ব্যবহার করা উচিত নয়। আপনার ভিউ মডেলগুলি ব্যবহার করা উচিত। প্রতিটি দর্শনীয় মডেলটিতে কেবলমাত্র সেই বৈশিষ্ট্য থাকবে যা প্রদত্ত দর্শন দ্বারা প্রয়োজনীয় এবং সেইসাথে এই প্রদত্ত দৃশ্যের সাথে সম্পর্কিত বৈধতা বৈশিষ্ট্যগুলিও অন্তর্ভুক্ত করবে। সুতরাং আপনার কাছে যদি 3 টি পদক্ষেপ উইজার্ড থাকে তবে এর অর্থ আপনার কাছে 3 টি ভিউ মডেল থাকবে, প্রতিটি পদক্ষেপের জন্য একটি:

public class Step1ViewModel
{
    [Required]
    public string SomeProperty { get; set; }

    ...
}

public class Step2ViewModel
{
    [Required]
    public string SomeOtherProperty { get; set; }

    ...
}

ইত্যাদি। এই সমস্ত দর্শন মডেলগুলিকে একটি প্রধান উইজার্ড দর্শন মডেল দ্বারা সমর্থন করা যেতে পারে:

public class WizardViewModel
{
    public Step1ViewModel Step1 { get; set; }
    public Step2ViewModel Step2 { get; set; }
    ...
}

তারপরে আপনার কাছে উইজার্ড প্রক্রিয়াটির প্রতিটি ধাপ রেন্ডারিং এবং মূলটি WizardViewModelদৃশ্যতে পাস করার জন্য নিয়ন্ত্রক পদক্ষেপ থাকতে পারে । আপনি যখন নিয়ামক পদক্ষেপের ভিতরে প্রথম পদক্ষেপে থাকেন তখন আপনি Step1সম্পত্তিটি আরম্ভ করতে পারেন । তারপরে দৃশ্যের ভিতরে আপনি ফর্মটি তৈরি করতে পারবেন যার মাধ্যমে ব্যবহারকারীকে পদক্ষেপ 1 সম্পর্কে বৈশিষ্ট্যগুলি পূরণ করতে পারবেন যখন ফর্মটি জমা দেওয়া হবে তখন নিয়ামক পদক্ষেপটি কেবল পদক্ষেপ 1 এর বৈধতা নিয়মগুলি প্রয়োগ করবে:

[HttpPost]
public ActionResult Step1(Step1ViewModel step1)
{
    var model = new WizardViewModel 
    {
        Step1 = step1
    };

    if (!ModelState.IsValid)
    {
        return View(model);
    }
    return View("Step2", model);
}

এখন পদক্ষেপ 2 দেখার অভ্যন্তরে আপনি MVC ফিউচার থেকে এইচটিএমএল.সিরাইজ হেল্পার ব্যবহার করতে পারেন ফর্মের অভ্যন্তরে কোনও লুকানো ক্ষেত্রের ধাপ 1 ক্রমিকায়িত করার জন্য (যদি আপনি চান কোনও ভিউস্টেটের সাজান):

@using (Html.BeginForm("Step2", "Wizard"))
{
    @Html.Serialize("Step1", Model.Step1)
    @Html.EditorFor(x => x.Step2)
    ...
}

এবং পদক্ষেপ 2 এর পোস্ট পদক্ষেপের ভিতরে:

[HttpPost]
public ActionResult Step2(Step2ViewModel step2, [Deserialize] Step1ViewModel step1)
{
    var model = new WizardViewModel 
    {
        Step1 = step1,
        Step2 = step2
    }

    if (!ModelState.IsValid)
    {
        return View(model);
    }
    return View("Step3", model);
}

এবং তাই শেষ অবধি পৌঁছা পর্যন্ত আপনি WizardViewModelসমস্ত ডেটা ভরাবেন। তারপরে আপনি ভিউ মডেলটি আপনার ডোমেন মডেলটিতে ম্যাপ করবেন এবং এটি প্রক্রিয়াজাতকরণের জন্য পরিষেবা স্তরে প্রেরণ করবেন। পরিষেবা স্তরটি কোনও বৈধতা নিয়ম নিজেই সম্পাদন করতে পারে এবং আরও ...

এছাড়াও আরও একটি বিকল্প রয়েছে: জাভাস্ক্রিপ্ট ব্যবহার করে এবং সমস্ত একই পৃষ্ঠায় রেখে দেওয়া। উইজার্ড কার্যকারিতা সরবরাহ করে এমন অনেক জেকোয়ারি প্লাগইন রয়েছে ( স্টেপি একটি দুর্দান্ত)। এটি মূলত ক্লায়েন্টের উপর ডিভগুলি দেখানো এবং লুকিয়ে রাখার বিষয় যেখানে আপনাকে আর পদক্ষেপগুলির মধ্যে স্থিতিশীল অবস্থার বিষয়ে চিন্তা করার দরকার নেই।

তবে আপনি যে সমাধানটি চয়ন করেন তা বিবেচনা করুন না কেন সর্বদা ভিউ মডেলগুলি ব্যবহার করুন এবং সেই ভিউ মডেলগুলিতে বৈধতা কার্যকর করুন। যতক্ষণ আপনি আপনার ডোমেন মডেলগুলিতে ডেটা টীকা বিশিষ্টতা যাচাইকরণের বৈশিষ্ট্যগুলি আঁকিয়ে রাখছেন আপনি ডোমেন মডেলগুলি দর্শন হিসাবে অভিযোজিত না হওয়ায় আপনি খুব কঠোর লড়াই করবেন।


হালনাগাদ:

ঠিক আছে, অসংখ্য মন্তব্যের কারণে আমি এই সিদ্ধান্তে পৌঁছেছি যে আমার উত্তর পরিষ্কার ছিল না। এবং আমি অবশ্যই একমত হতে হবে। সুতরাং আমার উদাহরণটি আরও বিস্তৃত করার চেষ্টা করি।

আমরা একটি ইন্টারফেসটি সংজ্ঞায়িত করতে পারি যা সমস্ত ধাপের দর্শনের মডেলগুলি প্রয়োগ করা উচিত (এটি কেবল একটি চিহ্নিত ইন্টারফেস):

public interface IStepViewModel
{
}

তারপরে আমরা উইজার্ডের জন্য 3 টি ধাপ সংজ্ঞায়িত করব যেখানে প্রতিটি ধাপে অবশ্যই কেবলমাত্র তার জন্য প্রয়োজনীয় প্রাসঙ্গিক বৈশিষ্ট্য এবং প্রাসঙ্গিক বৈধতা বৈশিষ্ট্য থাকবে:

[Serializable]
public class Step1ViewModel: IStepViewModel
{
    [Required]
    public string Foo { get; set; }
}

[Serializable]
public class Step2ViewModel : IStepViewModel
{
    public string Bar { get; set; }
}

[Serializable]
public class Step3ViewModel : IStepViewModel
{
    [Required]
    public string Baz { get; set; }
}

এরপরে আমরা মূল উইজার্ড ভিউ মডেলটি সংজ্ঞায়িত করি যা পদক্ষেপের তালিকা এবং বর্তমান পদক্ষেপ সূচক নিয়ে গঠিত:

[Serializable]
public class WizardViewModel
{
    public int CurrentStepIndex { get; set; }
    public IList<IStepViewModel> Steps { get; set; }

    public void Initialize()
    {
        Steps = typeof(IStepViewModel)
            .Assembly
            .GetTypes()
            .Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t))
            .Select(t => (IStepViewModel)Activator.CreateInstance(t))
            .ToList();
    }
}

তারপরে আমরা নিয়ামকের দিকে এগিয়ে যাই:

public class WizardController : Controller
{
    public ActionResult Index()
    {
        var wizard = new WizardViewModel();
        wizard.Initialize();
        return View(wizard);
    }

    [HttpPost]
    public ActionResult Index(
        [Deserialize] WizardViewModel wizard, 
        IStepViewModel step
    )
    {
        wizard.Steps[wizard.CurrentStepIndex] = step;
        if (ModelState.IsValid)
        {
            if (!string.IsNullOrEmpty(Request["next"]))
            {
                wizard.CurrentStepIndex++;
            }
            else if (!string.IsNullOrEmpty(Request["prev"]))
            {
                wizard.CurrentStepIndex--;
            }
            else
            {
                // TODO: we have finished: all the step partial
                // view models have passed validation => map them
                // back to the domain model and do some processing with
                // the results

                return Content("thanks for filling this form", "text/plain");
            }
        }
        else if (!string.IsNullOrEmpty(Request["prev"]))
        {
            // Even if validation failed we allow the user to
            // navigate to previous steps
            wizard.CurrentStepIndex--;
        }
        return View(wizard);
    }
}

এই নিয়ামক সম্পর্কে কয়েকবার মন্তব্য:

  • সূচক পোস্টের পদক্ষেপটি [Deserialize]মাইক্রোসফ্ট ফিউচার লাইব্রেরি থেকে বৈশিষ্ট্যগুলি ব্যবহার করে যাতে আপনি MvcContribন্যুগেট ইনস্টল করেছেন তা নিশ্চিত করুন । এজন্য দর্শনের মডেলগুলিকে [Serializable]গুণাবলী দিয়ে সজ্জিত করা উচিত
  • সূচী পোস্ট পদক্ষেপটি আর্গুমেন্ট হিসাবে একটি IStepViewModelইন্টারফেস নেয় যাতে এটি বোঝার জন্য আমাদের একটি কাস্টম মডেল বাইন্ডার প্রয়োজন।

এখানে সম্পর্কিত মডেল বাইন্ডার:

public class StepViewModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var stepTypeValue = bindingContext.ValueProvider.GetValue("StepType");
        var stepType = Type.GetType((string)stepTypeValue.ConvertTo(typeof(string)), true);
        var step = Activator.CreateInstance(stepType);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => step, stepType);
        return step;
    }
}

এই বাইন্ডারটিতে স্টেপটাইপ নামে একটি বিশেষ লুকানো ক্ষেত্র ব্যবহার করা হয়েছে যা প্রতিটি পদক্ষেপের কংক্রিট ধরণের থাকবে এবং যা আমরা প্রতিটি অনুরোধে প্রেরণ করব।

এই মডেল বাইন্ডার এতে নিবন্ধিত হবে Application_Start:

ModelBinders.Binders.Add(typeof(IStepViewModel), new StepViewModelBinder());

ধাঁধা শেষ হারিয়ে যাওয়া বিট ভিউ হয়। এখানে মূল ~/Views/Wizard/Index.cshtmlদৃষ্টিভঙ্গি:

@using Microsoft.Web.Mvc
@model WizardViewModel

@{
    var currentStep = Model.Steps[Model.CurrentStepIndex];
}

<h3>Step @(Model.CurrentStepIndex + 1) out of @Model.Steps.Count</h3>

@using (Html.BeginForm())
{
    @Html.Serialize("wizard", Model)

    @Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
    @Html.EditorFor(x => currentStep, null, "")

    if (Model.CurrentStepIndex > 0)
    {
        <input type="submit" value="Previous" name="prev" />
    }

    if (Model.CurrentStepIndex < Model.Steps.Count - 1)
    {
        <input type="submit" value="Next" name="next" />
    }
    else
    {
        <input type="submit" value="Finish" name="finish" />
    }
}

এবং এই কাজটি করতে আপনার প্রয়োজন। অবশ্যই যদি আপনি চান আপনি কাস্টম সম্পাদক টেম্পলেটটি সংজ্ঞায়িত করে উইজার্ডের কিছু বা সমস্ত পদক্ষেপের চেহারা এবং অনুভূতিটি ব্যক্তিগতকৃত করতে পারেন। উদাহরণস্বরূপ আসুন এটি ২ য় পদক্ষেপের জন্য করি So সুতরাং আমরা একটি ~/Views/Wizard/EditorTemplates/Step2ViewModel.cshtmlআংশিক সংজ্ঞা দিই :

@model Step2ViewModel

Special Step 2
@Html.TextBoxFor(x => x.Bar)

কাঠামোটি কেমন দেখাচ্ছে তা এখানে:

এখানে চিত্র বর্ণনা লিখুন

অবশ্যই উন্নতির জন্য জায়গা আছে। সূচক পোস্টের ক্রিয়া দেখতে s..t। এটিতে খুব বেশি কোড রয়েছে। আরও সরলকরণের সাথে ইনডেক্স, বর্তমান সূচী ব্যবস্থাপনা, বর্তমান পদক্ষেপের উইজার্ডে অনুলিপি করা, যেমন ... অন্য মডেল বাইন্ডারে রূপান্তর করা হবে all যাতে শেষ পর্যন্ত আমরা শেষ:

[HttpPost]
public ActionResult Index(WizardViewModel wizard)
{
    if (ModelState.IsValid)
    {
        // TODO: we have finished: all the step partial
        // view models have passed validation => map them
        // back to the domain model and do some processing with
        // the results
        return Content("thanks for filling this form", "text/plain");
    }
    return View(wizard);
}

যা আরও বেশি পোস্টের ক্রিয়াকলাপগুলি দেখতে হবে। আমি এই উন্নতিটি পরের বারের জন্য রেখে দিচ্ছি :-)


1
@ ডগ চেম্বারলাইন, আমি আমার ভিউ মডেল এবং ডোমেন মডেলগুলির মধ্যে রূপান্তর করতে অটোম্যাপার ব্যবহার করি।
দারিন দিমিত্রভ

1
@ ডগ চেম্বারলাইন, দয়া করে আমার আপডেট হওয়া উত্তরটি দেখুন। আমি আশা করি এটি আমার প্রাথমিক পোস্টের চেয়ে জিনিসগুলিকে কিছুটা আরও স্পষ্ট করে তুলবে।
দারিন দিমিত্রভ

20
+1 @ জানি: আপনাকে ডারিনকে এই উত্তরের জন্য 50 টি পয়েন্ট দেওয়া দরকার। এটি খুব ব্যাপক। এবং তিনি ভিউমোডেল এবং ডোমেন মডেলগুলি নয় ;-) ব্যবহারের প্রয়োজনীয়তার পুনরাবৃত্তি করতে সক্ষম হন
টম চ্যান্টলার

3
আমি কোথাও ডেসরিয়ালাইজ বৈশিষ্ট্যটি খুঁজে পাচ্ছি না ... এছাড়াও এমভিসিএন্ট্রিবিজের কোডপ্লেক্স পৃষ্ঠায় আমি এই 94fa6078a115 খুঁজে পেয়েছি জেরেমি স্কিনার অগস্ট 1 2010 এ 5:55 অপরাহ্ন 0 অবহেলিত ডিসরিয়ালাইজ বাইন্ডার সরান আপনি আমাকে কী প্রস্তাব দিচ্ছেন?
চক নরিস

2
আমি একটি সমস্যা পেয়েছি যেখানে আমি আমার মতামতগুলির নাম না দিয়েছি পদক্ষেপ 1, পদক্ষেপ 2, ইত্যাদি ... আমার নামকরণ করা হয়েছে আরও কিছু অর্থবহ, তবে বর্ণানুক্রমিক নয়। সুতরাং, আমি আমার মডেলগুলি ভুল ক্রমে পেয়েছি in আমি আইএসটিপভিউমোডেল ইন্টারফেসে একটি স্টেপবার্বার সম্পত্তি যুক্ত করেছি। উইজার্ডভিউমোডেলের প্রাথমিক পদ্ধতিতে এখন আমি এটি অনুসারে বাছাই করতে পারি।
জেফ রেড্ডি

13

অমিত বগার উত্তরের পরিপূরক করতে আপনি আমি যা করেছি তার নীচে পাবেন। এমনকি কম মার্জিত হলেও আমি ডারিনের উত্তরের চেয়ে এই পদ্ধতিটিকে সহজ।

নিয়ামক:

public ActionResult Step1()
{
    if (Session["wizard"] != null)
    {
        WizardProductViewModel wiz = (WizardProductViewModel)Session["wizard"];
        return View(wiz.Step1);
    }
    return View();
}

[HttpPost]
public ActionResult Step1(Step1ViewModel step1)
{
    if (ModelState.IsValid)
    {
        WizardProductViewModel wiz = new WizardProductViewModel();
        wiz.Step1 = step1;
        //Store the wizard in session
        Session["wizard"] = wiz;
        return RedirectToAction("Step2");
    }
    return View(step1);
}

public ActionResult Step2()
{
    if (Session["wizard"] != null)
    {
        WizardProductViewModel wiz = (WizardProductViewModel)Session["wizard"];
        return View(wiz.Step2);
    }
    return View();
}

[HttpPost]
public ActionResult Step2(Step2ViewModel step2)
{
    if (ModelState.IsValid)
    {
        //Pull the wizard from session
        WizardProductViewModel wiz = (WizardProductViewModel)Session["wizard"];
        wiz.Step2 = step2;
        //Store the wizard in session
        Session["wizard"] = wiz;
        //return View("Step3");
        return RedirectToAction("Step3");
    }
    return View(step2);
}

public ActionResult Step3()
{
    WizardProductViewModel wiz = (WizardProductViewModel)Session["wizard"];
    return View(wiz.Step3);
}

[HttpPost]
public ActionResult Step3(Step3ViewModel step3)
{
    if (ModelState.IsValid)
    {
        //Pull the wizard from session
        WizardProductViewModel wiz = (WizardProductViewModel)Session["wizard"];
        wiz.Step3 = step3;
        //Save the data
        Product product = new Product
        {
            //Binding with view models
            Name = wiz.Step1.Name,
            ListPrice = wiz.Step2.ListPrice,
            DiscontinuedDate = wiz.Step3.DiscontinuedDate
        };

        db.Products.Add(product);
        db.SaveChanges();
        return RedirectToAction("Index", "Product");
    }
    return View(step3);
}

মডেল:

 [Serializable]
    public class Step1ViewModel 
    {
        [Required]
        [MaxLength(20, ErrorMessage="Longueur max de 20 caractères")]
        public string Name { get; set; }

    }

    [Serializable]
    public class Step2ViewModel
    {
        public Decimal ListPrice { get; set; }

    }

    [Serializable]
    public class Step3ViewModel
    {
        public DateTime? DiscontinuedDate { get; set; }
    }

    [Serializable]
    public class WizardProductViewModel
    {
        public Step1ViewModel Step1  { get; set; }
        public Step2ViewModel Step2  { get; set; }
        public Step3ViewModel Step3  { get; set; }
    }

11

আমি আপনাকে Jquery ব্যবহার করে ক্লায়েন্টের সম্পূর্ণ প্রক্রিয়া অবস্থা বজায় রাখার পরামর্শ দিচ্ছি।

উদাহরণস্বরূপ আমাদের একটি থ্রি স্টেপ উইজার্ড প্রক্রিয়া রয়েছে।

  1. পদক্ষেপ 1 এর সাথে উপস্থাপিত ব্যবহারকারী যার উপর "বোতাম" লেবেলযুক্ত একটি বোতাম রয়েছে
  2. পরবর্তী ক্লিক করার সময় আমরা একটি অজ্যাক্স অনুরোধ করি এবং স্টেপ 2 নামে একটি ডিআইভি তৈরি করি এবং সেই ডিআইভিতে এইচটিএমএল লোড করি।
  3. স্টিপি 3 তে আমাদের বোতামে ক্লিক করে "সমাপ্ত" লেবেলযুক্ত একটি বোতাম $ .পস্ট কল ব্যবহার করে ডেটা পোস্ট করে post

এইভাবে আপনি সহজেই আপনার ডোমেন অবজেক্টটি সরাসরি ফর্ম পোস্ট ডেটা থেকে তৈরি করতে পারেন এবং যদি ডেটাতে ত্রুটি থাকে তবে সমস্ত ত্রুটি বার্তা ধারণ করে বৈধ JSON ফিরিয়ে দেয় এবং একটি ডিভিতে প্রদর্শন করে।

পদক্ষেপগুলি বিভক্ত করুন

public class Wizard 
{
  public Step1 Step1 {get;set;}
  public Step2 Step2 {get;set;}
  public Step3 Step3 {get;set;}
}

public ActionResult Step1(Step1 step)
{
  if(Model.IsValid)
 {
   Wizard wiz = new Wizard();
   wiz.Step1 = step;
  //Store the Wizard in Session;
  //Return the action
 }
}

public ActionResult Step2(Step2 step)
{
 if(Model.IsValid)
 {
   //Pull the Wizard From Session
   wiz.Step2=step;
 }
}

উপরেরটি হ'ল একটি প্রদর্শন যা শেষ ফলাফল অর্জনে আপনাকে সহায়তা করবে। চূড়ান্ত পদক্ষেপে আপনাকে ডোমেন অবজেক্ট তৈরি করতে হবে এবং উইজার্ড অবজেক্ট এবং স্টোর থেকে ডাটাবেসে সঠিক মানগুলি তৈরি করতে হবে।


হ্যাঁ, এটি একটি আকর্ষণীয় সমাধান, তবে দুর্ভাগ্যক্রমে ক্লায়েন্টের কাছে আমাদের খুব কম ইন্টারনেট সংযোগ রয়েছে এবং তার উচিত আমাদের একগুচ্ছ ফাইল send সুতরাং আমরা সেই সমাধানটিকে আগে প্রত্যাখ্যান করেছি।
জাহান

আপনি কি দয়া করে আমাকে ক্লায়েন্টটি আপলোড করতে চলেছে সেই পরিমাণের ভলিউমটি আমাকে জানান।
অমিত বগা

বেশ কয়েকটি ফাইল, প্রায় দশ, প্রতিটি এক প্রায় 1 এমবি।
জাহান

5

উইজার্ডস একটি সাধারণ মডেল প্রক্রিয়াজাতকরণের কেবল সহজ পদক্ষেপ। উইজার্ডের জন্য একাধিক মডেল তৈরি করার কোনও কারণ নেই। আপনি যা করবেন তা হ'ল একটি একক মডেল তৈরি করা এবং এটি একটি একক নিয়ামকের ক্রিয়াকলাপের মধ্যে পাস করা।

public class MyModel
{
     [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public Guid Id { get; set };
     public string StepOneData { get; set; }
     public string StepTwoData { get; set; }
}

উপরের কোড বোকা সহজ তাই আপনার ক্ষেত্রগুলিকে সেখানে প্রতিস্থাপন করুন। এরপরে আমরা একটি সাধারণ ক্রিয়া দিয়ে শুরু করি যা আমাদের উইজার্ডকে সূচনা করে।

    public ActionResult WizardStep1()
    {
        return View(new MyModel());
    }

এটি ভিউটিকে "উইজার্ডস্টেপ 1 সিএসটিএমটিএল (যদি রেজার ব্যবহার করা হয়) হিসাবে কল করে। আপনি চাইলে তৈরি টেম্পলেট উইজার্ডটি ব্যবহার করতে পারেন We

<WizardStep1.cshtml>
@using (Html.BeginForm("WizardStep2", "MyWizard")) {

নোটের বিষয় হ'ল আমরা এটিকে অন্য একটি ক্রিয়াতে পোস্ট করব; উইজার্ডস্টেপ 2 ক্রিয়া

    [HttpPost]
    public ActionResult WizardStep2(MyModel myModel)
    {
        return ModelState.IsValid ? View(myModel) : View("WizardStep1", myModel);
    }

এই ক্রিয়াটিতে আমরা আমাদের মডেলটি বৈধ কিনা তা পরীক্ষা করে দেখি এবং যদি তাই হয় তবে আমরা এটি আমাদের উইজার্ডস্টেপ 2 সিএসটিএমএল ভিউতে প্রেরণ করি অন্যথায় আমরা বৈধতা ত্রুটি সহ এটিকে আবার এক ধাপে প্রেরণ করি। প্রতিটি পদক্ষেপে আমরা এটি পরবর্তী ধাপে প্রেরণ করি, সেই পদক্ষেপটি বৈধতা দিন এবং এগিয়ে যান। এখন কিছু বুদ্ধিমান বিকাশকারীরা ভালভাবে বলতে পারে যে আমরা যদি পদক্ষেপের মধ্যে [প্রয়োজনীয়] বৈশিষ্ট্য বা অন্যান্য ডেটা টীকাগুলি ব্যবহার করি তবে আমরা এ জাতীয় ধাপগুলির মধ্যে যেতে পারি না। এবং আপনি সঠিক হবেন, সুতরাং এখনও পরীক্ষা করা হয়নি এমন আইটেমগুলির ত্রুটিগুলি সরান। নীচের মত।

    [HttpPost]
    public ActionResult WizardStep3(MyModel myModel)
    {
        foreach (var error in ModelState["StepTwoData"].Errors)
        {
            ModelState["StepTwoData"].Errors.Remove(error);
        }

শেষ পর্যন্ত আমরা মডেলটি একবার ডেটা স্টোরে সংরক্ষণ করব। এটি এমন কোনও ব্যবহারকারীকে বাধা দেয় যা উইজার্ড শুরু করে তবে এটি ডাটাবেসে অসম্পূর্ণ ডেটা সংরক্ষণ না করে শেষ করে না।

আমি আশা করি আপনি উইজার্ড প্রয়োগের এই পদ্ধতিটি পূর্বে উল্লিখিত যে কোনও পদ্ধতির তুলনায় ব্যবহার এবং পরিচালনা বজায় রাখার পক্ষে অনেক সহজ find

পড়ার জন্য ধন্যবাদ.


আমি কি চেষ্টা করতে পারি এমন একটি সম্পূর্ণ সমাধানে আপনার এটি আছে? ধন্যবাদ
এমপিওরা

5

আমি এই প্রয়োজনীয়তাগুলি পরিচালনা করার নিজস্ব পদ্ধতি ভাগ করতে চেয়েছিলাম। আমি মোটেও সেশনস্টেট ব্যবহার করতে চাইনি, না আমি এটি ক্লায়েন্টের পক্ষ থেকে পরিচালনা করতে চাইনি এবং সিরিয়ালাইজ পদ্ধতিতে এমভিসি ফিউচারের প্রয়োজন যা আমি আমার প্রকল্পে অন্তর্ভুক্ত করতে চাইনি।

পরিবর্তে আমি একটি এইচটিএমএল সহায়ক তৈরি করেছি যা মডেলের সমস্ত বৈশিষ্ট্যের মধ্যে পুনরাবৃত্তি করবে এবং প্রত্যেকটির জন্য একটি কাস্টম লুকানো উপাদান তৈরি করবে। যদি এটি একটি জটিল সম্পত্তি হয় তবে এটি এর উপর পুনরাবৃত্তভাবে চলবে।

আপনার ফর্মে এগুলি প্রতিটি "উইজার্ড" পদক্ষেপে নতুন মডেলের ডেটা সহ কন্ট্রোলারে পোস্ট করা হবে।

আমি এমভিসি 5 এর জন্য এটি লিখেছিলাম।

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Reflection;

namespace YourNamespace
{
    public static class CHTML
    {
        public static MvcHtmlString HiddenClassFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
        {
            return HiddenClassFor(html, expression, null);
        }

        public static MvcHtmlString HiddenClassFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
        {
            ModelMetadata _metaData = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

            if (_metaData.Model == null)
                return MvcHtmlString.Empty;

            RouteValueDictionary _dict = htmlAttributes != null ? new RouteValueDictionary(htmlAttributes) : null;

            return MvcHtmlString.Create(HiddenClassFor(html, expression, _metaData, _dict).ToString());
        }

        private static StringBuilder HiddenClassFor<TModel>(HtmlHelper<TModel> html, LambdaExpression expression, ModelMetadata metaData, IDictionary<string, object> htmlAttributes)
        {
            StringBuilder _sb = new StringBuilder();

            foreach (ModelMetadata _prop in metaData.Properties)
            {
                Type _type = typeof(Func<,>).MakeGenericType(typeof(TModel), _prop.ModelType);
                var _body = Expression.Property(expression.Body, _prop.PropertyName);
                LambdaExpression _propExp = Expression.Lambda(_type, _body, expression.Parameters);

                if (!_prop.IsComplexType)
                {
                    string _id = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(_propExp));
                    string _name = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(_propExp));
                    object _value = _prop.Model;

                    _sb.Append(MinHiddenFor(_id, _name, _value, htmlAttributes));
                }
                else
                {
                    if (_prop.ModelType.IsArray)
                        _sb.Append(HiddenArrayFor(html, _propExp, _prop, htmlAttributes));
                    else if (_prop.ModelType.IsClass)
                        _sb.Append(HiddenClassFor(html, _propExp, _prop, htmlAttributes));
                    else
                        throw new Exception(string.Format("Cannot handle complex property, {0}, of type, {1}.", _prop.PropertyName, _prop.ModelType));
                }
            }

            return _sb;
        }

        public static MvcHtmlString HiddenArrayFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
        {
            return HiddenArrayFor(html, expression, null);
        }

        public static MvcHtmlString HiddenArrayFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
        {
            ModelMetadata _metaData = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

            if (_metaData.Model == null)
                return MvcHtmlString.Empty;

            RouteValueDictionary _dict = htmlAttributes != null ? new RouteValueDictionary(htmlAttributes) : null;

            return MvcHtmlString.Create(HiddenArrayFor(html, expression, _metaData, _dict).ToString());
        }

        private static StringBuilder HiddenArrayFor<TModel>(HtmlHelper<TModel> html, LambdaExpression expression, ModelMetadata metaData, IDictionary<string, object> htmlAttributes)
        {
            Type _eleType = metaData.ModelType.GetElementType();
            Type _type = typeof(Func<,>).MakeGenericType(typeof(TModel), _eleType);

            object[] _array = (object[])metaData.Model;

            StringBuilder _sb = new StringBuilder();

            for (int i = 0; i < _array.Length; i++)
            {
                var _body = Expression.ArrayIndex(expression.Body, Expression.Constant(i));
                LambdaExpression _arrayExp = Expression.Lambda(_type, _body, expression.Parameters);
                ModelMetadata _valueMeta = ModelMetadata.FromLambdaExpression((dynamic)_arrayExp, html.ViewData);

                if (_eleType.IsClass)
                {
                    _sb.Append(HiddenClassFor(html, _arrayExp, _valueMeta, htmlAttributes));
                }
                else
                {
                    string _id = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(_arrayExp));
                    string _name = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(_arrayExp));
                    object _value = _valueMeta.Model;

                    _sb.Append(MinHiddenFor(_id, _name, _value, htmlAttributes));
                }
            }

            return _sb;
        }

        public static MvcHtmlString MinHiddenFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
        {
            return MinHiddenFor(html, expression, null);
        }

        public static MvcHtmlString MinHiddenFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
        {
            string _id = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression));
            string _name = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression));
            object _value = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Model;
            RouteValueDictionary _dict = htmlAttributes != null ? new RouteValueDictionary(htmlAttributes) : null;

            return MinHiddenFor(_id, _name, _value, _dict);
        }

        public static MvcHtmlString MinHiddenFor(string id, string name, object value, IDictionary<string, object> htmlAttributes)
        {
            TagBuilder _input = new TagBuilder("input");
            _input.Attributes.Add("id", id);
            _input.Attributes.Add("name", name);
            _input.Attributes.Add("type", "hidden");

            if (value != null)
            {
                _input.Attributes.Add("value", value.ToString());
            }

            if (htmlAttributes != null)
            {
                foreach (KeyValuePair<string, object> _pair in htmlAttributes)
                {
                    _input.MergeAttribute(_pair.Key, _pair.Value.ToString(), true);
                }
            }

            return new MvcHtmlString(_input.ToString(TagRenderMode.SelfClosing));
        }
    }
}

এখন আপনার "উইজার্ড" এর সমস্ত পদক্ষেপের জন্য আপনি একই বেস মডেলটি ব্যবহার করতে পারেন এবং ল্যাম্বডা এক্সপ্রেশনটি ব্যবহার করে @ এইচটিএমএল.হিডেনক্লাসফোর্ড সহায়কটিতে "পদক্ষেপ 1,2,3" মডেল বৈশিষ্ট্যগুলি পাস করতে পারেন।

আপনি চাইলে প্রতিটি পদক্ষেপে একটি পিছনের বোতামও রাখতে পারেন। আপনার ফর্মটিতে কেবল একটি পিছনের বোতাম রয়েছে যা এটি ফর্মেশন অ্যাট্রিবিউটটি ব্যবহার করে নিয়ামকের একটি স্টেপএনব্যাক অ্যাকশনে পোস্ট করবে। নীচের উদাহরণে অন্তর্ভুক্ত নয় তবে আপনার জন্য একটি ধারণা।

যাইহোক এখানে এখানে একটি প্রাথমিক উদাহরণ:

আপনার মডেলটি এখানে

public class WizardModel
{
    // you can store additional properties for your "wizard" / parent model here
    // these properties can be saved between pages by storing them in the form using @Html.MinHiddenFor(m => m.WizardID)
    public int? WizardID { get; set; }

    public string WizardType { get; set; }

    [Required]
    public Step1 Step1 { get; set; }

    [Required]
    public Step2 Step2 { get; set; }

    [Required]
    public Step3 Step3 { get; set; }

    // if you want to use the same model / view / controller for EDITING existing data as well as submitting NEW data here is an example of how to handle it
    public bool IsNew
    {
        get
        {
            return WizardID.HasValue;
        }
    }
}

public class Step1
{
    [Required]
    [MaxLength(32)]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Required]
    [MaxLength(32)]
    [Display(Name = "Last Name")]
    public string LastName { get; set; }
}

public class Step2
{
    [Required]
    [MaxLength(512)]
    [Display(Name = "Biography")]
    public string Biography { get; set; }
}

public class Step3
{        
    // lets have an array of strings here to shake things up
    [Required]
    [Display(Name = "Your Favorite Foods")]
    public string[] FavoriteFoods { get; set; }
}

এখানে আপনার কন্ট্রোলার

public class WizardController : Controller
{
    [HttpGet]
    [Route("wizard/new")]
    public ActionResult New()
    {
        WizardModel _model = new WizardModel()
        {
            WizardID = null,
            WizardType = "UserInfo"
        };

        return View("Step1", _model);
    }

    [HttpGet]
    [Route("wizard/edit/{wizardID:int}")]
    public ActionResult Edit(int wizardID)
    {
        WizardModel _model = database.GetData(wizardID);

        return View("Step1", _model);
    }

    [HttpPost]
    [Route("wizard/step1")]
    public ActionResult Step1(WizardModel model)
    {
        // just check if the values in the step1 model are valid
        // shouldn't use ModelState.IsValid here because that would check step2 & step3.
        // which isn't entered yet
        if (ModelState.IsValidField("Step1"))
        {
            return View("Step2", model);
        }

        return View("Step1", model);
    }

    [HttpPost]
    [Route("wizard/step2")]
    public ActionResult Step2(WizardModel model)
    {
        if (ModelState.IsValidField("Step2"))
        {
            return View("Step3", model);
        }

        return View("Step2", model);
    }

    [HttpPost]
    [Route("wizard/step3")]
    public ActionResult Step3(WizardModel model)
    {
        // all of the data for the wizard model is complete.
        // so now we check the entire model state
        if (ModelState.IsValid)
        {
            // validation succeeded. save the data from the model.
            // the model.IsNew is just if you want users to be able to
            // edit their existing data.
            if (model.IsNew)
                database.NewData(model);
            else
                database.EditData(model);

            return RedirectToAction("Success");
        }

        return View("Step3", model);
    }
}

আপনার ভিউগুলি এখানে

ধাপ 1

@model WizardModel

@{
    ViewBag.Title = "Step 1";
}

@using (Html.BeginForm("Step1", "Wizard", FormMethod.Post))
{
    @Html.MinHiddenFor(m => m.WizardID)
    @Html.MinHiddenFor(m => m.WizardType)

    @Html.LabelFor(m => m.Step1.FirstName)
    @Html.TextBoxFor(m => m.Step1.FirstName)

    @Html.LabelFor(m => m.Step1.LastName)
    @Html.TextBoxFor(m => m.Step1.LastName)

    <button type="submit">Submit</button>
}

ধাপ ২

@model WizardModel

@{
    ViewBag.Title = "Step 2";
}

@using (Html.BeginForm("Step2", "Wizard", FormMethod.Post))
{
    @Html.MinHiddenFor(m => m.WizardID)
    @Html.MinHiddenFor(m => m.WizardType)
    @Html.HiddenClassFor(m => m.Step1)

    @Html.LabelFor(m => m.Step2.Biography)
    @Html.TextAreaFor(m => m.Step2.Biography)

    <button type="submit">Submit</button>
}

ধাপ 3

@model WizardModel

@{
    ViewBag.Title = "Step 3";
}

@using (Html.BeginForm("Step3", "Wizard", FormMethod.Post))
{
    @Html.MinHiddenFor(m => m.WizardID)
    @Html.MinHiddenFor(m => m.WizardType)
    @Html.HiddenClassFor(m => m.Step1)
    @Html.HiddenClassFor(m => m.Step2)

    @Html.LabelFor(m => m.Step3.FavoriteFoods)
    @Html.ListBoxFor(m => m.Step3.FavoriteFoods,
        new SelectListItem[]
        {
            new SelectListItem() { Value = "Pizza", Text = "Pizza" },
            new SelectListItem() { Value = "Sandwiches", Text = "Sandwiches" },
            new SelectListItem() { Value = "Burgers", Text = "Burgers" },
        });

    <button type="submit">Submit</button>
}

1
আপনি ভিউ মডেল এবং নিয়ামক সরবরাহ করে আপনার সমাধানটি আরও পরিষ্কার করতে পারেন?
টাইলার ডারডেন

2

@ ডারিনের উত্তর থেকে আরও তথ্য যুক্ত করা হচ্ছে।

আপনি যদি প্রতিটি পদক্ষেপের জন্য পৃথক ডিজাইন শৈলী করেন এবং প্রতিটি পৃথক আংশিক দৃষ্টিতে বজায় রাখতে চান বা প্রতিটি পদক্ষেপের জন্য যদি আপনার একাধিক সম্পত্তি থাকে তবে কী হবে?

ব্যবহার করার সময় Html.EditorForআমাদের আংশিক দৃষ্টিভঙ্গি ব্যবহারের সীমাবদ্ধতা রয়েছে।

Sharedনামযুক্ত ফোল্ডারের অধীনে 3 আংশিক দৃশ্য তৈরি করুন :Step1ViewModel.cshtml , Step3ViewModel.cshtml , Step3ViewModel.cshtml

বংশবৃদ্ধির জন্য আমি কেবল 1 ম পাটিয়াল ভিউ পোস্ট করছি, অন্যান্য পদক্ষেপগুলি দারিনের উত্তর হিসাবে একই।

Step1ViewModel.cs

[Serializable]
public class Step1ViewModel : IStepViewModel
{
  [Required]
  public string FirstName { get; set; }

  public string LastName { get; set; }

  public string PhoneNo { get; set; }

  public string EmailId { get; set; }

  public int Age { get; set; }

 }

Step1ViewModel.cshtml

 @model WizardPages.ViewModels.Step1ViewModel

<div class="container">
    <h2>Personal Details</h2>

    <div class="form-group">
        <label class="control-label col-sm-2" for="email">First Name:</label>
        <div class="col-sm-10">
            @Html.TextBoxFor(x => x.FirstName)
        </div>
    </div>
    <div class="form-group">
        <label class="control-label col-sm-2" for="pwd">Last Name:</label>
        <div class="col-sm-10">
            @Html.TextBoxFor(x => x.LastName)
        </div>
    </div>
    <div class="form-group">
        <label class="control-label col-sm-2" for="pwd">Phone No:</label>
        <div class="col-sm-10"> 
            @Html.TextBoxFor(x => x.PhoneNo)
        </div>
    </div>
    <div class="form-group">
        <label class="control-label col-sm-2" for="pwd">Email Id:</label>
        <div class="col-sm-10">
            @Html.TextBoxFor(x => x.EmailId)
        </div>
    </div>


</div>

Index.cshtml

@using Microsoft.Web.Mvc
@model WizardPages.ViewModels.WizardViewModel

@{
    var currentStep = Model.Steps[Model.CurrentStepIndex];

    string viewName = currentStep.ToString().Substring(currentStep.ToString().LastIndexOf('.') + 1);
}

<h3>Step @(Model.CurrentStepIndex + 1) out of @Model.Steps.Count</h3>

@using (Html.BeginForm())
{
    @Html.Serialize("wizard", Model)

    @Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())

    @Html.Partial(""+ viewName + "", currentStep);

    if (Model.CurrentStepIndex > 0)
    {

     <input type="submit" value="Previous" name="prev" class="btn btn-warning" />

    }

    if (Model.CurrentStepIndex < Model.Steps.Count - 1)
    {

      <input type="submit" value="Next" name="next" class="btn btn-info" />

    }
    else
    {

      <input type="submit" value="Finish" name="finish" class="btn btn-success" />

    }
}

যদি এর থেকে আরও ভাল সমাধান হয় তবে দয়া করে অন্যকে জানানোর জন্য মন্তব্য করুন।


-9

একটি বিকল্প হ'ল অভিন্ন টেবিলের সেট তৈরি করা যা প্রতিটি পদক্ষেপে সংগৃহীত ডেটা সংরক্ষণ করবে। তারপরে শেষ ধাপে যদি সবকিছু ঠিকঠাক হয় তবে আপনি অস্থায়ী ডেটা অনুলিপি করে এটি সংরক্ষণ করতে পারবেন।

অন্যটি হ'ল Value Objectsপ্রতিটি পদক্ষেপের জন্য তৈরি করা এবং তারপরে Cacheবা সংরক্ষণ করা Session। তারপরে যদি সবকিছু ঠিকঠাক হয় তবে আপনি সেগুলি থেকে আপনার ডোমেন অবজেক্ট তৈরি করতে এবং সংরক্ষণ করতে পারেন


1
ভোট দেওয়া লোকেরাও যদি তাদের কারণটি দেয় তবে ভাল হবে।
মার্টিন

আপনাকে ভোট দেয় নি, তবে আপনার উত্তরটি প্রশ্নের সাথে সম্পূর্ণ অপ্রাসঙ্গিক। ওপি আপনাকে উইজার্ডটি কীভাবে তৈরি করবেন সে সম্পর্কে জিজ্ঞাসা করছে, যখন আপনি পিছনে প্রতিক্রিয়াটি কীভাবে পরিচালনা করবেন সে সম্পর্কে জবাব দিন।
ডেমেন্টিক

1
আমি সাধারণত ভোট দিই না, তবে আমি যখন করি তখন আমি নিশ্চিত হয়েছি যে এটির উত্সাহ :-)
সুহেল মমতাজ আওয়ান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.