تفاوت بین توابع کاربری وابسته به مدل و مستقل از مدل در DAX
این مقاله تفاوت میان توابع کاربری وابسته به مدل (Model-Dependent) و توابع مستقل از مدل (Model-Independent) در زبان DAX را توضیح میدهد. همچنین نشان میدهد که وابستگی توابع به ساختار مدل چگونه میتواند بر قابلیت حمل، نگهداری و پایداری کد تأثیر بگذارد، و در نهایت راهکارهایی برای جلوگیری از وابستگیهای پنهان و رعایت استانداردهای نامگذاری ارائه میکند.
فهرست مطالب
چرا شناخت تفاوت این دو نوع تابع مهم است؟
در Power BI و Analysis Services، استفاده از توابع کاربری (User-Defined Functions – UDFs) به توسعهدهندگان اجازه میدهد منطق پیچیده را یکبار بنویسند و چندینبار استفاده کنند.
اما اینکه تابع شما به مدل داده وابسته باشد یا نباشد، تعیین میکند:
تا چه حد میتوانید از تابع در فایلهای دیگر استفاده کنید
چقدر نگهداری و تغییر دادن آن آسان است
چطور در برابر تغییرات مدل (نام ستونها، جداول، روابط) مقاومت میکند
آیا انتقال آن به پروژه دیگر بدون خطا ممکن است یا خیر
توابع مدلوابسته (Model-Dependent DAX UDFs)
توابع مدلوابسته مستقیماً به عناصر مدل مانند:
نام جدولها
نام ستونها
روابط فعال/غیرفعال
فیلدهای محاسباتی خاص مدل
اتکا دارند.
پیامدهای استفاده از توابع مدلوابسته
قابل حمل نیستند؛ با تغییر مدل دچار خطا میشوند.
نگهداری دشوارتر؛ کوچکترین تغییر نام ستون باعث شکست تابع میشود.
وابستگیهای پنهان ایجاد میکنند؛ که در پروژههای بزرگ دردسرساز است.
توابع مستقل از مدل (Model-Independent DAX UDFs)
این توابع هیچ وابستگی مستقیم به ساختار مدل ندارند. ورودیها را از طریق پارامترها دریافت میکنند و نسبت به نام ستونها یا جداول بیتفاوت هستند.
مزایای توابع مستقل از مدل
کاملاً قابل حمل؛ در هر مدل میتوان از آنها استفاده کرد.
نگهداری سادهتر؛ تغییرات مدل مشکلی ایجاد نمیکند.
انعطافپذیری بالا؛ میتوان در پروژههای متعدد بدون تغییر استفاده کرد.
فهرست مطالب
تعریف توابع مدلوابسته در DAX
(Defining Model-Dependent Functions)
یک تابع مدلوابسته (Model-Dependent Function) تابعی است که یک یا چند وابستگی مستقیم به اشیای مشخص داخل مدل دارد؛ مانند:
ستونها (مثلاً Date[Date])
جدولها (مثلاً Customer)
یا حتی Measures موجود در همان مدل
وجود این ارجاعات باعث میشود تابع به ساختار مدل فعلی گره بخورد.
چرا چنین تابعی مدلوابسته محسوب میشود؟
اگر تابعی داخل خود به نام یک ستون، جدول یا Measure اشاره کند، در نتیجه:
نمیتوان آن را بدون خطا به یک Semantic Model دیگر منتقل کرد،
مگر اینکه تمام اجزای وابسته دقیقاً با همان نامها در مدل مقصد وجود داشته باشند.
به همین دلیل، این نوع تابع قابل حمل (Portable) نیست.
مشکل بزرگ: وابستگیها اعلام نمیشوند
یکی از چالشهای اصلی این است که توابع مدلوابسته وابستگیهای خود را بهطور صریح اعلام نمیکنند.
بههمین خاطر:
رابطهای کاربری فعلی برای ایجاد یا ویرایش User-Defined Functions
قبل از عملیات “Paste” هیچ بررسی یا هشدار جدی درباره نبود ستون یا جدول موردنیاز نمایش نمیدهند.
نتیجه؟
وقتی تابع را در یک مدل جدید Paste میکنی، خطاها فقط بعد از ثبت تابع ظاهر میشوند—نه قبل از آن.
PriceLookup = (
searchValue : VAL
) =>
SELECTCOLUMNS (
FILTER (
'Price Range',
'Price Range'[Min] <= searchValue && 'Price Range'[Max] > searchValue
),
'Price Range'[Range]
)
توابع مدلوابسته برای چه زمانی طراحی شدهاند؟
توابع مدلوابسته بهطور خاص برای استفاده در داخل یک Semantic Model واحد طراحی شدهاند.
این توابع نقش اصلی را در اشتراکگذاری منطق DAX در همان مدل ایفا میکنند.
به همین دلیل:
این توابع معمولاً وابستگی مستقیم به جدولها، ستونها یا Measures همان مدل دارند.
تعداد پارامترهای کمی دارند، زیرا اغلب به عناصر مدل ارجاع مستقیم میدهند.
هدف اصلی آنها سادهسازی اجرای منطق مشترک درون همان مدل است.
مزایا و محدودیتها
توابع مدلوابسته در یک مدل مشخص:
خوانا و ساده هستند
استفاده از آنها خیلی سریع است
اما قابل حمل نیستند و نمیتوان بدون تغییر در مدلهای دیگر از آنها استفاده کرد
هر گونه تغییر در ساختار Semantic Model—مثل تغییر نام ستونها، حذف جدولها یا تغییر روابط—میتواند باعث شکست توابع مدلوابسته شود.
به همین دلیل، توصیه میشود:
✔ قبل از انتشار یا استفاده گسترده، برای هر تابع مدلوابسته، Query یا Script تست اعتبار (Validation Test) اجرا شود تا صحت عملکرد آن در مدل بررسی شود.
تعریف توابع مدلمستقل (Model-Independent Functions)
یک تابع مدلمستقل هیچ وابستگی مستقیمی به ستونها، جدولها یا Measures ندارد.
تمام وابستگیها از طریق پارامترها به تابع منتقل میشوند.
به عبارت ساده:
تابع هیچ چیزی از مدل نمیداند؛ فقط ورودیهایی را که از بیرون میگیرد پردازش میکند.
ویژگیهای کلیدی
تمامی ارجاعات لازم باید بهصورت پارامتر وارد شود.
اعتبار ورودیها بر عهده تابعخوان (Caller) است.
به دلیل تعداد زیاد پارامترها، این توابع معمولاً طولانیتر و توصیفیتر هستند.
مثال
در ادامه مقاله از کدی به نام RangeLookup نام برده شده که نسخه مدلمستقل تابع PriceLookup است.
RangeLookup = (
searchValue : SCALAR VAL,
lookupTable : ANYREF EXPR,
minColumn : ANYREF EXPR,
maxColumn : ANYREF EXPR,
targetColumn : ANYREF EXPR
) =>
SELECTCOLUMNS (
FILTER (
lookupTable,
minColumn <= searchValue && maxColumn > searchValue
),
targetColumn
)
قابلیت استفاده مجدد توابع مدلمستقل (Model-Independent)
کد تابع RangeLookup بهراحتی میتواند در هر Semantic Model دیگری ادغام شود، زیرا:
تنها به پارامترهای ورودی وابسته است
هیچ وابستگیای به جدولها، ستونها، Measures یا روابط مدل ندارد
اجرای آن در مدلهای متنوع بدون نیاز به تغییر امکانپذیر است
این ویژگی باعث میشود این نوع تابع کاملاً قابل حمل (Portable) و مناسب برای اشتراکگذاری عمومی باشد.
توابع قابل انتشار در DAX Libraries
کتابخانههای DAX منتشرشده در فقط اجازه دارند توابع مدلمستقل را منتشر کنند.
به این دلیل:
توابع مدلوابسته وابسته به ساختار مدل هستند
قابل استفاده عمومی نیستند
ممکن است در مدلهای دیگر دچار خطا شوند
بنابراین توابع مدلوابسته در کتابخانههای عمومی DAX مجاز نیستند.
سادهسازی استفاده از توابع مدلمستقل در یک Semantic Model
پس از وارد کردن یک تابع مدلمستقل به داخل یک مدل، معمولاً لازم است استفاده از آن را در سطح مدل سادهتر کنیم.
برای این کار میتوان یک تابع مدلوابسته ایجاد کرد که تعداد پارامترها را کاهش دهد و وابستگی تابع را به مدل برقرار کند.
مثال
در ادامه مقاله، نمونهای از پیادهسازی تابع مدلوابسته PriceRangeLookup ارائه شده است.
این تابع:
در ظاهر یک تابع ساده با پارامترهای کمتر است
اما در درون خود، از تابع مدلمستقل PriceRange استفاده میکند
و با این کار، هم سادگی را حفظ میکند و هم قابلیت حمل تابع اصلی را از بین نمیبرد
به عبارت دیگر:
تابع مدلمستقل → برای استفاده مجدد در همه مدلها
تابع مدلوابسته → برای راحتی استفاده در یک مدل خاص
PriceRangeLookup = (
searchValue : VAL
) =>
RangeLookup(
searchValue,
'Price Range',
'Price Range'[Min],
'Price Range'[Max],
'Price Range'[Range]
)
توابع مدلوابسته در برابر توابع مدلمستقل
(Model-Dependent vs. Model-Independent Functions)
جدول زیر یک خلاصهی سریع از تفاوتهای میان توابع مدلوابسته و مدلمستقل ارائه میدهد. این مقایسه به شما کمک میکند تصمیم بگیرید چه زمانی باید از هر نوع تابع استفاده کنید و کدام رویکرد برای سناریوی شما مناسبتر است.
مشکلات وابستگی پنهان در Table Expressionها
(Hidden Dependency Issues in Table Expressions)
در نگاه اول، تشخیص اینکه یک تابع مدلوابسته است یا مدلمستقل بسیار ساده به نظر میرسد:
تابع یا به اشیای مدل ارجاع میدهد، یا نمیدهد.
اما در عمل، موضوع به این سادگی نیست—خصوصاً زمانی که پارامتر ورودی تابع یک جدول (Table) باشد.
مرز نامشخص بین استقلال و وابستگی
برخی توابع ظاهراً مدلمستقل هستند، اما بهصورت ضمنی فرضهایی درباره ساختار جدول ورودی دارند.
به عبارت دیگر:
تابع به مدل ارجاع مستقیم نمیدهد، اما به شکل و ستونهای جدول ورودی وابسته است.
این یعنی تابع یک «وابستگی پنهان» دارد.
وابستگی پنهان چگونه ایجاد میشود؟
اگر یک تابع هنگام پردازش جدولِ ورودی، به ستونهایی با نام مشخص یا ساختاری خاص دسترسی پیدا کند:
تابع در واقع فرض کرده است که جدول ورودی حتماً باید آن ستونها را داشته باشد
این فرض یک dependency خارجی ایجاد میکند
بنابراین تابع فقط زمانی درست کار میکند که جدول ورودی شرایط مورد انتظار را داشته باشد
این رفتار، یک وابستگی غیرشفاف و پنهان است که ممکن است هنگام استفاده در مدلهای مختلف، باعث خطا شود.
مثال
در ادامه مقاله، مثالی از چنین تابعی آورده شده است
SumAmount = (
transactionsTable : TABLE
) =>
SUMX (
transactionsTable,
[Quantity] * [Net Price]
)
وابستگی ستونها و اثر آن بر مستقل یا وابسته بودن تابع
ستونهای Quantity و Net Price باید در جدول transactionsTable وجود داشته باشند تا تابع SumAmount بدون خطا اجرا شود.
به همین دلیل، این تابع به ساختار مشخصی از پارامتر transactionsTable وابسته است.
وابستگیای که قابل اعلام نیست
متأسفانه این نوع وابستگی را نمیتوان با استفاده از کلماتکلیدی یا Syntax خاصی در DAX بهصورت برنامهنویسیشده بیان کرد.
تنها کاری که نویسنده تابع میتواند انجام دهد این است که:
در بخش کامنت (Comment) تابع، این پیشنیازها را توضیح دهد
و بگوید که جدول ورودی باید چه ستونهایی داشته باشد
شکست تابع در صورت نبودن ستونها
اگر پارامتر transactionsTable شامل هر دو ستون Quantity و Net Price نباشد:
اجرای تابع SumAmount با خطا مواجه میشود
زیرا تابع فرض میکند که این ستونها همیشه وجود دارند
آیا این وابستگی “کماهمیت” است؟
ممکن است فکر کنیم این وابستگی «ضعیفتر» از ارجاع مستقیم به مدل است،
چون تابع به مدل متصل نیست، بلکه فقط به ساختار جدول ورودی وابسته است.
اما واقعیت این است که:
این وابستگی همچنان قابلیت استفاده مجدد (Reusability) تابع را کاهش میدهد
و باعث میشود تابع عملاً مدلوابسته محسوب شود
زیرا بر وجود ستونهایی مشخص تکیه دارد—even if not bound to the model
بنابراین این نوع توابع نیز مدلوابسته هستند، چون وابسته به ساختار دادهاند، نه فقط به بایند مدل.
چگونه SumAmount را مدلمستقل کنیم؟
برای اینکه تابع SumAmount مدلمستقل شود، باید:
همه ارجاعات داخلی به ستونها
و هرگونه وابستگی محاسباتی
را بهصورت پارامترهای EXPR در تابع معرفی کنیم.
به عبارت دیگر:
تابع باید هیچ دانش داخلی از ستونها نداشته باشد
و تمامی نیازهایش را از طریق پارامترها دریافت کند.
مثال
در ادامه مقاله، نسخهی مدلمستقل SumAmount ارائه شده است که:
SumAmountIndependent = (
transactionsTable : TABLE EXPR,
quantityColumn : ANYREF EXPR,
priceColumn : ANYREF EXPR
) =>
SUMX (
transactionsTable,
quantityColumn * priceColumn
)
تابع SumAmountIndependent به دلیل عدم وابستگی به ستونها یا جداول مشخص، میتواند در یک کتابخانهٔ DAX مشترک (Shared DAX Library) منتشر شود.
این تابع:
کاملاً مدلمستقل است
فقط به پارامترهایی که دریافت میکند وابسته است
بنابراین در هر Semantic Model دیگری بدون مشکل کار میکند
سادهتر کردن استفاده در یک مدل مشخص
پس از وارد کردن نسخهٔ مدلمستقل تابع در یک مدل، معمولاً نیاز داریم استفاده از آن را برای کاربر نهایی یا توسعهدهنده سادهتر کنیم.
برای این هدف، میتوان یک تابع مدلوابسته تعریف کرد که:
هیچ پارامتری ندارد
در پشتصحنه، تابع مدلمستقل SumAmountIndependent را فراخوانی میکند
و مقادیر مناسب (مانند ستونهای Quantity و Net Price) را خودش ارسال میکند
SumAmountModel = ( ) =>
SumAmountIndependent (
Sales,
Sales[Quantity],
Sales[Net Price]
)
قواعد نامگذاری (Naming Conventions) برای توابع کاربری در DAX
اهمیت رعایت استاندارد نامگذاری
در سینتکس DAX هیچ اجباری برای انتخاب نام تابع یا پارامتر وجود ندارد.
اما از نظر:
خوانایی (Readability)
قابلیت استفاده مجدد (Usability)
جلوگیری از تداخل نامها هنگام وارد کردن چندین کتابخانه در یک مدل
رعایت استانداردهای نامگذاری بهشدت توصیه میشود.
برای خلاصهای کاملتر از استانداردها، میتوان به صفحه DAX Naming Conventions مراجعه کرد.
نامگذاری توابع (Function Names)
۱. استفاده از PascalCase
نام توابع باید با PascalCase نوشته شوند.
نمونه:GetCustomerDiscountCalculatePriceRange
۲. استفاده از نقطه (.) برای دستهبندی
استفاده از . باعث ایجاد ساختار مشابه namespace در زبانهای برنامهنویسی میشود.
۳. پیشوند Local. برای توابع مدلوابسته
این کار باعث میشود در آینده با نام توابع جدید DAX یا کتابخانههای دیگر دچار تداخل نشوند.
مثالهای توابع مدلوابسته:
GetCustomerDiscountLocal.GetCustomerDiscountLocal.Checkout.GetCustomerDiscount
۴. نامگذاری توابع مدلمستقل بر اساس کتابخانه
توابع مدلمستقل باید با نام کتابخانه شروع شوند.
مثالها:
Math.SumTwoNumbersMath.Common.SumTwoNumbersCompanyName.Math.SumTwoNumbers
در این مثالها:Math نام کتابخانه است.
اگر سازمان چند کتابخانه دارد، بهتر است از CompanyName بهعنوان پیشوند استفاده شود.
۵. ممنوعیت استفاده از کلمات رزرو شده DAX
کلماتی مانند Dax و سایر کلیدواژههایی که در DISCOVER_KEYWORDS DMV بازگردانده میشوند، نباید در نام توابع استفاده شوند.
نامگذاری پارامترها (Parameter Naming)
۱. استفاده از camelCase
نام پارامترها باید با camelCase نوشته شوند.
نمونه:lookupTableamountMeasure
۲. استفاده از پسوند برای EXPR Parameters
برای پارامترهایی با حالت عبور EXPR، باید پسوند نوع پارامتر اضافه شود.
این موضوع کمک میکند هنگام خواندن کد:
بفهمیم کجا Context Transition رخ میدهد
کدام پارامتر ستون است
کدام پارامتر جدول است
و کدام یک Measure است
پسوندهای استاندارد برای EXPR پارامترها:
| پسوند | نوع پارامتر | توضیح |
|---|---|---|
| Column | ستون | ارجاع به یک Column |
| Table | جدول | ارجاع به یک Table |
| Measure | میزِر | ارجاع به یک Measure |
| Expr | عبارت DAX | همه انواع EXPR غیر از موارد بالا |
| Calendar | تقویم | برای ارجاع تقویم، و نمیتواند EXPR باشد |
نمونه پارامترهای صحیح:
lookupTablelistPriceColumnamountMeasuremetricExpr
جمعبندی (Conclusions)
درک مفهوم وابستگی مدل در توابع DAX برای:
نگهداری کد
جلوگیری از خطا
و افزایش قابلیت استفادهی مجدد
کاملاً حیاتی است.
توصیههای کلیدی:
از توابع مدلمستقل برای ساخت کتابخانهها و اشتراکگذاری عمومی استفاده کنید.
از توابع مدلوابسته برای سادهسازی فراخوانی توابع مدلمستقل در داخل Semantic Model استفاده کنید.
نام توابع و پارامترها را طبق استانداردهای معرفیشده انتخاب کنید تا:
خوانایی کد بالا برود
ارائه آرگومانها هنگام فراخوانی تابع آسانتر شود
دیدگاهتان را بنویسید