إطار الكيان 4 - أين تضع منطق "ApplicCurrentValues"؟
-
26-09-2019 - |
سؤال
أنا أستخدم "تقنية كعب" إلى تحديث بلدي POCO (المستخدمة في سياق منفصل ، ASP.NET MVC).
هذا هو الرمز الذي لدي حاليًا في وحدة التحكم الخاصة بي (الذي يعمل):
[HttpPost]
public ActionResult Edit(Review review)
{
Review originalReview = _userContentService.FindById(review.PostId) as Review;
var ctx = _unitOfWork as MySqlServerObjectContext;
ctx.ApplyCurrentValues("MyEntities.Posts", review);
_unitOfWork.Commit();
// ..snip - MVC stuff..
}
كما ترون ، هناك رائحة رمز في كل مكان. قون
بضع نقاط:
- أستخدم حقن التبعية (المستند إلى الواجهة) لكل شيء بشكل أساسي
- يمكنني استخدام وحدة العمل إلى مجردة ObjectContext وتوفير الثبات عبر مستودعات متعددة
- حاليا بلدي Iunitofwork الواجهة لديها طريقة واحدة فقط:
void Commit();
- وحدة التحكم
IUserContentService
وIUnitOfWork
حقن بواسطة DI IUserContentService
المكالماتFind
في المستودعات ، التي تستخدمObjectContext
.
هذان شيئان لا يعجبهما الرمز أعلاه:
- لا أريد أن ألقي Iunitofwork كما
MySqlServerObjectContext
. - لا أريد أن يهتم المراقب
ApplyCurrentValues
أريد أساسًا أن يبدو الكود الخاص بي هكذا:
[HttpPost]
public ActionResult Edit(Review review)
{
_userContentService.Update(review);
_unitOfWork.Commit();
// ..snip - MVC stuff..
}
أي أفكار كيف يمكنني فعل ذلك؟ (أو شيئا من هذا القبيل).
لديّ بالفعل ذكاء لتوصيل اسم مجموعة الكيان بناءً على النوع (مزيج من الأدوية الجيرية ، التعددية) ، لذلك لا تقلق كثيرًا بشأن ذلك.
ولكن أتساءل أين أفضل مكان لوضعه ApplyCurrentValues
هو؟ لا يبدو من المناسب وضعه في IUnitOfWork
واجهة ، لأن هذا هو مصدر قلق الثبات (EF). لنفس السبب أنها لا تنتمي إلى الخدمة. إذا وضعته في بلدي MySqlServerObjectContext
الفصل (منطقي) ، من أين أسمي هذا من هذا ، حيث لا يوجد شيء مباشرة يمكن للوصول إلى هذه الفئة - يتم حقنه عبر DI عندما يطلب شيء ما IUnitOfWork
.
أي أفكار؟
تعديل
لديّ حل أدناه باستخدام تقنية Stub ، ولكن المشكلة هي إذا كنت قد استرجعت الكيان الذي أقوم بتحديثه مسبقًا ، فهو يلقي استثناءً ، وذكر وجود كيان بهذا المفتاح بالفعل.
ما هو أمر منطقي ، على الرغم من أنني لست متأكدًا من كيفية حل هذا؟
هل أحتاج إلى "التحقق مما إذا كان الكيان مرفقًا بالفعل ، إن لم يكن ، إرفاقه؟"
هل يمكن لأي خبراء EF4 المساعدة هناك؟
تعديل
فما باللك - وجدت الحل ، انظر الإجابة أدناه.
المحلول
اكتشفها - لم يكن سهلاً ، لذلك سأحاول شرح أفضل ما أستطيع. (لأولئك الذين يهتمون)
وحدة التحكم ذات الصلة الرمز:
// _userContentService is IUserContentService
_userContentService.Update(review);
لذلك ، تسمى وحدة التحكم الخاصة بي طريقة تسمى Update
على IUserContentService
, ، يمر عبر القوي Review
هدف.
خدمة محتوى المستخدم الرمز ذات الصلة
public void Update(Post post)
{
// _userContentRepository is IPostRepository
_userContentRepository.UpdateModel(post);
}
لذلك ، تدعو خدمتي طريقة تسمى UpdateModel
على IPostRepository
, ، يمر عبر القوي Review
هدف.
الآن هنا هو الجزء صعبة.
لدي بالفعل لا مستودعات محددة. انا املك مستودع عام اتصل GenericRepository<T> : IRepository<T>
, الذي يتعامل جميع المستودعات المختلفة.
لذلك عندما يطلب شيء ما IPostRepository
(وهو ما كانت خدمتي يفعله) ، سوف يعطيها دي GenericRepository<Post>
.
لكن الآن ، أعطيها PostRepository
:
public class PostRepository : GenericRepository<Post>, IPostRepository
{
public void UpdateModel(Post post)
{
var originalPost = CurrentEntitySet.SingleOrDefault(p => p.PostId == post.PostId);
Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}
}
ولأن الفصل مشتق من genericrepository, ، يرث جميع منطق المستودع الأساسي (البحث ، إضافة ، إلخ).
في البداية ، حاولت وضع ذلك updateModel رمز في genericrepository الفصل نفسه (وبعد ذلك لم أكن بحاجة إلى هذا المستودع المحدد) ، لكن المشكلة هي المنطق لاسترداد الكيان الحالي يعتمد على مفتاح كيان معين ، وهو ما GenericRepository<T>
لن يعرف عن.
لكن النتيجة النهائية هي التطريز يتم إخفاءه في أعماق طبقة البيانات ، وينتهي بي الأمر بوحدة تحكم نظيفة حقًا.
تعديل
تعمل هذه "تقنية الكعب" هذه أيضًا:
public void UpdateModel(Post post)
{
var stub = new Review {PostId = post.PostId};
CurrentEntitySet.Attach(stub);
Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}
لكن المشكلة هي بسبب بريد مجردة ، لا يمكنني إنشاء مثيل لها ، وبالتالي سأضطر إلى التحقق من نوع المنشور وإنشاء كوب من أجل كل مفرد نوع مشتق. ليس حقا خيار.
تحرير 2 (آخر مرة)
حسنًا ، حصلت على "تقنية الكعب" التي تعمل مع فصول مجردة ، لذلك تم حل مشكلة التزامن الآن.
أضفت معلمة نوع عام إلى بلدي updateModel الطريقة والخاصة القيد الجديد ().
تطبيق:
public void UpdateModel<T>(T post) where T : Post, new()
{
var stub = new T { PostId = post.PostId };
CurrentEntitySet.Attach(stub);
Context.ApplyCurrentValues(GetEntityName<Post>, post);
}
واجهه المستخدم:
void UpdateModel<T>(T post) where T : Post, new();
هذا يمنعني من الاضطرار إلى معرفة نوع T يدويًا ، ويمنع مشاكل التزامن ويمنع أيضًا رحلة إضافية إلى DB.
رائع جدا.
تحرير 3 (اعتقدت أن آخر مرة كانت آخر مرة)
تعمل "تقنية Stub" أعلاه ، ولكن إذا قمت باسترداد الكائن مسبقًا ، فإنه يلقي استثناءًا يوضح كيانًا بهذا المفتاح بالفعل في OSM.
هل يمكن لأي شخص أن ينصح كيفية التعامل مع هذا؟
تحرير 4 (حسنًا - هذا هو!)
لقد وجدت الحل ، بفضل هذا حتى الإجابة: هل من الممكن التحقق مما إذا كان الكائن مرتبطًا بالفعل بسياق بيانات في إطار الكيان؟
كنت قد حاولت "التحقق مما إذا كان الكيان مرفقًا" باستخدام الكود التالي:
ObjectStateEntry entry;
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(entity, out entry);
لكنها عادت دائما لا شيء, ، حتى عندما استكشفت OSM ، يمكنني رؤية كياني هناك بنفس المفتاح.
لكن هذا الرمز يعمل:
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), entity), out entry)
ربما لأنني أستخدم Pure PoCo ، واجه OSM مشكلة في معرفة مفتاح الكيان ، من يدري.
أوه وشيء آخر أضفته - حتى لا أضطر إلى إضافة مستودع محدد لكل كيان ، قمت بإنشاء سمة تسمى "entitykey"(سمة الممتلكات العامة).
يجب أن يكون لدى جميع POCO خاصية عامة واحدة مزينة بهذه السمة ، أو أرمي استثناء في وحدة المستودع الخاص بي.
لذلك يبحث مستودعك العام عن هذه الخاصية من أجل إنشاء/إعداد كعب.
نعم - يستخدم الانعكاس ، لكنه انعكاس ذكي (يعتمد على السمات) وأنا أستخدم بالفعل انعكاسًا لإضفاء الطابع على أسماء كيان من T.
على أي حال ، تم حل المشكلة - كل شيء على ما يرام الآن!