0

私は、将来的に作成する予見可能なすべての調査を処理するために、単一の Controller クラスを作成しようとしています。現在、フィールドを持つ「Surveys」テーブルがあります:IdSurveyNameActive。「マスター」Surveys のインデックス ページで、そのテーブルで見つかったすべての SurveyName を一覧表示します。各 SurveyName はクリック可能で、クリックすると、ページは SurveyName を文字列として受信コントローラー アクションに送信します。上記のコントローラー アクションは次のようになります。

    //
    //GET: /Surveys/TakeSurvey/
    public ActionResult TakeSurvey(string surveyName)
    {
        Assembly thisAssembly = Assembly.GetExecutingAssembly();
        Type typeToCreate = thisAssembly.GetTypes().Where(t => t.Name == surveyName).First();

        object newSurvey = Activator.CreateInstance(typeToCreate);

        ViewBag.surveyName = surveyName;

        return View(surveyName, newSurvey);
    }

リフレクションを使用して、渡された文字列 'surveyName' で指定されたタイプ (モデル) の新しいインスタンスを作成し、そのモデルを同じ名前のビューに渡すことができます。


誰かが「SummerPicnic」をクリックすると、文字列「SummerPicnic」がコントローラに渡されます。コントローラーは、リフレクションを使用して、SummerPicnic クラスの新しいインスタンスを作成し、それを同じ名前のビューに渡します。次に、夏のピクニック計画のフォームに記入することができます。

これはすべてうまく機能します。私が立ち往生している部分は、POST メソッドによって返されたフォームを正しい対応する DB テーブルに保存しようとしていることです。コントローラーがどのようなモデルを取得するかを前もって知らないので、どのようなモデルを保存するかを伝える方法がわからないだけでなく、保存先もわかりません。次のようなばかげたことをしないでください。

    //
    //POST: Surveys/TakeSurvey
    [HttpPost]
    public ActionResult TakeSurvey(Model survey)
    {

        if (ModelState.IsValid)
        {
            _db. + typeof(survey) + .Add(survey);
            _db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }

        return View();
    }

これを行う方法はありますか、それともまったく別の角度からこれを行う必要がありますか? 私の最終的な目標は、すべての単純な調査を調整する単一のコントローラーを用意することです。そのため、最終的に作成するすべての調査に対して個別のコントローラーを作成する必要はありません。

私が考えることができる別の解決策は、調査ごとに個別のメソッドを用意し、どのメソッドを呼び出すかをすべての調査のビュー内で定義することです。たとえば、SummerPicnic の調査がある場合、送信ボタンは「SummerPicnic」という ActionMethod を呼び出します。

@Ajax.ActionLink("Create", "SummerPicnic", "Surveys", new AjaxOptions { HttpMethod = "POST" })

PartyAttendance の調査では、ActionMethod を 'PartyAttendance' などと呼びます。しかし、そうする必要はありません...

UPDATE 1 私が電話したとき:

    _db.Articles.Add(article);
    _db.SaveChanges();

これは _ dbとは次のとおりです。

    private IntranetDb _db = new IntranetDb();

どちらが...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace Intranet.Models
{
    public class IntranetDb : DbContext
    {
        public DbSet<Article> Articles { get; set; }
        public DbSet<ScrollingNews> ScrollingNews { get; set; }
        public DbSet<Survey> Surveys { get; set; }
        public DbSet<Surveys.test> tests { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }
}
4

3 に答える 3

2

最終的に、Mark のコードを少し変更したバージョンになりました。

    [HttpPost]
    public ActionResult TakeSurvey(string surveyName, FormCollection form)
    {
        //var surveyType = Type.GetType(surveyName);
        //var surveyObj = Activator.CreateInstance(surveyType);

        // Get survey type and create new instance of it
        var thisAssembly = Assembly.GetExecutingAssembly();
        var surveyType = thisAssembly.GetTypes().Where(t => t.Name == surveyName).First();
        var newSurvey = Activator.CreateInstance(surveyType);

        var binder = Binders.GetBinder(surveyType);

        var bindingContext = new ModelBindingContext()
        {
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => newSurvey, surveyType),
            ModelState = ModelState,
            ValueProvider = form
        };

        binder.BindModel(ControllerContext, bindingContext);

        if (ModelState.IsValid)
        {
            var objCtx = ((IObjectContextAdapter)_db).ObjectContext;
            objCtx.AddObject(surveyName, newSurvey);
            _db.SaveChanges();
        return RedirectToAction("Index", "Home");
        }

        return View();
    }

Type.GetType(surveyName);に設定されているときに、 surveyTypeが「null」になっていました。そこで先に進み、Reflection を介して Type を取得しました。

私が今遭遇している唯一の問題はここにあります:

        if (ModelState.IsValid)
        {
            var objCtx = ((IObjectContextAdapter)_db).ObjectContext;
            objCtx.AddObject(surveyName, newSurvey);
            _db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }

AddObject を試行すると、「EntitySet 名 'IntranetDb.test' が見つかりませんでした」という例外が発生します。接頭辞「IntranetDb」を取り除く方法を理解する必要があるだけです。そしてうまくいけば、私はビジネスをするでしょう。

更新
私が完全に見落としていたのは、モデルをビューからコントローラーに渡すことでした...ああ、面倒です。現在、Survey モデルの正しいインスタンスを作成するために必要な文字列をコントローラーに渡す方法がわからなかったため、通常の [送信] ボタンを ActionLink に置き換えています。

    <p>
        @Ajax.ActionLink("Create", "TakeSurvey", "Surveys", new { surveyName = ViewBag.surveyName }, new AjaxOptions { HttpMethod = "POST" })
        @*<input type="submit" value="Create" />*@
    </p>

したがって、'IntranetDb.test' を単に 'test' にする方法を理解したら、送信時に調査フィールドをすべて 'null' にしない方法に取り組みます。

更新 2
送信方法を Ajax ActionLink の使用から通常の送信ボタンに変更しました。これにより、Mark の bindingContext がバインディングを行っている (Model 値にフォーム値を注入する) ことに気付いた後、Model 値に設定される null 値が修正されました。だから今、私のビューは簡単に送信されます:

<input type="submit" value="Submit" />

「IntranetDb.test」を「test」だけに切り詰める方法を考え出すことに戻ります...

問題は、私
の IntranetDb クラスにあります。

public class IntranetDb : DbContext
{
    public DbSet<Article> Articles { get; set; }
    public DbSet<ScrollingNews> ScrollingNews { get; set; }
    public DbSet<SurveyMaster> SurveyMaster { get; set; }
    public DbSet<Surveys.test> tests { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

objCtx.AddObject(surveyName, newSurveyEntry); 「test」という IntranetDb クラスのエントリ (「EntitySet」) を探していました。問題は、「テスト」という名前の EntitySet を持っていないという事実にありますが、複数形の「s」を含む「テスト」という名前です。何も切り詰める必要はまったくないことがわかりました。正しいオブジェクトを指し示すだけでよいのです。マークとアビジット、ご協力ありがとうございます。^_^

終了した

    //
    //POST: Surveys/TakeSurvey
    [HttpPost]
    public ActionResult TakeSurvey(string surveyName, FormCollection form)
    {
        //var surveyType = Type.GetType(surveyName);
        //var surveyObj = Activator.CreateInstance(surveyType);

        // Create Survey Type using Reflection
        var thisAssembly = Assembly.GetExecutingAssembly();
        var surveyType = thisAssembly.GetTypes().Where(t => t.Name == surveyName).First();
        var newSurveyEntry = Activator.CreateInstance(surveyType);

        // Set up binder
        var binder = Binders.GetBinder(surveyType);            
        var bindingContext = new ModelBindingContext()
        {
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => newSurveyEntry, surveyType),
            ModelState = ModelState,
            ValueProvider = form        // Get values from form
        };

        var objCtx = ((IObjectContextAdapter)_db).ObjectContext;

        // Retrieve EntitySet name for Survey type
        var container = objCtx.MetadataWorkspace.GetEntityContainer(objCtx.DefaultContainerName, DataSpace.CSpace);
        string setName = (from meta in container.BaseEntitySets
                                      where meta.ElementType.Name == surveyName
                                      select meta.Name).First();

        binder.BindModel(ControllerContext, bindingContext);    // bind form values to survey object     

        if (ModelState.IsValid)
        {
            objCtx.AddObject(setName, newSurveyEntry);  // Add survey entry to appropriate EntitySet
            _db.SaveChanges();

            return RedirectToAction("Index", "Home");
        }

        return View();
    }

やや肥大化していますが、今のところ機能しています。 この投稿は、Survey オブジェクト自体から EntitySet を取得するのに役立ったので、ある種の EntitySet 命名規則の確立について心配する必要はありませんでした。

于 2012-06-18T19:44:23.113 に答える
2

このようなものを試すことができます。

アップデート:

ビルトインUpdateModelは汎用モデルで動作するため、この記事を参照してください。

[HttpPost]
public ActionResult TakeSurvey(FormCollection form, surveyName)
{
  var surveyType = Type.GetType(surveyName);
  var surveyObj = Activator.CreateInstance(surveyType);

  var binder = Binders.GetBinder(surveyType);

  var bindingContext = new ModelBindingContext()
  {
    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => surveyObj, surveyType),
    ModelState = ModelState,
    ValueProvider = form
  };

  binder.BindModel(ControllerContext, bindingContext);

  if (ModelState.IsValid)
  {
    // if "db" derives from ObjectContext then..
    db.AddObject(surveyType, surveyObj);         
    db.SaveChanges();

    // if "db" derives from DbContext then..
    var objCtx = ((IObjectContextAdapter)db).ObjectContext;        
    objCtx.AddObject(surveyType, surveyObj);         
    db.SaveChanges();

    return RedirectToAction("Index", "Home");
  }

  return View();
}

この2 つが と の違いを知っていることをDbContext確認してくださいObjectContext

于 2012-06-16T03:54:16.243 に答える
0

TakeSurvey私が見る主な問題は、モデルをPOST メソッドにバインドすることです。さまざまなタイプの調査モデルをこのメソッドで処理し、アクションを呼び出す前に MVC をこのモデルにバインドする必要がある場合は、そのような汎用モデルすべてにラッパー モデル クラスを作成し、SurveyModelカスタム モデル バインダーを使用してバインドすることができると思います。これらのモデル。

public class SurveyModel
{
    public string GetSurveyModelType();
    public SummerPicnicSurvey SummerPicnicSurvey { get; set; }
    public PartyAttendanceSurvey PartyAttendanceSurvey { get; set; }
}

次に、このモデルをバインドするカスタム mobel バインダーを作成します。リクエスト フォーム フィールドから、投稿された調査モデルのタイプを確認し、それに応じてすべてのフィールドを取得して、SurveyModel クラスを初期化できます。SummerPicnicSurvey が投稿された場​​合、クラス SurveyModel はこのクラスで設定され、PartyAttendanceSurvey は null になります。カスタム モデル バインダーの例。

コントローラー アクションのTakeSurveyPOST メソッドから、次のように db を更新できます。

 [HttpPost]
    public ActionResult TakeSurvey(SurveyModel survey)
    {

        if (ModelState.IsValid)
        {
            if(survey.GetSurveyModelType() == "SummerPicnicSurvey")
                _db.UpdateSummerPicnicSurvey(survey.SummerPicnicSurvey);
            else if (survey.GetSurveyModelType() == "PartyAttendanceSurvey")
                _db.UpdateSummerPicnicSurvey(survey.PartyAttendanceSurvey);

            _db.SaveChanges();
            return RedirectToAction("Index", "Home");
        }

        return View();
    }

他の調査をカプセル化する SurveyModel の代わりに、継承を行い、.netasを使用してチェックで型キャストし、モデルを使用できます。

とは言え、機種ごとに使い分けても問題ないと思います。これにより、コードを適切に単体テストできます。そうでなければ維持するのが健康的でない場合は、多すぎます。または、汎用モデルSurveyModelをリポジトリまたはデータ アクセス レイヤーに転送し、それをポリモーフィックな方法で処理させることもできます。私はより小さな関数を好み、コードをきれいに保ちます。

編集:継承方法:

    public class SurveyModel
    {      
        public virtual bool Save();        
    }

    public partial class SummerPicnicSurvey : SurveyModel
    {
      public bool Save(SummerPicnicSurvey survey)
      {
         using(var _dbContext = new MyContext())
         {
           _dbContex.SummerPicnicSurveys.Add(survey);
           _dbContex.SaveChanges();
         }
      }
    }

  [HttpPost]
  public ActionResult TakeSurvey(SurveyModel survey)
  {
     if (ModelState.IsValid)
      {
         survey.Save();
           return RedirectToAction("Index", "Home");
       }

      return View();
   }

追加する新しい調査モデル タイプは、適切な dbcontext メソッドを呼び出す SaveChanges または Save メソッドを実装する必要があります。コントローラー アクションは、渡された汎用の「SurveyModel」参照に対して Save を呼び出すだけです。したがって、アクションは修正のために閉じられますが、修正のために開かれます。開閉設計の原則。

于 2012-06-16T04:16:18.703 に答える