Entity Framework half-heartedly supported Domain-Driven Design patterns. But the new-from-scratch EF Core has brought new hope for developers to map your well-designed domain classes to a database, reducing the cases where a separate data model is needed. EF Core 2.1 is very DDD friendly, even supporting things like fully encapsulated collections, backing fields and the return of support for value objects. In this session, we'll review some well-designed aggregates and explore how far EF Core 2.1 goes to act as the mapper between your domain classes and your data store.
نکته :
CultureInfo provider = CultureInfo.InvariantCulture; DateTime temp; string datestr = "01‐22‐2016"; Console.WriteLine(DateTime.TryParseExact(datestr, new string[] { "MM/dd/yyyy", "MM‐dd‐yyyy", "MM.dd.yyyy" }, provider, DateTimeStyles.None, out temp)); Console.WriteLine(temp);
کد بالا رو در ویندوز 10 و با سیستم تقویم شمسی اجرا کردم در نتیجه خروجی تابع یک تاریخ شمسی میباشد.این تبدیل به تاریخ شمسی به خاطر فرهنگ جاری سیستم است .
مطالب دورهها
متدهای توکار استفاده از نوع دادهای XML - قسمت دوم
امکان ترکیب دادههای یک بانک اطلاعاتی رابطهای و XML در SQL Server به کمک یک سری تابع کمکی خاص به نامهای sql:variable و sql:column پیش بینی شدهاست. sql:variable امکان استفاده از یک متغیر T-SQL را داخل یک XQuery میسر میسازد و توسط sql:column میتوان با یکی از ستونهای ذکر شده در قسمت select، داخل XQuery کار کرد. در ادامه به مثالهایی در این مورد خواهیم پرداخت.
ابتدا جدول xmlTest را به همراه چند رکورد ثبت شده در آن، درنظر بگیرید:
استفاده از متد sql:column
در ادامه میخواهیم مقدار ویژگی name رکوردی را که نام آن Vahid است، به همراه id آن ردیف، توسط یک XQuery بازگشت دهیم:
یک sql:column حتما نیاز به یک نام ستون دو قسمتی دارد. قسمت اول آن نام جدول است و قسمت دوم، نام ستون مورد نظر.
در مورد متد data در قسمت قبل بیشتر بحث شد و از آن برای استخراج دادهی یک ویژگی در اینجا استفاده شدهاست. عبارات داخل {} نیز پویا بوده و به همراه سایر قسمتهای ثابت return، ابتدا محاسبه و سپس بازگشت داده میشود.
اگر این کوئری را اجرا کنید، ردیف اول آن مساوی عبارت زیر خواهد بود
به همراه دو ردیف خالی دیگر در ادامه. این ردیفهای خالی به علت وجود دو رکورد دیگری است که با شرط where یاد شده تطابق ندارند.
یک روش برای حذف این ردیفهای خالی استفاده از متد exist است به شکل زیر:
در اینجا فقط ردیفی انتخاب خواهد شد که نام ویژگی آن Vahid است.
روش دوم استفاده از یک derived table و بازگشت ردیفهای غیرخالی است:
استفاده از متد sql:variable
در این مثال نحوهی بکارگیری یک متغیر T-SQL را داخل یک XQuery توسط متد sql:variable ملاحظه میکنید.
استفاده از For XML برای دریافت یکبارهی تمام ردیفهای XML
اگر کوئری معمولی ذیل را اجرا کنیم:
سه ردیف خروجی را مطابق سه رکوردی که ثبت کردیم، بازگشت میدهد.
اما اگر بخواهیم این سه ردیف را با هم ترکیب کرده و تبدیل به یک نتیجهی واحد کنیم، میتوان از For XML به نحو ذیل استفاده کرد:
بررسی متد xml.nodes
متد xml.nodes اندکی متفاوت است نسبت به تمام متدهایی که تاکنون بررسی کردیم. کار آن تجزیهی محتوای XML ایی به ستونها و سطرها میباشد. بسیار شبیه است به متد OpenXML اما کارآیی بهتری دارد.
در اینجا یک سند XML را درنظر بگیرید که از چندین نود شخص تشکیل شدهاست. اغلب آنها دارای یک name هستند. چهارمین نود، دو نام دارد و آخری بدون نام است.
در ادامه قصد داریم این اطلاعات را تبدیل به ردیفهایی کنیم که هر ردیف حاوی یک نام است. اولین سعی احتمالا استفاده از متد value خواهد بود:
این روش کار نمیکند زیرا متد value، بیش از یک مقدار را نمیتواند بازگشت دهد. البته میتوان از متد value به نحو زیر استفاده کرد:
اما حاصل آن دقیقا چیزی نیست که دنبالش هستیم؛ ما دقیقا نیاز به تمام نامها داریم و نه تنها یکی از آنها را.
سعی بعدی استفاده از متد query است:
در این حالت تمام نامها را بدست میآوریم:
اما این حاصل دو مشکل را به همراه دارد:
الف) خروجی آن XML است.
ب) تمام اینها در طی یک ردیف و یک ستون بازگشت داده میشوند.
و این خروجی نیز چیزی نیست که برای ما مفید باشد. ما به ازای هر شخص نیاز به یک ردیف جداگانه داریم. اینجا است که متد xml.nodes مفید واقع میشود:
خروجی متد xml.nodes یک table valued function است؛ یک جدول را باز میگرداند که دقیقا حاوی یک ستون میباشد. به همین جهت Alias آنرا با tab col مشخص کردهایم. tab متناظر است با جدول بازگشت داده شده و col متناظر است با تک ستون این جدول حاصل. این نامها در اینجا مهم نیستند؛ اما ذکر آنها اجباری است.
هر ردیف حاصل از این جدول بازگشت داده شده، یک اشارهگر است. به همین جهت نمیتوان آنها را مستقیما نمایش داد. هر سطر آن، به نودی که با آن مطابق XQuery وارد شده تطابق داشته است، اشاره میکند. در اینجا مطابق کوئری نوشته شده، هر ردیف به یک نود name اشاره میکند. در ادامه برای استخراج اطلاعات آن میتوان از متد text استفاده کرد.
اگر قصد داشتید، اطلاعات کامل نود ردیف جاری را مشاهده کنید میتوان از
استفاده کرد. دات در اینجا به معنای self است. دو دات (نقطه) پشت سرهم به معنای درخواست اطلاعات والد نود میباشد.
روش دیگر بدست آوردن مقدار یک نود را در کوئری ذیل مشاهده میکنید؛ value دات و data دات. خروجی value مقدار آن نود است و خروجی data مقدار آن نود با فرمت XML.
همچنین اگر بخواهیم اطلاعات تنها یک نود خاص را بدست بیاوریم، میتوان مانند کوئری ذیل عمل کرد:
در مورد کار با جداول، بجای متغیرهای T-SQL نیز روال کار به همین نحو است:
در اینجا یک جدول حاوی ستون XML ایی ایجاد شدهاست. سپس چهار ردیف در آن ثبت شدهاند. در آخر مقدار ویژگی نام این ردیفها بازگشت داده شدهاست.
نکته : استفادهی وسیع SQL Server از XML برای پردازش کارهای درونی آن
بسیاری از ابزارهایی که در نگارشهای جدید SQL Server اضافه شدهاند و یا مورد استفاده قرار میگیرند، استفادهی وسیعی از امکانات توکار XML آن دارند. مانند:
Showplan، گرافهای dead lock، گزارش پروسههای بلاک شده، اطلاعات رخدادها، SSIS Jobs، رخدادهای Trace و ...
مثال اول: کدام کوئریها در Plan cache، کارآیی پایینی داشته و table scan را انجام میدهند؟
اطلاعات Query Plan در SQL Server با فرمت XML ارائه میشود. در اینجا میخواهیم یک سری متغیر مانند Clustered Index Scan و امثال آنرا از ویژگی PhysicalOp آن کوئری بگیریم. بنابراین از متد sql:variable کمک گرفته شدهاست.
اگر علاقمند هستید که اصل این اطلاعات را با فرمت XML مشاهده کنید، کوئری نوشته شده را تا پیش از where آن یکبار مستقلا اجرا کنید. ستون آخر آن query_plan نام دارد و حاوی اطلاعات XML ایی است.
مثال دوم: استخراج اپراتورهای رابطهای (RelOp) از یک Query Plan ذخیره شده
در اینجا کار کردن با WITH XMLNAMESPACES در حین استفاده از متد xml.nodes سادهتر است؛ بجای قرار دادن فضای نام در تمام کوئریهای نوشته شده.
بررسی متد xml.modify
تا اینجا تمام کارهایی که صورت گرفت و نکاتی که بررسی شدند، به مباحث select اختصاص داشتند. اما insert، delete و یا update قسمتی از یک سند XML بررسی نشدند. برای این منظور باید از متد xml.modify استفاده کرد. از آن در عبارات update و یا set کمک گرفته شده و ورودی آن نباید نال باشد. در ادامه در طی مثالهایی این موارد را بررسی خواهیم کرد.
ابتدا فرض کنید که سند XML ما چنین شکلی را دارا است:
در ادامه قصد داریم یک نود جدید را پس از CustomerName اضافه کنیم.
اینکار را با استفاده از دستور insert، به نحو فوق میتوان انجام داد. از عبارت Set و متغیر doc مقدار دهی شده، کار شروع شده و سپس نود جدیدی پس از (after) اولین نود CustomerName موجود insert میشود. Select بعدی نتیجه را نمایش خواهد داد.
در SQL Server 2008 به بعد، امکان استفاده از متغیرهای T-SQL نیز در اینجا مجاز شدهاست:
بنابراین اگر نیاز به تعریف متغیری در اینجا داشتید از جمع زدن رشتهها استفاده نکنید. حتما نیاز است متغیر تعریف شود و گرنه باخطای ذیل متوقف خواهید شد:
افزودن ویژگیهای جدید به یک سند XML توسط متد xml.modify
اگر بخواهیم یک ویژگی (attribute) جدید را به نود خاصی اضافه کنیم میتوان به نحو ذیل عمل کرد:
که خروجی دو سطر ابتدایی آن پس از اضافه شدن ویژگی status با مقدار backorder به نحو ذیل است:
حذف نودهای یک سند XML توسط متد xml.modify
اگر بخواهیم تمام LineItemها را حذف کنیم میتوان نوشت:
با این خروجی:
به روز رسانی نودهای یک سند XML توسط متد xml.modify
اگر نیاز باشد تا مقدار یک نود را تغییر دهیم میتوان از replace value of استفاده کرد:
با خروجی ذیل که در آن نام اولین مشتری با مقدار Farid جایگزین شده است:
replace value of فقط با یک نود کار میکند و همچنین، فقط مقدار آن نود را تغییر میدهد. به همین جهت از متد text استفاده شدهاست. اگر از text استفاده نشود با خطای ذیل متوقف خواهیم شد:
به روز رسانی نودهای خالی توسط متد xml.modify
باید دقت داشت، نودهای خالی (بدون مقدار)، مانند LineItems پس از delete کلیه اعضای آن در مثال قبل، قابل replace نیستند و باید مقادیر جدید را در آنها insert کرد. یک مثال:
در این مثال اگر از replace value of برای مقدار دهی نود سوم استفاده میشد:
تغییری را پس از اعمال دستورات مشاهده نمیکردید؛ زیرا این المان ()text ایی را برای replace شدن ندارد.
ابتدا جدول xmlTest را به همراه چند رکورد ثبت شده در آن، درنظر بگیرید:
CREATE TABLE xmlTest ( id INT IDENTITY PRIMARY KEY, doc XML ) GO INSERT xmlTest VALUES('<Person name="Vahid" />') INSERT xmlTest VALUES('<Person name="Farid" />') INSERT xmlTest VALUES('<Person name="Mehdi" /><Person name="Hamid" />') GO
استفاده از متد sql:column
در ادامه میخواهیم مقدار ویژگی name رکوردی را که نام آن Vahid است، به همراه id آن ردیف، توسط یک XQuery بازگشت دهیم:
SELECT doc.query(' for $p in //Person where $p/@name="Vahid" return <li>{data($p/@name)} has id = {sql:column("xmlTest.id")}</li> ') FROM xmlTest
در مورد متد data در قسمت قبل بیشتر بحث شد و از آن برای استخراج دادهی یک ویژگی در اینجا استفاده شدهاست. عبارات داخل {} نیز پویا بوده و به همراه سایر قسمتهای ثابت return، ابتدا محاسبه و سپس بازگشت داده میشود.
اگر این کوئری را اجرا کنید، ردیف اول آن مساوی عبارت زیر خواهد بود
<li>Vahid has id = 1</li>
یک روش برای حذف این ردیفهای خالی استفاده از متد exist است به شکل زیر:
SELECT doc.query(' for $p in //Person where $p/@name="Vahid" return <li>{data($p/@name)} has id = {sql:column("xmlTest.id")}</li> ') FROM xmlTest WHERE doc.exist(' for $p in //Person where $p/@name="Vahid" return <li>{data($p/@name)} has id = {sql:column("xmlTest.id")}</li> ')=1
روش دوم استفاده از یک derived table و بازگشت ردیفهای غیرخالی است:
SELECT * FROM ( (SELECT doc.query(' for $p in //Person where $p/@name="Vahid" return <li>{data($p/@name)} has id = {sql:column("xmlTest.id")}</li> ') AS col1 FROM xmlTest) ) A WHERE CONVERT(VARCHAR(8000), col1)<>''
استفاده از متد sql:variable
DECLARE @number INT = 1 SELECT doc.query(' for $p in //Person where $p/@name="Vahid" return <li>{data($p/@name)} has number = {sql:variable("@number")}</li> ') FROM xmlTest
استفاده از For XML برای دریافت یکبارهی تمام ردیفهای XML
اگر کوئری معمولی ذیل را اجرا کنیم:
SELECT doc.query('/Person') FROM xmlTest
اما اگر بخواهیم این سه ردیف را با هم ترکیب کرده و تبدیل به یک نتیجهی واحد کنیم، میتوان از For XML به نحو ذیل استفاده کرد:
DECLARE @doc XML SET @doc = (SELECT * FROM xmlTest FOR XML AUTO, ELEMENTS) SELECT @doc.query('/xmlTest/doc/Person')
بررسی متد xml.nodes
متد xml.nodes اندکی متفاوت است نسبت به تمام متدهایی که تاکنون بررسی کردیم. کار آن تجزیهی محتوای XML ایی به ستونها و سطرها میباشد. بسیار شبیه است به متد OpenXML اما کارآیی بهتری دارد.
DECLARE @doc XML =' <people> <person><name>Vahid</name></person> <person><name id="2">Farid</name></person> <person><name>Mehdi</name></person> <person><name>Hooshang</name><name id="1">Hooshi</name></person> <person></person> </people> '
در ادامه قصد داریم این اطلاعات را تبدیل به ردیفهایی کنیم که هر ردیف حاوی یک نام است. اولین سعی احتمالا استفاده از متد value خواهد بود:
SELECT @doc.value('/people/person/name', 'varchar(50)')
SELECT @doc.value('(/people/person/name)[1]', 'varchar(50)')
سعی بعدی استفاده از متد query است:
SELECT @doc.query('/people/person/name')
<name>Vahid</name> <name id="2">Farid</name> <name>Mehdi</name> <name>Hooshang</name> <name id="1">Hooshi</name>
الف) خروجی آن XML است.
ب) تمام اینها در طی یک ردیف و یک ستون بازگشت داده میشوند.
و این خروجی نیز چیزی نیست که برای ما مفید باشد. ما به ازای هر شخص نیاز به یک ردیف جداگانه داریم. اینجا است که متد xml.nodes مفید واقع میشود:
SELECT tab.col.value('text()[1]', 'varchar(50)') AS name, tab.col.query('.'), tab.col.query('..') from @doc.nodes('/people/person/name') AS tab(col)
هر ردیف حاصل از این جدول بازگشت داده شده، یک اشارهگر است. به همین جهت نمیتوان آنها را مستقیما نمایش داد. هر سطر آن، به نودی که با آن مطابق XQuery وارد شده تطابق داشته است، اشاره میکند. در اینجا مطابق کوئری نوشته شده، هر ردیف به یک نود name اشاره میکند. در ادامه برای استخراج اطلاعات آن میتوان از متد text استفاده کرد.
اگر قصد داشتید، اطلاعات کامل نود ردیف جاری را مشاهده کنید میتوان از
tab.col.query('.'),
روش دیگر بدست آوردن مقدار یک نود را در کوئری ذیل مشاهده میکنید؛ value دات و data دات. خروجی value مقدار آن نود است و خروجی data مقدار آن نود با فرمت XML.
SELECT tab.col.value('.', 'varchar(50)') AS name, tab.col.query('data(.)'), tab.col.query('.'), tab.col.query('..') from @doc.nodes('/people/person/name') AS tab(col)
همچنین اگر بخواهیم اطلاعات تنها یک نود خاص را بدست بیاوریم، میتوان مانند کوئری ذیل عمل کرد:
SELECT tab.col.value('name[.="Farid"][1]', 'varchar(50)') AS name, tab.col.value('name[.="Farid"][1]/@id', 'varchar(50)') AS id, tab.col.query('.') from @doc.nodes('/people/person[name="Farid"]') AS tab(col)
در مورد کار با جداول، بجای متغیرهای T-SQL نیز روال کار به همین نحو است:
DECLARE @tblXML TABLE ( id INT IDENTITY PRIMARY KEY, doc XML ) INSERT @tblXML VALUES('<person name="Vahid" />') INSERT @tblXML VALUES('<person name="Farid" />') INSERT @tblXML VALUES('<person />') INSERT @tblXML VALUES(NULL) SELECT id, doc.value('(/person/@name)[1]', 'varchar(50)') AS name FROM @tblXML
نکته : استفادهی وسیع SQL Server از XML برای پردازش کارهای درونی آن
بسیاری از ابزارهایی که در نگارشهای جدید SQL Server اضافه شدهاند و یا مورد استفاده قرار میگیرند، استفادهی وسیعی از امکانات توکار XML آن دارند. مانند:
Showplan، گرافهای dead lock، گزارش پروسههای بلاک شده، اطلاعات رخدادها، SSIS Jobs، رخدادهای Trace و ...
مثال اول: کدام کوئریها در Plan cache، کارآیی پایینی داشته و table scan را انجام میدهند؟
CREATE PROCEDURE LookForPhysicalOps (@op VARCHAR(30)) AS SELECT sql.text, qs.EXECUTION_COUNT, qs.*, p.* FROM sys.dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text(sql_handle) sql CROSS APPLY sys.dm_exec_query_plan(plan_handle) p WHERE query_plan.exist(' declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; /ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = sql:variable("@op")] ') = 1 GO EXECUTE LookForPhysicalOps 'Table Scan' EXECUTE LookForPhysicalOps 'Clustered Index Scan' EXECUTE LookForPhysicalOps 'Hash Match'
اگر علاقمند هستید که اصل این اطلاعات را با فرمت XML مشاهده کنید، کوئری نوشته شده را تا پیش از where آن یکبار مستقلا اجرا کنید. ستون آخر آن query_plan نام دارد و حاوی اطلاعات XML ایی است.
مثال دوم: استخراج اپراتورهای رابطهای (RelOp) از یک Query Plan ذخیره شده
WITH XMLNAMESPACES(DEFAULT N'http://schemas.microsoft.com/sqlserver/2004/07/showplan') SELECT RelOp.op.value(N'../../@NodeId', N'int') AS ParentOperationID, RelOp.op.value(N'@NodeId', N'int') AS OperationID, RelOp.op.value(N'@PhysicalOp', N'varchar(50)') AS PhysicalOperator, RelOp.op.value(N'@LogicalOp', N'varchar(50)') AS LogicalOperator, RelOp.op.value(N'@EstimatedTotalSubtreeCost ', N'float') AS EstimatedCost, RelOp.op.value(N'@EstimateIO', N'float') AS EstimatedIO, RelOp.op.value(N'@EstimateCPU', N'float') AS EstimatedCPU, RelOp.op.value(N'@EstimateRows', N'float') AS EstimatedRows, cp.plan_handle AS PlanHandle, st.TEXT AS QueryText, qp.query_plan AS QueryPlan, cp.cacheobjtype AS CacheObjectType, cp.objtype AS ObjectType FROM sys.dm_exec_cached_plans cp CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp CROSS APPLY qp.query_plan.nodes(N'//RelOp') RelOp(op)
بررسی متد xml.modify
تا اینجا تمام کارهایی که صورت گرفت و نکاتی که بررسی شدند، به مباحث select اختصاص داشتند. اما insert، delete و یا update قسمتی از یک سند XML بررسی نشدند. برای این منظور باید از متد xml.modify استفاده کرد. از آن در عبارات update و یا set کمک گرفته شده و ورودی آن نباید نال باشد. در ادامه در طی مثالهایی این موارد را بررسی خواهیم کرد.
ابتدا فرض کنید که سند XML ما چنین شکلی را دارا است:
DECLARE @doc XML = ' <Invoice> <InvoiceId>100</InvoiceId> <CustomerName>Vahid</CustomerName> <LineItems> <LineItem> <Sku>134</Sku> <Quantity>10</Quantity> <Description>Item 1</Description> <UnitPrice>9.5</UnitPrice> </LineItem> <LineItem> <Sku>150</Sku> <Quantity>5</Quantity> <Description>Item 2</Description> <UnitPrice>1.5</UnitPrice> </LineItem> </LineItems> </Invoice> '
SET @doc.modify(' insert <InvoiceInfo><InvoiceDate>2014-02-10</InvoiceDate></InvoiceInfo> after /Invoice[1]/CustomerName[1] ') SELECT @doc
<Invoice> <InvoiceId>100</InvoiceId> <CustomerName>Vahid</CustomerName> <InvoiceInfo> <InvoiceDate>2014-02-10</InvoiceDate> </InvoiceInfo> <LineItems> ...
در SQL Server 2008 به بعد، امکان استفاده از متغیرهای T-SQL نیز در اینجا مجاز شدهاست:
SET @x.modify('insert sql:variable("@x") into /doc[1]')
The argument 1 of the XML data type method "modify" must be a string literal.
افزودن ویژگیهای جدید به یک سند XML توسط متد xml.modify
اگر بخواهیم یک ویژگی (attribute) جدید را به نود خاصی اضافه کنیم میتوان به نحو ذیل عمل کرد:
SET @doc.modify(' insert attribute status{"backorder"} into /Invoice[1] ') SELECT @doc
<Invoice status="backorder"> <InvoiceId>100</InvoiceId> ....
حذف نودهای یک سند XML توسط متد xml.modify
اگر بخواهیم تمام LineItemها را حذف کنیم میتوان نوشت:
SET @doc.modify('delete /Invoice/LineItems/LineItem') SELECT @doc
<Invoice status="backorder"> <InvoiceId>100</InvoiceId> <CustomerName>Vahid</CustomerName> <InvoiceInfo> <InvoiceDate>2014-02-10</InvoiceDate> </InvoiceInfo> <LineItems /> </Invoice>
به روز رسانی نودهای یک سند XML توسط متد xml.modify
اگر نیاز باشد تا مقدار یک نود را تغییر دهیم میتوان از replace value of استفاده کرد:
SET @doc.modify('replace value of /Invoice[1]/CustomerName[1]/text()[1] with "Farid" ') SELECT @doc
<Invoice status="backorder"> <InvoiceId>100</InvoiceId> <CustomerName>Farid</CustomerName> <InvoiceInfo> <InvoiceDate>2014-02-10</InvoiceDate> </InvoiceInfo> <LineItems /> </Invoice>
The target of 'replace value of' must be a non-metadata attribute or an element with simple typed content.
به روز رسانی نودهای خالی توسط متد xml.modify
باید دقت داشت، نودهای خالی (بدون مقدار)، مانند LineItems پس از delete کلیه اعضای آن در مثال قبل، قابل replace نیستند و باید مقادیر جدید را در آنها insert کرد. یک مثال:
DECLARE @tblTest AS TABLE (xmlField XML) INSERT INTO @tblTest(xmlField) VALUES ( '<Sample> <Node1>Value1</Node1> <Node2>Value2</Node2> <Node3/> </Sample>' ) DECLARE @newValue VARCHAR(50) = 'NewValue' UPDATE @tblTest SET xmlField.modify( 'insert text{sql:variable("@newValue")} into (/Sample/Node3)[1] [not(text())]' ) SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest
UPDATE @tblTest SET xmlField.modify( 'replace value of (/Sample/Node3/text())[1] with sql:variable("@newValue")' )
اشتراکها
کتابخانه jquery-sortable
jQuery Sortable is a flexible, opinionated sorting plugin. It enables items in a list (or table etc.) to be sorted horizontally and vertically using the mouse. Supports nested lists and pure drag/drop containers. Demo
jQuery Sortable does not depend on jQuery UI and works well with Twitter's Bootstrap (You can even sort the Bootstrap navigation).
سلام؛ چطوری میشه از لینک سرور در تریگر جداول استفاده کرد؟ یک جدول داریم که میخوایم در هنگام ویرایش بعضی از فیلدها این مقادیر در یک دیتابیس دیگه (MySQL) که از طریق لینک سرور بهش دسترسی داریم، آپدیت یا درج بشن. در حال حاضر کدهای ارائه شده در اینجا در حالت عادی درست کار میکنن اما به محض گذاشتنشون تو تریگر دیگه کار نمیکنن و خطای زیر رو میده :
The requested operation could not be performed because OLE DB provider "MSDASQL" for linked server "MYSQL_LINKSERVER" does not support the required transaction interface.
نظرات مطالب
خلاصه اشتراکهای روز دو شنبه 21 آذر 1390
http://visualstudiomagazine.com/blogs/redmond-review/2011/11/does-windows-8-spell-the-end-of-silverlight.aspxتو این لینک نوشته شده که گروه اصلی سیلورلایت به گروه Windows Azure رفته و جان پاپا هم کلا از مایکروسافت رفته البته این رو هم نوشته که نمیدونه این نسخه آخرین نسخه هست یا نه.
در این لینک هم
http://forums.silverlight.net/t/244120.aspx/2/10?When+will+Microsoft+announce+this+as+the+final+version+
Pete Brown مطالبی رو عنوان میکنه که قابل توجه هستش
در این لینک هم
http://forums.silverlight.net/t/244120.aspx/2/10?When+will+Microsoft+announce+this+as+the+final+version+
Pete Brown مطالبی رو عنوان میکنه که قابل توجه هستش
یا اگر نمونهی AuthorizationHandler سفارشی آنرا نیاز داشتید، به صورت زیر است:
- ابتدا یک IAuthorizationRequirement و AuthorizationHandler سفارشی را ایجاد میکنیم که در هندلر آن دسترسی کاملی به اطلاعات کاربر وارد شدهی به سیستم وجود دارد:
public class UserCanSeeProjectRequirement : IAuthorizationRequirement { public UserCanSeeProjectRequirement() { } } public class UserCanSeeProjectHandler : AuthorizationHandler<UserCanSeeProjectRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserCanSeeProjectRequirement requirement) { //claim-based validation if (context.User.HasClaim("permission.cansee", "CanSee")) context.Succeed(requirement); //role-based validation if (context.User.IsInRole("admin") || context.User.IsInRole("user")) context.Succeed(requirement); return Task.CompletedTask; } }
namespace BlazorWasm.Client { public class Program { public static async Task Main(string[] args) { // ... services.AddScoped<IAuthorizationHandler, UserCanSeeProjectHandler>(); services.AddAuthorizationCore(options => { options.AddPolicy("UserCanSeeProjectPolicy", policy => policy.Requirements.Add(new UserCanSeeProjectRequirement())); }); // ... } } }
@attribute [Authorize(Policy = "UserCanSeeProjectPolicy")]
<AuthorizeView Policy="UserCanSeeProjectPolicy"> <NotAuthorized> <h2 class="mt-5">You are not authorized to view this page</h2> </NotAuthorized> <Authorized> <div class="container my-profile"> --- Place here all the content you want your user to view ---- </div> </Authorized> </AuthorizeView>
نظرات مطالب
ExtJs! رویا یا کابوس؟
به عنوان کسی که توی یه شرکت توی یه پروژه Enterprise تقریباً یک سال با این فریمورک درگیر بودم، فقط یه چیز میتونم بگم:
Get away from Ext JS
نظرات مطالب
EF Code First #12
با عرض سلام
TEntity از کجا اومده و کجا و چطور سیستم ازش استفاده میکنه؟
ممنون میشم اگه توضیح بدین این خط کد یعنی چی؟
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
در مورد ساختار این کد توضیح بدید .
با تشکر.
در دو قسمت قبل، ساختار ابتدایی برنامهی Minimal API's بلاگ دهی را ایجاد کردیم. در این قسمت میخواهیم بررسی کنیم، معادلهای کنترلرهای MVC و اکشن متدهای آنها در سیستم جدید Minimal API، به چه صورتی ایجاد میشوند.
ایجاد اولین endpoint از نوع Get مبتنی بر Minimal API
برای افزودن اولین endpoint برنامه، به فایل Program.cs برنامه مراجعه کرده و آنرا به صورت زیر تکمیل میکنیم:
app.MapGet، معادل یک اکشن متد کنترلرهای MVC را که از نوع HttpGet هستند، ارائه میدهد. در همینجا میتوان آدرس دقیق این endpoint را به عنوان پارامتر اول، مشخص کرد که پس از فراخوانی آن در مرورگر، یک Delegate که هندلر نام دارد (پارامتر دوم این متد)، اجرا میشود تا Response ای را ارائه دهد.
همانطور که مشاهده میکنید میتوان در اینجا، این Delegate را از نوع Lambda expressions تعریف کرد و با ذکر MinimalBlogDbContext به صورت یک پارامتر آن، کار تزریق وابستگیهای خودکار آن نیز صورت میگیرد. شبیه به حالتی که میتوان یک سرویس را به عنوان پارامتر یک اکشن متد، با ذکر ویژگی [FromServices] در کنترلرهای MVC معرفی کرد؛ البته در اینجا بدون نیاز به ذکر این ویژگی (هرچند هنوز هم قابل ذکر است). مزیت آن این است که هر endpoint، تنها سرویسهای مورد نیاز خودش را دریافت میکند و نه یک لیست قابل توجه از تمام سرویسهایی که قرار است در قسمتهای مختلف یک کنترلر استفاده شوند.
پس از آن میتوان با Context ای که در اختیار داریم، عملیات مدنظر را پیاده سازی کرده و یک خروجی را ارائه دهیم. در اینجا دیگر نیازی به تعریف IActionResultها و امثال آن نیست و همه چیز ساده شدهاست.
ایجاد اولین endpoint از نوع Post مبتنی بر Minimal API
app.MapPost، معادل یک اکشن متد کنترلرهای MVC را که از نوع HttpPost هستند، ارائه میدهد:
در ابتدا یک Dto را که حاوی اطلاعات نویسندهی جدیدی است، معادل خواص مدل Author دومین برنامه، تعریف میکنیم. سپس میتوان این Dto را نیز به صورت یک پارامتر جدید به Lambda Expression متد app.MapPost معرفی کرد تا کار نگاشت اطلاعات دریافتی به آن، به صورت خودکار انجام شود (حالت پیشفرض آن [FromBody] است که نیازی به ذکر آن نیست).
سعی شدهاست تا این مثال در سادهترین شکل ممکن خودش ارائه شود. در ادامه کار نگاشت خواص Dto را به مدل دومین برنامه، توسط AutoMapper انجام خواهیم داد.
مابقی نکات متد app.MapPost نیز مانند متد app.MapGet است؛ برای مثال در اینجا نیز تعریف مسیر endpoint، توسط اولین پارامتر این متد صورت میگیرد و نحوهی تزریق سرویس DbContext برنامه نیز یکی است.
آزمایش برنامهی Minimal API's
برنامهی Minimal API's تهیه شده، به همراه یک Swagger از پیش تنظیم شده نیز هست. به همین جهت برای کار با این API الزاما نیازی به استفادهی از مثلا برنامهی Postman یا راه حلهای مشابه نیست. بنابراین فقط کافی است تا برنامهی API را اجرا کرده و در رابط کاربری ظاهر شده در آدرس https://localhost:7085/swagger/index.html، بر روی دکمهی Try it out هر کدام از endpointها کلیک کنیم. برای مثال اگر چنین کاری را در قسمت Post انجام دهیم، به تصویر زیر میرسیم:
در اینجا پس از ویرایش اطلاعات شیء JSON ای که برای ما تدارک دیدهاست، فقط کافی است بر روی دکمهی execute ذیل آن کلیک کنیم تا اطلاعات این Dto را به app.MapPost متناظر فوق ارسال کند و برای نمونه خروجی بازگشتی از سرور را نیز در همینجا نمایش میدهد که در آن، Id رکورد نیز پس از ثبت در بانک اطلاعاتی، مشخص است:
شروع به Refactoring و خلوت کردن فایل Program.cs
اگر بخواهیم به همین نحو تمام endpoints و dtoها را داخل فایل Program.cs اضافه کنیم، پس از مدتی به یک فایل بسیار حجیم و غیرقابل نگهداری خواهیم رسید. بنابراین در مرحلهی اول، تنظیمات سرویسها و میان افزارها را به خارج از آن منتقل میکنیم. برای این منظور پوشهی جدید Extensions را به همراه دو کلاس زیر ایجاد میکنیم:
کار این متد الحاقی، خارج کردن تنظیمات سرویسهای برنامه از کلاس Program است.
همچنین نیاز به متد الحاقی دیگری برای خارج کردن تنظیمات میانافزارها داریم:
پس از این تغییرات، اکنون ابتدای کلاس Program برنامهی Api به صورت زیر تغییر میکند و خلاصه میشود:
در قسمت بعد، endpoints را از این کلاس آغازین برنامه خارج میکنیم.
ایجاد اولین endpoint از نوع Get مبتنی بر Minimal API
برای افزودن اولین endpoint برنامه، به فایل Program.cs برنامه مراجعه کرده و آنرا به صورت زیر تکمیل میکنیم:
// ... app.UseHttpsRedirection(); app.MapGet("/api/authors", async (MinimalBlogDbContext ctx) => { var authors = await ctx.Authors.ToListAsync(); return authors; }); app.Run();
همانطور که مشاهده میکنید میتوان در اینجا، این Delegate را از نوع Lambda expressions تعریف کرد و با ذکر MinimalBlogDbContext به صورت یک پارامتر آن، کار تزریق وابستگیهای خودکار آن نیز صورت میگیرد. شبیه به حالتی که میتوان یک سرویس را به عنوان پارامتر یک اکشن متد، با ذکر ویژگی [FromServices] در کنترلرهای MVC معرفی کرد؛ البته در اینجا بدون نیاز به ذکر این ویژگی (هرچند هنوز هم قابل ذکر است). مزیت آن این است که هر endpoint، تنها سرویسهای مورد نیاز خودش را دریافت میکند و نه یک لیست قابل توجه از تمام سرویسهایی که قرار است در قسمتهای مختلف یک کنترلر استفاده شوند.
پس از آن میتوان با Context ای که در اختیار داریم، عملیات مدنظر را پیاده سازی کرده و یک خروجی را ارائه دهیم. در اینجا دیگر نیازی به تعریف IActionResultها و امثال آن نیست و همه چیز ساده شدهاست.
ایجاد اولین endpoint از نوع Post مبتنی بر Minimal API
app.MapPost، معادل یک اکشن متد کنترلرهای MVC را که از نوع HttpPost هستند، ارائه میدهد:
//... app.UseHttpsRedirection(); //... app.MapPost("/api/authors", async (MinimalBlogDbContext ctx, AuthorDto authorDto) => { var author = new Author(); author.FirstName = authorDto.FirstName; author.LastName = authorDto.LastName; author.Bio = authorDto.Bio; author.DateOfBirth = authorDto.DateOfBirth; ctx.Authors.Add(author); await ctx.SaveChangesAsync(); return author; }); app.Run(); internal record AuthorDto(string FirstName, string LastName, DateTime DateOfBirth, string? Bio);
سعی شدهاست تا این مثال در سادهترین شکل ممکن خودش ارائه شود. در ادامه کار نگاشت خواص Dto را به مدل دومین برنامه، توسط AutoMapper انجام خواهیم داد.
مابقی نکات متد app.MapPost نیز مانند متد app.MapGet است؛ برای مثال در اینجا نیز تعریف مسیر endpoint، توسط اولین پارامتر این متد صورت میگیرد و نحوهی تزریق سرویس DbContext برنامه نیز یکی است.
آزمایش برنامهی Minimal API's
برنامهی Minimal API's تهیه شده، به همراه یک Swagger از پیش تنظیم شده نیز هست. به همین جهت برای کار با این API الزاما نیازی به استفادهی از مثلا برنامهی Postman یا راه حلهای مشابه نیست. بنابراین فقط کافی است تا برنامهی API را اجرا کرده و در رابط کاربری ظاهر شده در آدرس https://localhost:7085/swagger/index.html، بر روی دکمهی Try it out هر کدام از endpointها کلیک کنیم. برای مثال اگر چنین کاری را در قسمت Post انجام دهیم، به تصویر زیر میرسیم:
در اینجا پس از ویرایش اطلاعات شیء JSON ای که برای ما تدارک دیدهاست، فقط کافی است بر روی دکمهی execute ذیل آن کلیک کنیم تا اطلاعات این Dto را به app.MapPost متناظر فوق ارسال کند و برای نمونه خروجی بازگشتی از سرور را نیز در همینجا نمایش میدهد که در آن، Id رکورد نیز پس از ثبت در بانک اطلاعاتی، مشخص است:
شروع به Refactoring و خلوت کردن فایل Program.cs
اگر بخواهیم به همین نحو تمام endpoints و dtoها را داخل فایل Program.cs اضافه کنیم، پس از مدتی به یک فایل بسیار حجیم و غیرقابل نگهداری خواهیم رسید. بنابراین در مرحلهی اول، تنظیمات سرویسها و میان افزارها را به خارج از آن منتقل میکنیم. برای این منظور پوشهی جدید Extensions را به همراه دو کلاس زیر ایجاد میکنیم:
using Microsoft.EntityFrameworkCore; using MinimalBlog.Dal; namespace MinimalBlog.Api.Extensions; public static class ServiceCollectionExtensions { public static IServiceCollection AddApplicationServices(this IServiceCollection services, WebApplicationBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var connectionString = builder.Configuration.GetConnectionString("Default"); builder.Services.AddDbContext<MinimalBlogDbContext>(opt => opt.UseSqlServer(connectionString)); return services; } }
همچنین نیاز به متد الحاقی دیگری برای خارج کردن تنظیمات میانافزارها داریم:
namespace MinimalBlog.Api.Extensions; public static class WebApplicationExtensions { public static WebApplication ConfigureApplication(this WebApplication app) { if (app == null) { throw new ArgumentNullException(nameof(app)); } if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); return app; } }
var builder = WebApplication.CreateBuilder(args); builder.Services.AddApplicationServices(builder); var app = builder.Build(); app.ConfigureApplication();
در قسمت بعد، endpoints را از این کلاس آغازین برنامه خارج میکنیم.