1

問題:

ユーザーが新しいアカウントを作成します。必須フィールドの 1 つは ですBusinessGroupBusinessGroupナビゲーション参照プロパティです。ユーザーBusinessGroupがドロップダウン ボックスから を選択すると、コードがBusinessGroupデータベースで を検索して取得し、アカウントに割り当てます。かなり単純に聞こえますよね?

何らかの理由で、新しいアカウントを保存するたびBusinessGroupに、データベースから取得してアカウントに直接割り当てたときに既に存在していても、BusinessGroups のデータベース テーブルに別のアカウントが挿入されます。EFコンテキストはまだ新しいものだと考えています。

ところで、TDD アプローチに従っているため、Code-First を使用します。

ここに私のPOCOがあります:

    [Table("Account")]
    public class Account
    {
        [HiddenInput]
        public int Id { get; set; }

        [Required(ErrorMessage = "The Name is required")]
        public string Name { get; set; }

        public Guid? BusinessGroupId { get; set; }
        [Required(ErrorMessage = "The Business Group is required")]            
        public BusinessGroup BusinessGroup { get; set; }
    }

データベースで生成される外部キーの命名規則をオーバーライドしたいので、上記の外部キー BusinessGroupId も指定しました。

BusinessGroup POCO は次のとおりです。

        [Table("BsuinessGroup")]
        public class BusinessGroup
        {
            [HiddenInput]
            [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid Id { get; set; }

            [Required]                
            public string Name { get; set; }
        }

ユーザーがドロップダウン ボックスから選択したものに基づいてデータベースから BusinessGroup を取得するコードを次に示します。ID は GUID です (このコードは MVC カスタム モデル バインダー内に存在するため、ModelBinderContext を公開することに注意してください。

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            //see if there is an existing model to update and create one if not
            var account = (Account) bindingContext.Model ?? new Account();
            //find out if the value provider has a required prefix
            bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
            var prefix = hasPrefix ? String.Format("{0}.", bindingContext.ModelName) : String.Empty;
            _context = bindingContext;
            _prefix = prefix;

            //map the fields of the model object
            account.Id = Convert.ToInt32((GetValue("Id")));
            account.Name = (GetValue("Name"));
            account.Phone = (GetValue("Phone"));
            account.Fax = (GetValue("Fax"));
            account.Email = (GetValue("Email"));
            account.Website = (GetValue("Website"));

            account.Audit = new Audit{Created = DateTime.Now, CreatedBy = "renso"};

            //I changed this to assign the ID rather than assign the BusinessGroup itself since that did not work 
            //and causes a new BusinessGroup to be inserted into the DB on every save of an account,
                           // this solution seems to work but I am not sure if this is the correct behavior.
            **var tempBusinessGroup = ((Controllers.AccountController)controllerContext.Controller).
                BusinessGroupRepository.Find(GetGuidValue("BusinessGroup"));
            account.BusinessGroupId = tempBusinessGroup.Id;**

            return account;
        }
        private Guid GetGuidValue(string key)
        {
            var vpr = _context.ValueProvider.GetValue(_prefix + key);
            return vpr == null ? new Guid() : new Guid(vpr.AttemptedValue);
        }
        private string GetValue(string key)
        {
            var vpr = _context.ValueProvider.GetValue(_prefix + key);
            return vpr == null ? null : vpr.AttemptedValue;
        }
.............
    }

上記の太字のコードでは、値 (GUID) を BusinessGroupId に割り当てるのではなく、BusinessGroup正しく動作しているようです。それはEFの意図した動作ですか、バグですか?

BusinessGroup以下のコードに変更すると、新しいアカウントを保存するときに新しいアカウントが作成されるという問題が発生します。

   account.BusinessGroup = ((Controllers.AccountController)controllerContext.Controller).
        BusinessGroupRepository.Find(GetGuidValue("BusinessGroup"));

ここに私のアクションがあります:

[HttpPost]
public ActionResult Create(Account account)
{
    if (ModelState.IsValid)
    {
        AccountRepository.InsertOrUpdate(account);
        AccountRepository.Save();
        return RedirectToAction("List");
    }
    var viewData = new AccountControllerViewData { Account = account, BusinessGroups = BusinessGroupRepository.All };
    return View(viewData);
}

保存も失敗します。何らかの理由で、AccountRepository の保存で ValidateOnSaveEnabled=false をオフにする必要があります。それが機能する理由がまったくわかりません。BusinessGroupアカウントの BusinessGroupId プロパティを設定すると、欠落しているというエラーが表示されます。

public void InsertOrUpdate(Account account)
{
    if (account.Id == default(Int32))
    {
        // New entity
        _context.Account.Add(account);
    }
    else
    {
        // Existing entity
        _context.Entry(account).State = EntityState.Modified;
    }
}

public void Save()
{
    //need the validation switched off or it fails with an error
    _context.Configuration.ValidateOnSaveEnabled = false;
    _context.SaveChanges();
}

ここで何かが欠けているように感じます。EF 2ed、Code-First、または DbContext の本で答えを見つけることができませんでした。POCO で独自の外部キーを定義すると、私の例で Account から BusinessGroup にナビゲーション参照を割り当てたいときはいつでも、BusinessGroup に直接割り当てることはできず、値/キーを割り当てる必要があるようです外部キー。この例では BusinessGroupId です。私は何か間違ったことをしていますか?

4

2 に答える 2

0

実際、同じ DbContext を使用してアカウントとビジネス グループを取得するようにコードをリファクタリングしました。これは期待どおりに機能します。ありがとうございます。問題は、私のリポジトリ パターンでは、新しいコンテキストを通知する抽象クラスから継承することですが、それでも各リポジトリに独自のコンテキストがあるという問題を解決できないということです。皆さんはその問題をどのように解決しましたか? ? たとえば、新しいアカウントを作成すると、ビジネス グループ、国、州 (ペンシルベニア州、ニュージャージー州など) などのナビゲーション参照があり、データベース内の国のリストを維持するためなど、それぞれに独自のリポジトリがあります。アカウントには国のナビゲーション リファレンスがあります。http リクエストごとに 1 つずつ、同じコンテキストを共有するにはどのようなパターンに従うべきでしょうか?

于 2012-07-23T21:02:24.560 に答える
0

ほとんどの場合、あなたBusinessGroupRepositoryとあなたAccountRepositoryは独自のコンテキスト インスタンスを持っています。BusinessGroupからロードすると、新しいものを保存しBusinessGroupRepositoryたコンテキストとは別のコンテキストで発生します。2 番目のコンテキストは不明なエンティティであり、EF はそれを新しいエンティティとして保存します。AccountRepositoryaccountBusinessGroup

コードをリファクタリングして、両方のリポジトリが同じコンテキスト インスタンスを使用するようにします。コンテキストはリポジトリ内で作成するのではなく、外部で作成して代わりに両方に注入する必要があります。または、2 つのリポジトリを 1 つの単一リポジトリにマージします。

于 2012-07-19T19:11:47.530 に答える