قم بإنشاء الاسم وتجميعه لفهرسة الترجمة/التعيين من أجل إعادة الاستخدام بشكل أسرع
-
11-12-2019 - |
سؤال
لنفترض أنني حصلت على بيانات من خدمة (لا أستطيع التحكم فيها) على النحو التالي:
public class Data
{
// an array of column names
public string[] ColumnNames { get; set; }
// an array of rows that contain arrays of strings as column values
public string[][] Rows { get; get; }
}
وفي الطبقة الوسطى أود تعيين/ترجمة هذا إلى ملف IEnumerable<Entity>
حيث أسماء الأعمدة في Data
ربما ممثلة كخصائص في بلدي Entity
فصل.انا قلت يمكن لأنني قد لا أحتاج إلى جميع البيانات التي تعيدها الخدمة ولكن بعضها فقط.
تحويل
هذا تجريد للخوارزمية التي ستقوم بالترجمة:
- يخترع
IDictionary<string, int>
لColumnNames
حتى أتمكن بسهولة من تعيين أسماء الأعمدة الفردية لصفيف المؤشرات في صفوف فردية. - استخدام التفكير لفحص بلدي
Entity
أسماء الخصائص حتى أتمكن من مطابقتها مع أسماء الأعمدة - التكرار من خلال
Data.Rows
وإنشاء بلديEntity
الكائنات وملء الخصائص وفقًا للتعيين الذي تم إجراؤه في رقم 1.من المحتمل استخدام الانعكاس وSetValue
على خصائص لتعيينها.
تحسين
ستعمل الخوارزمية العليا بالطبع، لكنني أعتقد أنه نظرًا لأنها تستخدم الانعكاس، فيجب أن تقوم ببعض التخزين المؤقت وربما بعض عمليات التجميع السريعة، مما قد يؤدي إلى تسريع الأمور إلى حد كبير.
عند الانتهاء من الخطوتين 1 و2، يمكننا بالفعل إنشاء طريقة تأخذ مصفوفة من السلاسل وتنشئ مثيلاً للكيانات الخاصة بي باستخدام المؤشرات مباشرةً وتجميعها وتخزينها مؤقتًا لإعادة استخدامها في المستقبل.
عادةً ما أحصل على صفحة من النتائج، لذا فإن الطلبات اللاحقة ستعيد استخدام نفس الطريقة المجمعة.
حقيقة إضافية
هذا ليس ضروريًا للسؤال (والإجابات) ولكني قمت أيضًا بإنشاء سمتين تساعدان في تعيين العمود إلى الخاصية عندما لا تتطابق هذه السمات في الأسماء.لقد خلقت الأكثر وضوحا MapNameAttribute
(يستغرق ذلك سلسلة ويمكّن أيضًا حساسية حالة الأحرف بشكل اختياري) و IgnoreMappingAttribute
للعقارات على بلدي Entity
لا ينبغي أن يتم تعيينها لأية بيانات.ولكن تتم قراءة هذه السمات في الخطوة 2 من الخوارزمية العليا بحيث يتم جمع أسماء الخصائص وإعادة تسميتها وفقًا لبيانات التعريف التعريفية هذه بحيث تتطابق مع أسماء الأعمدة.
سؤال
ما هي أفضل وأسهل طريقة لإنشاء وتجميع مثل هذه الطريقة؟تعبيرات لامدا؟ CSharpCodeProvider
فصل؟
هل ربما يكون لديك مثال على التعليمات البرمجية التي تم إنشاؤها وتجميعها والتي تقوم بشيء مماثل؟أعتقد أن التعيينات هي سيناريو شائع إلى حد ما.
ملحوظة:في غضون ذلك، سأقوم بفحص PetaPoco (وربما أيضًا Massive) لأن كلا منهما يقوم بالتجميع والتخزين المؤقت بسرعة لأغراض رسم الخرائط تمامًا.
المحلول
اقتراح: الحصول على FastMember من NuGet
ثم استخدم فقط:
var accessor = TypeAccessor.Create(typeof(Entity));
ثم فقط في حلقتك، عندما تجد memberName
و newValue
للتكرار الحالي:
accessor[obj, memberName] = newValue;
تم تصميم هذا للقيام بما تطلبه؛داخليا، فإنه يحتفظ بمجموعة من الأنواع إذا كان قد شاهدها من قبل.عندما يتم رؤية نوع جديد، فإنه يقوم بإنشاء فئة فرعية جديدة من TypeAccessor
على الطاير (عبر TypeBuilder
) ويخزنها مؤقتًا.كل فريدة من نوعها TypeAccessor
على علم بخصائص هذا النوع، و أساسًا يتصرف فقط مثل:
switch(memberName) {
case "Foo": obj.Foo = (int)newValue;
case "Bar": obj.Bar = (string)newValue;
// etc
}
نظرًا لأنه تم تخزينه مؤقتًا، فإنك تدفع فقط أي تكلفة (وليس في الواقع تكلفة كبير التكلفة) في المرة الأولى التي يرى فيها النوع الخاص بك؛وبقية الوقت، فهو مجاني.لأنه يستخدم ILGenerator
مباشرة, ، كما أنه يتجنب أي تجريد غير ضروري، على سبيل المثال عبر Expression
أو CodeDom، لذا فهو بأسرع ما يمكن.
(يجب أن أوضح ذلك أيضًا dynamic
أنواع، أي.الأنواع التي تنفذ IDynamicMetaObjectProvider
, ، يمكنه استخدام مثيل واحد لدعم كل كائن).
إضافي:
ما لك استطاع افعل هو:خذ الموجود FastMember
الكود وتحريره للمعالجة MapNameAttribute
و IgnoreMappingAttribute
خلال WriteGetter
و WriteSetter
;ثم يحدث كل الفودو على الخاص بك بيانات الأسماء، وليس عضو أسماء.
وهذا يعني تغيير الخطوط:
il.Emit(OpCodes.Ldstr, prop.Name);
و
il.Emit(OpCodes.Ldstr, field.Name);
معا WriteGetter
و WriteSetter
, ، والقيام أ continue
في بداية foreach
الحلقات إذا كان ينبغي تجاهلها.