3

ある種のRepositoryクラスを実装しましたがGetByIDDeleteByIDメソッドなどがありますが、メソッドの実装に問題がありUpdateByIDます。

私はこのようなことをしました:

public virtual void UpdateByID(int id, T entity)
{
        var dbcontext = DB;
        var item = GetByID(dbcontext, id);
        item = entity; 
        dbcontext.SubmitChanges();
}

protected MusicRepo_DBDataContext DB
{
    get
    {
        return new MusicRepo_DBDataContext();
    }
}

しかし、渡されたエンティティを更新していません。

誰かがそのような方法を実装しましたか?


参考までにやり方はこちらGetByID


[アップデート]

マークが正しく示唆したように、ローカル変数の値を変更しているだけです。では、この方法についてどうすればよいと思いますか? リフレクションを使用して、プロパティを から にコピーしentityますitem

4

7 に答える 7

8

更新したのはローカル変数だけです。それが機能するには、メンバーの値をコピーする必要がありますentityitemそれほど単純ではありません。


以下のようなもの; 私が使用した唯一の理由TKeyは、文字列キーを持つ Northwind.Customer でテストしたためです ;-p

メタモデルを使用する利点は、POCO クラス (および xml ベースのマッピング) を使用している場合でも機能し、モデルに関係のないものを更新しようとしないことです。

この例では、データ コンテキストを渡しましたSubmitChanges。ある時点で a を追加する必要がありますが、残りは直接比較できるはずです。

ところで - 渡されたオブジェクトから ID を喜んで取得できれば、それも簡単です。その後、複合 ID テーブルをサポートできます。

    static void Update<TEntity>(DataContext dataContext, int id, TEntity obj)
        where TEntity : class
    {
        Update<TEntity, int>(dataContext, id, obj);
    }
    static void Update<TEntity, TKey>(DataContext dataContext, TKey id, TEntity obj)
        where TEntity : class
    {
        // get the row from the database using the meta-model
        MetaType meta = dataContext.Mapping.GetTable(typeof(TEntity)).RowType;
        if(meta.IdentityMembers.Count != 1) throw new InvalidOperationException("Composite identity not supported");
        string idName = meta.IdentityMembers[0].Member.Name;

        var param = Expression.Parameter(typeof(TEntity), "row");
        var lambda = Expression.Lambda<Func<TEntity,bool>>(
            Expression.Equal(
                Expression.PropertyOrField(param, idName),
                Expression.Constant(id, typeof(TKey))), param);

        object dbRow = dataContext.GetTable<TEntity>().Single(lambda);

        foreach (MetaDataMember member in meta.DataMembers)
        {
            // don't copy ID
            if (member.IsPrimaryKey) continue; // removed: || member.IsVersion
            // (perhaps exclude associations and timestamp/rowversion? too)

            // if you get problems, try using StorageAccessor instead -
            // this will typically skip validation, etc
            member.MemberAccessor.SetBoxedValue(
                ref dbRow, member.MemberAccessor.GetBoxedValue(obj));
        }
        // submit changes here?
    }
于 2009-05-18T22:20:03.333 に答える
1

同様の問題がいくつかあり、最終的にPLINQOを使用しました。これは、LINQ-TO-SQL 生成コードに対する多くの機能強化です。ただし、まだお持ちでない場合は、CodeSmith を購入する必要があります (30 日間は無料で評価できます)。

于 2009-06-12T02:39:15.720 に答える
0

ちょっとドレア、私もこれに苦労し、非常にエレガントな解決策を見つけました。

基本的に DataContext.Attach(EntityToUpdate,OriginalEntity) メソッドを使用する必要があります。

いくつかの落とし穴があります...この情報を読んでください。すべてが説明されています

読み終えたら、質問があれば私に戻ってきてください。その情報に基づいて非常に便利な EntitySaver クラスを作成したので、必要に応じて、落とし穴を見つけたらクラスを調べることができます。

乾杯

編集:試してみたい場合に備えて、ここに私のクラスが完全にあります。実際には、更新と挿入を自動的に処理します。ご不明な点がございましたらお知らせください。

エンティティセーバー:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QDAL.CoreContext;
using QDAL.CoreEntities;
using LinqExtension.CustomExtensions;

namespace QDAL
{
    internal class DisconnectedEntitySaver
    {
        private QDataDataContext ContextForUpdate;

        public DisconnectedEntitySaver() {
            ContextForUpdate = Base.CreateDataContext();
        }

        public List<TEntityType> SaveEntities<TEntityType, TKeyType>(List<TEntityType> EntitiesToSave) {

            string PKName;

            PKName = Base.GetPrimaryKeyName(typeof(TEntityType), ContextForUpdate);

            return SaveEntities<TEntityType, TKeyType>(EntitiesToSave, PKName);
        }

        public List<TEntityType> SaveEntities<TEntityType, TKeyType>(List<TEntityType> EntitiesToSave, string KeyFieldName)
        {
            List<TEntityType> EntitiesToPossiblyUpdate;
            List<TEntityType> EntitiesToInsert;
            List<TEntityType> HandledEntities = new List<TEntityType>();

            bool TimeStampEntity;
            Type ActualFieldType;

            if (EntitiesToSave.Count > 0) {
                TimeStampEntity = Base.EntityContainsTimeStamp(typeof(TEntityType), ContextForUpdate);

                ActualFieldType = EntitiesToSave.FirstOrDefault().GetPropertyType(KeyFieldName);

                if (ActualFieldType != typeof(TKeyType)) {
                    throw new Exception("The UniqueFieldType[" + typeof(TKeyType).Name + "] specified does not match the actual field Type[" + ActualFieldType.Name + "]");
                }

                if (ActualFieldType == typeof(string)) {
                    EntitiesToPossiblyUpdate = EntitiesToSave.Where(ent => string.IsNullOrEmpty(ent.GetPropertyValue<string>(KeyFieldName)) == false).ToList();
                    EntitiesToInsert = EntitiesToSave.Where(ent => string.IsNullOrEmpty(ent.GetPropertyValue<string>(KeyFieldName)) == true).ToList();
                } else {
                    EntitiesToPossiblyUpdate = EntitiesToSave.Where(ent => EqualityComparer<TKeyType>.Default.Equals(ent.GetPropertyValue<TKeyType>(KeyFieldName), default(TKeyType)) == false).ToList();
                    EntitiesToInsert = EntitiesToSave.Where(ent => EqualityComparer<TKeyType>.Default.Equals(ent.GetPropertyValue<TKeyType>(KeyFieldName), default(TKeyType)) == true).ToList();
                }

                if (EntitiesToPossiblyUpdate.Count > 0) {
                    EntitiesToInsert.AddRange(ResolveUpdatesReturnInserts<TEntityType, TKeyType>(EntitiesToPossiblyUpdate, KeyFieldName));

                    HandledEntities.AddRange(EntitiesToPossiblyUpdate.Where(ent => EntitiesToInsert.Select(eti => eti.GetPropertyValue<TKeyType>(KeyFieldName)).Contains(ent.GetPropertyValue<TKeyType>(KeyFieldName)) == false));
                }

                if (EntitiesToInsert.Count > 0) {
                    ContextForUpdate.GetTable(typeof(TEntityType)).InsertAllOnSubmit(EntitiesToInsert);

                    HandledEntities.AddRange(EntitiesToInsert);
                }

                ContextForUpdate.SubmitChanges();
                ContextForUpdate = null;

                return HandledEntities;
            } else {
                return EntitiesToSave;
            }
        }

        private List<TEntityType> ResolveUpdatesReturnInserts<TEntityType, TKeyType>(List<TEntityType> PossibleUpdates, string KeyFieldName)
        {
            QDataDataContext ContextForOrginalEntities;

            List<TKeyType> EntityToSavePrimaryKeys;
            List<TEntityType> EntitiesToInsert = new List<TEntityType>();
            List<TEntityType> OriginalEntities;

            TEntityType NewEntityToUpdate;
            TEntityType OriginalEntity;

            string TableName;

            ContextForOrginalEntities = Base.CreateDataContext();

            TableName = ContextForOrginalEntities.Mapping.GetTable(typeof(TEntityType)).TableName;
            EntityToSavePrimaryKeys = (from ent in PossibleUpdates select ent.GetPropertyValue<TKeyType>(KeyFieldName)).ToList();

            OriginalEntities = ContextForOrginalEntities.ExecuteQuery<TEntityType>("SELECT * FROM " + TableName + " WHERE " + KeyFieldName + " IN('" + string.Join("','", EntityToSavePrimaryKeys.Select(varobj => varobj.ToString().Trim()).ToArray()) + "')").ToList();

            //kill original entity getter
            ContextForOrginalEntities = null;

            foreach (TEntityType NewEntity in PossibleUpdates)
            {
                NewEntityToUpdate = NewEntity;
                OriginalEntity = OriginalEntities.Where(ent => EqualityComparer<TKeyType>.Default.Equals(ent.GetPropertyValue<TKeyType>(KeyFieldName),NewEntityToUpdate.GetPropertyValue<TKeyType>(KeyFieldName)) == true).FirstOrDefault();

                if (OriginalEntity == null)
                {
                    EntitiesToInsert.Add(NewEntityToUpdate);
                }
                else
                {
                    ContextForUpdate.GetTable(typeof(TEntityType)).Attach(CloneEntity<TEntityType>(NewEntityToUpdate), OriginalEntity);
                }
            }

            return EntitiesToInsert;
        }

        protected  TEntityType CloneEntity<TEntityType>(TEntityType EntityToClone)
        {
            var dcs = new System.Runtime.Serialization.DataContractSerializer(typeof(TEntityType));
            using (var ms = new System.IO.MemoryStream())
            {
                dcs.WriteObject(ms, EntityToClone);
                ms.Seek(0, System.IO.SeekOrigin.Begin);
                return (TEntityType)dcs.ReadObject(ms);
            }
        }
    }
}

これらのヘルパーも必要になります。

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QDAL.CoreContext;
using QDAL.CoreEntities;
using System.Configuration;

namespace QDAL
{
    internal class Base
    {
        public Base() {
        }

        internal static QDataDataContext CreateDataContext() {
            QDataDataContext newContext;
            string ConnStr;

            ConnStr = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

            newContext = new QDataDataContext(ConnStr);

            return newContext;
        }

        internal static string GetTableName(Type EntityType, QDataDataContext CurrentContext) {
            return CurrentContext.Mapping.GetTable(EntityType).TableName;
        }

        internal static string GetPrimaryKeyName(Type EntityType, QDataDataContext CurrentContext) {
            return (from m in CurrentContext.Mapping.MappingSource.GetModel(CurrentContext.GetType()).GetMetaType(EntityType).DataMembers where m.IsPrimaryKey == true select m.Name).FirstOrDefault();
        }

        internal static bool EntityContainsTimeStamp(Type EntityType, QDataDataContext CurrentContext) {
            return (CurrentContext.Mapping.MappingSource.GetModel(CurrentContext.GetType()).GetMetaType(EntityType).DataMembers.Where(dm => dm.IsVersion == true).FirstOrDefault() != null);
        }
    }
}

これらの拡張機能により、リフレクションが容易になります。

<System.Runtime.CompilerServices.Extension()> _
    Public Function GetPropertyValue(Of ValueType)(ByVal Source As Object, ByVal PropertyName As String) As ValueType
        Dim pInfo As System.Reflection.PropertyInfo

        pInfo = Source.GetType.GetProperty(PropertyName)

        If pInfo Is Nothing Then
            Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
        Else
            Return pInfo.GetValue(Source, Nothing)
        End If
    End Function

    <System.Runtime.CompilerServices.Extension()> _
    Public Function GetPropertyType(ByVal Source As Object, ByVal PropertyName As String) As Type
        Dim pInfo As System.Reflection.PropertyInfo

        pInfo = Source.GetType.GetProperty(PropertyName)

        If pInfo Is Nothing Then
            Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
        Else
            Return pInfo.PropertyType
        End If
    End Function
于 2009-06-06T23:31:30.337 に答える
0

さて、私は(私の頭の上から)このようなものを持っています:

public Question UpdateQuestion(Question newQuestion)
    {
        using (var context = new KodeNinjaEntitiesDataContext())
        {
            var question = (from q in context.Questions where q.QuestionId == newQuestion.QuestionId select q).SingleOrDefault();
            UpdateFields(newQuestion, question);
            context.SubmitChanges();                
            return question;
        }
    }

    private static void UpdateFields(Question newQuestion, Question oldQuestion)
    {
        if (newQuestion != null && oldQuestion != null)
        {
            oldQuestion.ReadCount = newQuestion.ReadCount;
            oldQuestion.VotesCount = newQuestion.VotesCount;
            //.....and so on and so on.....
        }
    }

単純なエンティティでは問題なく機能します。もちろん、多くのエンティティがある場合は、リフレクションを使用できます。

于 2009-05-19T14:39:28.427 に答える
0

私が正しく理解していれば、これについての反省は必要ないはずです。

特定のエンティティに対してこれを行うには、エンティティを取得して DB コンテキストにアタッチする必要があります。アタッチされると、LINQ-to-SQL は何を更新する必要があるかを判断します。次のようなもの:

// update an existing member
dbcontext.Members.Attach(member, true);

// commit to database
dbcontext.SubmitChanges();

これは、Members テーブルの Member を更新するためのものです。真の引数は、あなたがそれを変更したことを示しています。または、オリジナルが横たわっている場合は、それを 2 番目の引数として渡し、DB コンテキストに差分を実行させることができます。これは、DB コンテキスト実装 (「作業単位」パターンを実装する) の主要な部分です。

これを一般化するには、Member 型を T に置き換え、.Members を .GetTable に置き換えます。

public virtual void Update(T entity)
{
        var dbcontext = DB;
        dbcontext.GetTable<T>().Attach(entity, true);
        dbcontext.SubmitChanges();
}

ID が既にエンティティに正しく設定されている (そしてモデルで主キーとしてマークされている) と仮定すると、最初に ID を調べる必要さえありません。必要に応じて、ID で検索し、Attach メソッドに渡すこともできますが、これはおそらく不要な余分な検索を引き起こすだけです。

編集:モデル内の列で UpdateCheck を Never に設定する必要があります。そうしないと、同時実行チェックを実行しようとします。なしに設定すると、最終更新が優先されます。それ以外の場合は、テーブルに tmestamp フィールドを追加すると、同時実行チェックによってエンティティが古くなっているかどうかが判断されます。

UpdateCheck.Never を Attach(entity, bool) と組み合わせると、LINQ-to-SQL でこれを解決する最も簡単でパフォーマンスの高い方法になります。

于 2009-06-06T23:17:00.613 に答える
0

私はリポジトリ パターンにあまり詳しくありませんが、古いエンティティをデータベースから削除し、新しいエンティティを同じ ID でデータベースに配置するとどうなるでしょうか? このようなもの:

public virtual void UpdateByID(int id, T entity)
{
    DeleteByID(id);
    var dbcontext = DB;
    //insert item (would have added this myself but you don't say how)
    dbcontext.SubmitChanges();
}
于 2009-06-09T21:24:52.943 に答える