0

EF4 CTP5を使用していますが、レコードをデータベースに保存するのに問題があります。ContactエンティティとContactTypeエンティティがあります。投稿のタイトルにあるように、テーブル間に多対多のナビゲーションプロパティを設定しました。

問題は、ContactType値の検証にあります。ModelState.IsValidは、フォームから返された値(ContactType IDの文字列配列をContactTypeオブジェクトに変換できないため)はfalseです。

POCOの

public partial class Contact
{
    public Contact()
    {            
        this.ContactTypes = new HashSet<ContactType>();
    }

    // Primitive properties
    public int ContactId { get; set; }
    public string ContactName { get; set; }

    // Navigation properties
    public virtual ICollection<ContactType> ContactTypes { get; set; }
}

public partial class ContactType
{
    public ContactType()
    {
        this.Contacts = new HashSet<Contact>();
    }

    // Primitive properties
    public int ContactTypeId { get; set; }
    public string Name { get; set; }

    // Navigation properties
    public virtual ICollection<Contact> Contacts { get; set; }
}

コントローラ

//
// GET: /Contact/Edit/5
public virtual ActionResult Edit(int id)
{
    Contact contact = context.Contacts.Include(c => c.ContactTypes).Single(x => x.ContactId == id);
    ViewData["ContactTypesAll"] = GetTypesList();
    return View(contact);
}

//
// POST: /Contact/Edit/5
[HttpPost]
public virtual ActionResult Edit(Contact contact)
{
    if (ModelState.IsValid)
    {
        context.Entry(contact).State = EntityState.Modified;
        context.SaveChanges();
        return RedirectToAction("Index");
    }   
    ViewData["ContactTypesAll"] = GetTypesList();
    return View(contact);
}

意見

<div class="field-block">
    @Html.LabelFor(model => model.ContactId)
    @Html.EditorFor(model => model.ContactId, new { fieldName = "ContactId" })
    @Html.ValidationMessageFor(model => model.ContactId)
</div>
<div class="field-block">
    @Html.LabelFor(model => model.OrganizationNameInternal)
    @Html.EditorFor(model => model.OrganizationNameInternal)
    @Html.ValidationMessageFor(model => model.OrganizationNameInternal)
</div>
<div class="field-block">
    @Html.LabelFor(model => model.ContactTypes)
    @Html.ListBoxFor(modelContactType, 
            new MultiSelectList((IEnumerable<TDAMISObjects.ContactType>)ViewData["ContactTypesAll"],
                "ContactTypeId",
                "Name",
                Model.ContactTypes))
    @Html.ValidationMessageFor(model => model.ContactTypes)
</div>

ModelStateエラー

ModelState.Values.ElementAt(2).Value
{System.Web.Mvc.ValueProviderResult}
    AttemptedValue: "5"
    Culture: {en-US}
    RawValue: {string[1]}

ModelState.Values.ElementAt(2).Errors[0]
{System.Web.Mvc.ModelError}
    ErrorMessage: ""
    Exception: {"The parameter conversion from type 'System.String' to type 'ProjectObjects.ContactType' failed because no type converter can convert between these types."}

したがって、問題が何であるかはかなり明らかなようですが、解決策を見つけることができないようです。ContactType IDを手動でContactTypeオブジェクトに変換し、それらをEdit関数(「contact」と呼ばれる)に渡されるContactオブジェクトに追加しようとしました。

contact.ContactTypes.Clear();
string[] ids = this.HttpContext.Request.Form["ContactTypes"].Split(',');
for(int i = 0; i< ids.Length; i++)
{
    int x = Convert.ToInt32(ids[i]);
    ContactType selectedType = context.ContactTypes.Single(t => t.ContactTypeId == x);
    contact.ContactTypes.Add(selectedType);
}

しかし、エラーは解決しません。私も電話してみました

context.ChangeTracker.DetectChanges();

しかし、それはうまくいきませんでした。また、検証しない値のValueProviderResultを手動で設定し、

ModelState.SetModelValue("ContactTypes", val);

これも機能しませんでした。ここで基本的なものが欠けているような気がします。何か案は?

ありがとう、スティーブ

4

1 に答える 1

0

これについてさらに作業した後、回避策を見つけました。基本的に、検証エラーを無視し、既存のContactTypeを手動で削除してから、ユーザーが選択したものを追加する必要がありました。Contact.ContactTypesプロパティのカスタムバリデーターを作成しようとしましたが、ContactTypeオブジェクトは常にそのメソッドに渡されました。文字列の配列を見たことがありません。確かに、それは私が構築した最初のカスタムバリデーターだったので、おそらく何かが欠けていました。

とにかく、これが私が最終的に得た編集メソッドです:

//
// POST: /Contact/Edit/5
[HttpPost]
public virtual ActionResult Edit(Contact contact)
{
    // clear up ModelState.IsValid for contact type
    ModelState.Remove("ContactTypes");

    if(ModelState.IsValid)
    {
        // remove all contact types for contact
        Contact dummy = context.Contacts.Single(c => c.ContactId == contact.ContactId);
        if(dummy.ContactTypes.Count > 0)
        {
            dummy.ContactTypes.Clear();
            context.Entry(dummy).State = EntityState.Modified;
            context.SaveChanges();
        }
        context.Detach(dummy);
        context.Contacts.Attach(contact);

        // add currently selected contact types, then save
        string[] ids = this.HttpContext.Request.Form["ContactTypes"].Split(',');
        for(int i = 0; i< ids.Length; i++)
        {
            int x = Convert.ToInt32(ids[i]);
            ContactType selectedType = context.ContactTypes.Single(t => t.ContactTypeId == x);
            contact.ContactTypes.Add(selectedType);                    
        }
        context.Entry(contact).State = EntityState.Modified;
        context.SaveChanges();

        ViewBag.Message = "Save was successful.";
    }

    ViewData["ContactTypes"] = contact.ContactTypes.Select(t => t.ContactTypeId);
    ViewData["ContactTypesAll"] = GetTypesList();            
    return View(contact);
}

DBContextクラスにもDetachメソッドを追加する必要がありました(CTP 5ではこれは公開されていません)。

public void Detach(object entity) 
{
    ((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext.Detach(entity);
}
于 2011-02-16T15:04:58.100 に答える