थोड़ा शैक्षिक कार्यक्रम
मैं वास्तव में ऑटोमैपर को पसंद करता हूं, विशेष रूप से इसकी क्वेरीटेक्स्टेन्शन और प्रोजेक्टटो <> विधि । संक्षेप में, यह विधि SQL क्वेरी में सीधे प्रकार के प्रक्षेपण की अनुमति देती है। यह वास्तव में डेटाबेस से dto प्राप्त करने की अनुमति देता है। यानी डेटाबेस से सभी संस्थाओं को प्राप्त करने की आवश्यकता नहीं है, उन्हें मेमोरी में लोड करें, Automapper.Map<>
का Automapper.Map<>
करें, जिसके कारण एक बड़ी खपत और मेमोरी ट्रैफ़िक हुआ।
प्रोजेक्शन प्रकार
Linq में प्रोजेक्शन प्राप्त करने के लिए, आपको कुछ इस तरह लिखना होगा:
from user in dbContext.Users where user.IsActive select new { Name = user.Name, Status = user.IsConnected ? "Connected" : "Disconnected" }
QueryableExtensions का उपयोग करते हुए, इस कोड को निम्नलिखित के साथ प्रतिस्थापित किया जा सकता है (बेशक, बशर्ते कि रूपांतरण नियम उपयोगकर्ता -> UserInfo पहले से ही वर्णित हैं)
dbContext.Users.Where(x => x.IsActive).ProjectTo<UserInfo>();
Enum और इसके साथ समस्याएं
प्रोजेक्शन में एक खामी है जिस पर विचार करने की आवश्यकता है। यह प्रदर्शन किए गए कार्यों पर प्रतिबंध है। सब कुछ एसक्यूएल क्वेरी में अनुवादित नहीं किया जा सकता है । विशेष रूप से, गणना प्रकार द्वारा जानकारी प्राप्त करना संभव नहीं है। उदाहरण के लिए, निम्नलिखित एनम है
public enum FooEnum { [Display(Name = "")] Any, [Display(Name = "")] Open, [Display(Name = "")] Closed }
एक इकाई है जिसमें FooEnum प्रकार की संपत्ति घोषित की गई है। Dto में, आपको स्वयं Enum नहीं, बल्कि DisplayAttribute विशेषता के नाम गुण का मान प्राप्त करने की आवश्यकता है। प्रक्षेपण के माध्यम से यह महसूस करने के लिए काम नहीं करता है, क्योंकि विशेषता मान प्राप्त करने के लिए परावर्तन की आवश्यकता होती है, जो एसक्यूएल केवल "कुछ भी नहीं जानता है"।
नतीजतन, आपको या तो सामान्य Map<>
का उपयोग करना होगा, सभी संस्थाओं को मेमोरी में लोड करना होगा, या एनम मूल्यों और उस पर विदेशी कुंजियों के साथ एक अतिरिक्त तालिका शुरू करना होगा।
एक समाधान है - अभिव्यक्तियाँ
लेकिन "बूढ़ी औरत पर एक कातिल होगा।" आखिरकार, एनम के सभी मूल्यों को अग्रिम में जाना जाता है। SQL में एक switch
कार्यान्वयन है जिसे आप प्रक्षेपण बनाते समय सम्मिलित कर सकते हैं। यह समझने के लिए रहता है कि यह कैसे करना है। हैशटैग: "अभिव्यक्ति पेड़-हमारा-सब।"
ऑटोमैपर, जब प्रोजेक्टिंग प्रकार, अभिव्यक्ति को अभिव्यक्ति में बदल सकता है, जो कि एंटिटी फ्रेमवर्क के बाद, संबंधित SQL क्वेरी में परिवर्तित हो जाता है।
पहली नज़र में, रनटाइम में अभिव्यक्ति के पेड़ बनाने के लिए वाक्य रचना बेहद असुविधाजनक है। लेकिन कुछ छोटी समस्याओं के हल के बाद, सब कुछ स्पष्ट हो जाता है। Enum के साथ समस्या को हल करने के लिए, आपको स्रोत डेटा के आधार पर, मूल्यों को वापस करने वाले सशर्त अभिव्यक्तियों का एक नेस्टेड ट्री बनाने की आवश्यकता है। कुछ इस तरह
IF enum=Any THEN RETURN "" ELSE IF enum=Open THEN RETURN "" ELSE enum=Closed THEN RETURN "" ELSE RETURN ""
विधि हस्ताक्षर पर निर्णय लें।
public class FooEntity { public int Id { get; set; } public FooEnum Enum { get; set; } } public class FooDto { public int Id { get; set; } public string Name { get; set; } } // Automapper CreateMap<FooEntity, FooDto>() .ForMember(x => x.Enum, options => options.MapFrom(GetExpression())); private Expression<Func<FooEntity, string>> GetExpression() { }
GetExpression()
विधि को एक अभिव्यक्ति उत्पन्न करनी चाहिए जो FooEntity का एक उदाहरण प्राप्त करती है और Enum
संपत्ति के लिए एक स्ट्रिंग प्रतिनिधित्व लौटाती है।
सबसे पहले, इनपुट पैरामीटर को परिभाषित करें और संपत्ति का मूल्य स्वयं प्राप्त करें
ParameterExpression value = Expression.Parameter(typeof(FooEntity), "x"); var propertyExpression = Expression.Property(value, "Enum");
प्रॉपर्टी नेम स्ट्रिंग के बजाय, आप nameof(FooEntity.Enum)
कंपाइलर nameof(FooEntity.Enum)
उपयोग कर सकते हैं या यहां तक कि System.Reflection.PropertyInfo
प्रॉपर्टी या System.Reflection.PropertyInfo
बारे में डेटा प्राप्त कर सकते हैं। लेकिन उदाहरण के लिए, हमारे लिए संपत्ति के नाम को स्पष्ट रूप से निर्धारित करना पर्याप्त है।
किसी विशिष्ट मान को वापस करने के लिए, हम Expression.Constant
पद्धति का उपयोग करते हैं। हम डिफ़ॉल्ट मान बनाते हैं
Expression resultExpression = Expression.Constant(string.Empty);
उसके बाद, हम क्रमिक रूप से एक स्थिति में "लपेट" करते हैं।
resultExpression = Expression.Condition( Expression.Equal(propertyExpression, Expression.Constant(FooEnum.Any)), Expression.Constant(EnumHelper.GetShortName(FooEnum.Any)), resultExpression); resultExpression = Expression.Condition( Expression.Equal(propertyExpression, Expression.Constant(FooEnum.Open)), Expression.Constant(EnumHelper.GetShortName(FooEnum.Open)), resultExpression); resultExpression = Expression.Condition( Expression.Equal(propertyExpression, Expression.Constant(FooEnum.Closed)), Expression.Constant(EnumHelper.GetShortName(FooEnum.Closed)), resultExpression);
public static class EnumHelper { public static string GetShortName(this Enum enumeration) { return (enumeration .GetType() .GetMember(enumeration.ToString())? .FirstOrDefault()? .GetCustomAttributes(typeof(DisplayAttribute), false)? .FirstOrDefault() as DisplayAttribute)? .ShortName ?? enumeration.ToString(); } }
जो कुछ भी बचता है, उसका परिणाम निकालना है
return Expression.Lambda<Func<TEntity, string>>(resultExpression, value);
थोड़ा और प्रतिबिंब
Enum के सभी मूल्यों की नकल करना अत्यंत असुविधाजनक है। चलो इसे ठीक करते हैं
var enumValues = Enum.GetValues(typeof(FooEnum)).Cast<Enum>(); Expression resultExpression = Expression.Constant(string.Empty); foreach (var enumValue in enumValues) { resultExpression = Expression.Condition( Expression.Equal(propertyExpression, Expression.Constant(enumValue)), Expression.Constant(EnumHelper.GetShortName(enumValue)), resultExpression); }
संपत्ति के मूल्य में सुधार होने दें
ऊपर दिए गए कोड का नुकसान उपयोग की गई इकाई के प्रकार के तंग बंधन है। यदि किसी अन्य वर्ग के संबंध में इसी तरह की समस्या को हल करने की आवश्यकता है, तो आपको टाइप एन्यूमरेशन की संपत्ति का मूल्य प्राप्त करने के लिए एक तरह से आने की आवश्यकता है। तो अभिव्यक्ति हमारे लिए करते हैं। विधि के एक पैरामीटर के रूप में, हम एक अभिव्यक्ति पास करेंगे जो संपत्ति का मूल्य प्राप्त करता है, और खुद कोड - हम बस इस संपत्ति के लिए परिणामों का एक सेट बनाते हैं। हमारी मदद करने के लिए टेम्पलेट
public static Expression<Func<TEntity, string>> CreateEnumShortNameExpression<TEntity, TEnum>(Expression<Func<TEntity, TEnum>> propertyExpression) where TEntity : class where TEnum : struct { var enumValues = Enum.GetValues(typeof(TEnum)).Cast<Enum>(); Expression resultExpression = Expression.Constant(string.Empty); foreach (var enumValue in enumValues) { resultExpression = Expression.Condition( Expression.Equal(propertyExpression.Body, Expression.Constant(enumValue)), Expression.Constant(EnumHelper.GetShortName(enumValue)), resultExpression); } return Expression.Lambda<Func<TEntity, string>>(resultExpression, propertyExpression.Parameters); }
कुछ स्पष्टीकरण। क्योंकि हमें एक और अभिव्यक्ति के माध्यम से इनपुट मूल्य मिलता है, फिर हमें Expression.Parameter
माध्यम से पैरामीटर घोषित करने की आवश्यकता नहीं है। हम इस पैरामीटर को इनपुट अभिव्यक्ति की संपत्ति से लेते हैं, और संपत्ति के मूल्य को प्राप्त करने के लिए अभिव्यक्ति के शरीर का उपयोग करते हैं।
तो आप इस तरह से नई विधि का उपयोग कर सकते हैं:
CreateMap<FooEntity, FooDto>() .ForMember(x => x.Enum, options => options.MapFrom(GetExpression<FooEntity, FooEnum>(x => x.Enum)));
अभिव्यक्ति पेड़ों के सभी सफल विकास।
मैं मैक्सिम अर्शिनोव के लेखों को पढ़ने की अत्यधिक सलाह देता हूं। विशेष रूप से उद्यम विकास में अभिव्यक्ति पेड़ के बारे में ।