4

SurveyController クラスに次のメソッドがあります。

public ActionResult AddProperties(int id, int[] propertyids, int page = 1)
{
    var survey = _uow.SurveyRepository.Find(id);
    if (propertyids == null)
        return GetPropertiesTable(survey, page);

    var repo = _uow.PropertySurveyRepository;

    propertyids.Select(propertyid => new PropertySurvey
                {
                    //Setting the Property rather than the PropertyID 
                    //prevents the error occurring later
                    //Property = _uow.PropertyRepository.Find(propertyid),
                    PropertyID = propertyid,
                    SurveyID = id
                })
                .ForEach(x => repo.InsertOrUpdate(x));
    _uow.Save();

    return GetPropertiesTable(survey, page);
}

GetPropertiesTable はプロパティを再表示しますが、PropertySurvey.Property は仮想とマークされており、新しい演算子を使用してエンティティを作成したため、遅延読み込みをサポートするプロキシは作成されず、アクセスすると null になります。DbContext に直接アクセスできる場合は、Create メソッドを使用してプロキシを明示的に作成できます。しかし、ここには作業単位とリポジトリ パターンがあります。context.Create メソッドを repository.Create メソッドを介して公開できると思いますが、エンティティを追加するときに new 演算子の代わりにそれを使用することを覚えておく必要があります。しかし、問題を InsertOrUpdate メソッドにカプセル化する方がよいのではないでしょうか? 追加されているエンティティがプロキシではないことを検出し、プロキシを代用する方法はありますか? これは、ベース リポジトリ クラスの InsertOrUpdate メソッドです。

    protected virtual void InsertOrUpdate(T e, int id)
    {
        if (id == default(int))
        {
            // New entity
            context.Set<T>().Add(e);
        }
        else
        {
            // Existing entity
            context.Entry(e).State = EntityState.Modified;
        }
    }
4

2 に答える 2

4

qujck によって提供された回答に基づいています。オートマッパーを使用せずにそれを行う方法は次のとおりです。

コメントで提案されているように、挿入時だけでなく、常にプロキシをチェックするように編集されました

プロキシがメソッドに渡されたかどうかを確認する別の方法を使用するように再度編集しました。手法を変更した理由は、別のエンティティから継承したエンティティを導入したときに問題が発生したためです。その場合、継承されたエンティティentity.e.GetType().Equals(instance.GetType()は、プロキシであってもチェックに失敗する可能性があります。この答えから新しいテクニックを得ました

public virtual T InsertOrUpdate(T e)
{
    DbSet<T> dbSet = Context.Set<T>();

    DbEntityEntry<T> entry;
    if (e.GetType().BaseType != null 
        && e.GetType().Namespace == "System.Data.Entity.DynamicProxies")
    {
        //The entity being added is already a proxy type that supports lazy 
        //loading - just get the context entry
        entry = Context.Entry(e);
    }
    else
    {
        //The entity being added has been created using the "new" operator. 
        //Generate a proxy type to support lazy loading  and attach it
        T instance = dbSet.Create();
        instance.ID = e.ID;
        entry = Context.Entry(instance);
        dbSet.Attach(instance);

        //and set it's values to those of the entity
        entry.CurrentValues.SetValues(e);
        e = instance;
    }

    entry.State = e.ID == default(int) ?
                            EntityState.Added :
                            EntityState.Modified;

    return e;
}

public abstract class ModelBase
{
    public int ID { get; set; }
}
于 2013-05-29T10:53:48.033 に答える
1

これは 1 か所で処理する必要があり、すべてのルックをキャッチするのに最適な場所はリポジトリであることに同意します。Tの型をコンテキストによって作成されたインスタンスと比較し、型が一致しない場合はAutomapperなどを使用してすべての値をすばやく転送できます。

private bool mapCreated = false;

protected virtual void InsertOrUpdate(T e, int id)
{
    T instance = context.Set<T>().Create();
    if (e.GetType().Equals(instance.GetType()))
        instance = e;
    else
    {
        //this bit should really be managed somewhere else
        if (!mapCreated)
        {
            Mapper.CreateMap(e.GetType(), instance.GetType());
            mapCreated = true;
        }
        instance = Mapper.Map(e, instance);
    }

    if (id == default(int))
        context.Set<T>().Add(instance);
    else
        context.Entry(instance).State = EntityState.Modified;
}
于 2013-05-24T14:04:21.783 に答える