هدف | سرویسهای اضافه شده | متد تنظیم سرویسها |
- مناسب برای توسعهی Web API - اما باید بخاطر داشت که این سرویسها را به صورت پیشفرض اضافه نمیکند:
|
| ()AddControllers |
شبیه به ASP.NET Core 1.X عمل میکند؛ یعنی سرویس Pages را که مرتبط با Razor Pages است، به صورت پیشفرض ثبت نمیکند. |
| ()AddControllersWithViews |
برای کار با Razor pages بوده و این سرویسها را به صورت پیشفرض به همراه ندارد:
|
| ()AddRazorPages |
- اگر هاست شما تمام سایتها را با یک Application pool مدیریت میکند، کرش یکی از چند ده سایت دیگر میتواند سبب ریاستارت شدن سایت شما هم بشود؛ چون برنامهها از همه ایزوله نشدهاند. راه حل آن ایجاد یک Application pool مجزا به ازای هر سایت هست (توسط هاستدار).
زمانیکه تمام سایتها با یک Application pool واحد مدیریت میشوند، تمام آنها توسط یک وهله از w3wp.exe اجرا خواهند شد. با تعریف Application poolهای مجزا، هر سایت، یک وهلهی مجزا از w3wp.exe را به خود اختصاص خواهد داد. یعنی اگر Task manager سرور را بررسی کنید، به ازای هر سایت، یک w3wp.exe با pid مجزا قابل مشاهده است. به این ترتیب اگر pid=1234 کرش کرد، تاثیری روی pid=4321 نخواهد داشت.
- یک برنامهی ASP.NET پس از مدتی بیکاری (قابل تنظیم در Application pool برنامه)، به صورت خودکار توسط IIS از حافظه خارج میشود. با درخواست بعدی که به آن برنامه میرسد (مثلا گشودن یک صفحهی آن توسط یک کاربر)، مجددا از صفر اجرا خواهد شد. این مورد نیز به معنای ریاستارت کامل برنامه است.
- در تنظیمات Application pool موارد زیادی را میتوان تنظیم کرد که سبب ریاستارت شدن برنامه میشوند. برای مثال اگر مصرف CPU و یا حافظهی برنامه به حد مشخصی رسید، برنامه ریاستارت شود و امثال آن.
یک سالی میشود که روی یک فریمورک رابط کاربری بصورت
متن باز به اسم HandyControl مشارکت دارم، این فریمورک در درجه اول مخصوص
برنامه نویسهای چین طراحی شده بود اما با بازخوردهایی که دریافت کرد،
جامعه انگلیسی زبان را هم پشتیبانی میکند. بدلیل اینکه هدف این فریمورک
ارائه کنترلهای ساده و بدور از پیچیدگی هست، Nabian با
اضافه کردن کنترلهای پیچیده مخالف هست. به همین دلیل تصمیم گرفتم تا شاخه
جدایی از این پروژه را ایجاد کنم و کنترلها و ویژگیهای مختلفی که سازگار
با برنامه نویسهای ایرانی باشد را اضافه کنم. درحال حاضر تقویم شمسی،
ساعت_تقویم شمسی، پشتیبانی از کد ملی، حروف فارسی در نوع ورودی TextBox
،استایلهای جدید بارگذاری، کنترل BusyIndicator، کنترل سرعت شمار
(SpeedoMeter) و چندین کلاس هلپر جهت کار با رجیستری، رمزنگاری و... را به
پروژه اضافه کردم.(لیست ویژگیهای اضافه شده را میتوانید از این قسمت مشاهده
کنید) (جهت مشاهده کنترلها و ویژگیهای اضافه شده در نسخه شخصی سازی شده
بعد از اجرای دمو از بخش Controls بخش PersianToolkit را انتخاب کنید)
صفحات پروژه اصلی===> سورس برنامه | صفحه ناگت | صفحه مارکت پلیس | مستندات چینی | مستندات انگلیسی | مستندات ویکی
همیشه مشکل هایی با API وجود داشته که توسعه دهندگان را آزار میدهد:
- وجود endpointهای متفاوت
- نسخههای متفاوت هر API
- درخواستهای متعدد
- دادههای زیاد که نیازی به آنها نداریم
- مستند سازی هر endpoint و پارامترهای آن
برای حل هرکدام از مشکلات بالا در RestAPI راه حل هایی ارائه شده است.
شرکتهای بزرگ نیز از این قاعده جدا نیستند فیس بوک اما عقب نکشیده است و راه حلی ارائه کرده است به نام graphql این رویکرد یک library یا یک فریم ورک نیست بلکه راه حلی برای ساخت api با ساختاری شبیه به graph است.
Graphql در اصل به جای اینکه api را در قالب endpointهای متعدد در نظر بگیرید آنها را در قالب graph در نظر میگیرد.
تزریق وابستگیهای رایج ASP.NET MVC به برنامه
ioc.For<IIdentity>() .Use( () => (HttpContext.Current != null && HttpContext.Current.User != null) ? HttpContext.Current.User.Identity : null);
عیب یابی و دیباگ برنامههای SignalR
پردازشگر json، در نگارشهای اخیر IE به آن اضافه شده است. برای رفع این مشکل در نگارشهای قدیمی، نیاز است از اسکریپت کمکی http://nuget.org/List/Packages/json2 استفاده نمائید. همچنین مرورگر IE را نیز باید وادار ساخت تا بر اساس آخرین موتور پردازشی خود کار کند:
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
2) هنگام فراخوانی مسیر signalr/hubs پیغام 404 (یافت نشد) دریافت میشود.
برای رفع این مشکل ابتدا اطمینان حاصل کنید که تنظیمات مسیریابی تعریف شده در فایل global.asax.cs موجود هستند.
در ادامه اطمینان حاصل نمائید مسیر اسکریپتهای signalr/hubs به درستی تعریف شدهاند:
<script type="text/javascript" src="/signalr/hubs"></script>
MVC: <script type="text/javascript" src="@Url.Content("~/signalr/hubs")"></script> Web forms: <script type="text/javascript" src='<%= ResolveClientUrl("~/signalr/hubs") %>'></script>
<configuration> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> </modules> </system.webServer> </configuration>
3) متدهای سمت کلاینت من فراخوانی نمیشوند.
بهترین راه برای مشاهده ریز جرئیات خطاها، ذکر سطر ذیل در کدهای سمت کلاینت جاوا اسکریپتی برنامه است:
$.connection.hub.logging = true;
4) خطای «Connection must be started before data can be sent» را دریافت میکنم.
همانطور که در قسمت قبل عنوان شد، کلیه فراخوانیهای SignalR از نوع غیرهمزمان هستند. بنابراین باید با استفاده از callback و زمان فراخوانی آنها که عموما پس از برقراری اتصال رخ میدهد، نسبت به انجام امور دلخواه اقدام کرد.
var connection = $.hubConnection('http://localhost:8081/'); proxy = connection.createProxy('collectionhub') connection.start() .done(function () { proxy.invoke('subscribe', 'Product'); $('#messages').append('<li>invoked subscribe</li>'); }) .fail(function () { alert("Could not Connect!"); });
در حالت استفاده بدون پروکسی نیز چنین callbackهایی قابل تعریف هستند:
$.connection.hub.start() .done(function() { myHub.server.SomeFunction(SomeParam) //e.g. a login or init .done(connectionReady); }) .fail(function() { alert("Could not Connect!"); });
5) بعد از 10 اتصال به IIS، برنامه متوقف میشود.
این مورد، محدودیت ذاتی IIS 7 نصب شده بر روی ویندوز 7 است. بهتر است از یک IIS کامل موجود در ویندوزهای سرور استفاده کنید. در این سرورها عدد پیش فرض تنظیم شده 5000 اتصال است که در صورت نیاز با استفاده از دستور زیر قابل تغییر است:
appcmd.exe set config /section:system.webserver/serverRuntime /appConcurrentRequestLimit:100000
%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet.config
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <runtime> <legacyUnhandledExceptionPolicy enabled="false" /> <legacyImpersonationPolicy enabled="true"/> <alwaysFlowImpersonationPolicy enabled="false"/> <SymbolReadingPolicy enabled="1" /> <shadowCopyVerifyByTimestamp enabled="true"/> </runtime> <startup useLegacyV2RuntimeActivationPolicy="true" /> <system.web> <applicationPool maxConcurrentRequestsPerCPU="20000" /> </system.web> </configuration>
%windir%\Microsoft.NET\Framework\v4.0.30319\Config\machine.config
<processModel autoConfig="false" requestQueueLimit="250000" />
رابطه ای از جدول Type به جدول Category.
به کمک Scaffolding یک کنترلر برای کلاس Tap (شیر آب) میسازیم ، به طور عادی در فایل Create.chtml مقدار گروه را به صورت DropDown نمایش میدهد، حال ما نیاز داریم که خودمان DropDown را برای Type ایجاد کنیم و بعد ارتباط اینها را بر قرار کنیم.
تابع اولی Create را این طوری ویرایش میکنیم :
public ActionResult Create() { ViewBag.Type = new SelectList(db.Types, "Id", "Title"); ViewBag.Category = new SelectList(db.Categories, "Id", "Title"); return View(); }
همان طور که مشخص است ، علاوه بر مقادیر Category که خودش ارسال میکند ، ما نیز مقادیر نوعها را به View مورد نظر ارسال میکنیم.
برای نمایش دادن هر دو DropDownList ویو مورد نظر را به این صورت ویرایش میکنیم :
<div> نوع </div> <div> @Html.DropDownList("Type", (SelectList)ViewBag.Type, "-- انتخاب ---", new { id = "rdbTyoe" }) @Html.ValidationMessageFor(model => model.Category) </div> <div> دسته بندی </div> <div> @Html.DropDownList("Category", (SelectList)ViewBag.Category, "-- انتخاب ---", new { id = "rdbCategory"}) @Html.ValidationMessageFor(model => model.Category) </div>
همان طور که مشاهده میکنید ، در اینجا DropDownList مربوط به Type که خودمان سمت سرور ،مقادیر آن را پر کرده بودیم نمایش میدهیم.
خب شاید تا اینجای کار ، ساده بود ولی میرسیم به اصل مطلب و ارتباط بین این دو DropDownList. (قبل از این قسمت حتما نگاهی به ساختار DropDownList یا همان تگ select بیندازید ، اطلاعات جی کوئری شما در این قسمت خیلی کمک حال شما است)
برای این کار ما از jQuery استفادی میکنیم ، کار به این صورت است که هنگامی که مقدار DropDownList اول تغییر کرد :
- ما Id آن را به سرور ارسال میکنیم.
- در آنجا Category هایی که دارای Type با Id مورد نظر هستند را جدا میکنیم
- فیلدهای مورد نیاز یعنی Id و Title را میگیریم
- و بعد به کمک Json مقادیر را بر میگردانیم
- و مقادیر ارسالی از سرور را در optionهای DropDownList دوم (گروهها ) قرار میدهم
public ActionResult SelectCategory(int id) { var categoris = db.Categories.Where(m => m.Type1.Id == id).Select(c => new { c.Id, c.Title }); return Json(categoris, JsonRequestBehavior.AllowGet); }
$('#rdbTyoe').change(function () { jQuery.getJSON('@Url.Action("SelectCategory")', { id: $(this).attr('value') }, function (data) { $('#rdbCategory').empty(); jQuery.each(data, function (i) { var option = $('<option></option>').attr("value", data[i].Id).text(data[i].Title); $("#rdbCategory").append(option); }); }); });
- ابتدا یک تگ option میسازیم
- مقادیر مربوطه شامل Id که باید در attribute مورد نظر value قرار گیرد و متن آن که باید به عنوان text باشد را مقدار دهی میکنیم
- option آماده شده را به DropDownList دومی (Category ) اضافه میکنیم.
$('#rdbCategory').empty();
بعد از انتخاب :
var result = _db.Users.Where(filteringExpression).ToList();
آشنایی با الگوی MVP
پروژههای زیادی را میتوان یافت که اگر سورس کدهای آنها را بررسی کنیم، یک اسپاگتی کد تمام عیار را در آنها میتوان مشاهده نمود. منطق برنامه، قسمت دسترسی به دادهها، کار با رابط کاربر، غیره و غیره همگی درون کدهای یک یا چند فرم خلاصه شدهاند و آنچنان به هم گره خوردهاند که هر گونه تغییر یا اعمال درخواستهای جدید کاربران، سبب از کار افتادن قسمت دیگری از برنامه میشود.
همچنین از کدهای حاصل در یک پروژه، در پروژههای دیگر نیز نمیتوان استفاده کرد (به دلیل همین در هم تنیده بودن قسمتهای مختلف). حداقل نتیجه یک پروژه برای برنامه نویس، باید یک یا چند کلاس باشد که بتوان از آن به عنوان ابزار تسریع انجام پروژههای دیگر استفاده کرد. اما در یک اسپاگتی کد، باید مدتی طولانی را صرف کرد تا بتوان یک متد را از لابلای قسمتهای مرتبط و گره خورده با رابط کاربر استخراج و در پروژهای دیگر استفاده نمود. برای نمونه آیا میتوان این کدها را از یک برنامه ویندوزی استخراج کرد و آنها را در یک برنامه تحت وب استفاده نمود؟
یکی از الگوهایی که شیوهی صحیح این جدا سازی را ترویج میکند، الگوی MVP یا Model-View-Presenter میباشد. خلاصهی این الگو به صورت زیر است:
Model :
من میدانم که چگونه اشیاء برنامه را جهت حصول منطقی خاص، پردازش کنم.
من نمیدانم که چگونه باید اطلاعاتی را به شکلی بصری به کاربر ارائه داد یا چگونه باید به رخدادها یا اعمال صادر شده از طرف کاربر پاسخ داد.
View :
من میدانم که چگونه باید اطلاعاتی را به کاربر به شکلی بصری ارائه داد.
من میدانم که چگونه باید اعمالی مانند data binding و امثال آن را انجام داد.
من نمیدانم که چگونه باید منطق پردازشی موارد ذکر شده را فراهم آورم.
Presenter :
من میدانم که چگونه باید درخواستهای رسیده کاربر به View را دریافت کرده و آنها را به Model انتقال دهم.
من میدانم که چگونه باید اطلاعات را به Model ارسال کرده و سپس نتیجهی پردازش آنها را جهت نمایش در اختیار View قرار دهم.
من نمیدانم که چگونه باید اطلاعاتی را ترسیم کرد (مشکل View است نه من) و نمیدانم که چگونه باید پردازشی را بر روی اطلاعات انجام دهم. (مشکل Model است و اصلا ربطی به اینجانب ندارد!)
یک مثال ساده از پیاده سازی این روش
برنامهای وبی را بنویسید که پس از دریافت شعاع یک دایره از کاربر، مساحت آنرا محاسبه کرده و نمایش دهد.
یک تکست باکس در صفحه قرار خواهیم داد (txtRadius) و یک دکمه جهت دریافت درخواست کاربر برای نمایش نتیجه حاصل در یک برچسب به نام lblResult
الف) پیاده سازی به روش متداول (اسپاگتی کد)
protected void btnGetData_Click(object sender, EventArgs e)
{
lblResult.Text = (Math.PI * double.Parse(txtRadius.Text) * double.Parse(txtRadius.Text)).ToString();
}
اما این مشکلات را هم دارد:
- منطق برنامه (روش محاسبه مساحت دایره) با رابط کاربر گره خورده.
- کدهای برنامه در پروژهی دیگری قابل استفاده نیست. (شما متد یا کلاسی را اینجا با قابلیت استفاده مجدد میتوانید پیدا میکنید؟ آیا یکی از اهداف برنامه نویسی شیءگرا تولید کدهایی با قابلیت استفاده مجدد نبود؟)
- چگونه باید برای آن آزمون واحد نوشت؟
ب) بهبود کد و جدا سازی لایهها از یکدیگر
در روش MVP متداول است که به ازای هر یک از اجزاء ابتدا یک interface نوشته شود و سپس این اینترفیسها پیاده سازی گردد.
پیاده سازی منطق برنامه:
1- ایجاد Model :
یک فایل جدید را به نام CModel.cs به پروژه اضافه کرده و کد زیر را به آن خواهیم افزود:
using System;
namespace MVPTest
{
public interface ICircleModel
{
double GetArea(double radius);
}
public class CModel : ICircleModel
{
public double GetArea(double radius)
{
return Math.PI * radius * radius;
}
}
}
- خبری از textbox و برچسب و غیره نیست. اصلا نمیداند که رابط کاربری وجود دارد یا نه.
- خبری از رخدادهای برنامه و پاسخ دادن به آنها نیست.
- از این کد میتوان مستقیما و بدون هیچ تغییری در برنامههای دیگر هم استفاده کرد.
- اگر باگی در این قسمت وجود دارد، تنها این کلاس است که باید تغییر کند و بلافاصله کل برنامه از این بهبود حاصل شده میتواند بدون هیچگونه تغییری و یا به هم ریختگی استفاده کند.
- نوشتن آزمون واحد برای این کلاس که هیچگونه وابستگی به UI ندارد ساده است.
2- ایجاد View :
فایل دیگری را به نام CView.cs را به همراه اینترفیس زیر به پروژه اضافه میکنیم:
namespace MVPTest
{
public interface IView
{
string RadiusText { get; set; }
string ResultText { get; set; }
}
}
کار View دریافت ابتدایی مقادیر از کاربر توسط RadiusText و نمایش نهایی نتیجه توسط ResultText است البته با یک اما.
View نمیداند که چگونه باید این پردازش صورت گیرد. حتی نمیداند که چگونه باید این مقادیر را به Model جهت پردازش برساند یا چگونه آنها را دریافت کند (به همین جهت از اینترفیس برای تعریف آن استفاده شده).
3- ایجاد Presenter :
در ادامه فایل جدیدی را به نام CPresenter.cs با محتویات زیر به پروژه خواهیم افزود:
namespace MVPTest
{
public class CPresenter
{
IView _view;
public CPresenter(IView view)
{
_view = view;
}
public void CalculateCircleArea()
{
CModel model = new CModel();
_view.ResultText = model.GetArea(double.Parse(_view.RadiusText)).ToString();
}
}
}
کار این کلاس برقراری ارتباط با Model است.
میداند که چگونه اطلاعات را به Model ارسال کند (از طریق _view.RadiusText) و میداند که چگونه نتیجهی پردازش را در اختیار View قرار دهد. (با انتساب آن به _view.ResultText)
نمیداند که چگونه باید این پردازش صورت گیرد (کار مدل است نه او). نمیداند که نتیجهی نهایی را چگونه نمایش دهد (کار View است نه او).
روش معرفی View به این کلاس به constructor dependency injection معروف است.
اکنون کد وب فرم ما که در قسمت (الف) معرفی شده به صورت زیر تغییر میکند:
using System;
namespace MVPTest
{
public partial class _Default : System.Web.UI.Page, IView
{
protected void Page_Load(object sender, EventArgs e)
{
}
public string RadiusText
{
get { return txtRadius.Text; }
set { txtRadius.Text = value; }
}
public string ResultText
{
get { return lblResult.Text; }
set { lblResult.Text = value; }
}
protected void btnGetData_Click(object sender, EventArgs e)
{
CPresenter presenter = new CPresenter(this);
presenter.CalculateCircleArea();
}
}
}
در اینجا یک وهله از Presenter برای برقراری ارتباط با Model ایجاد میشود. همچنین کلاس وب فرم ما اینترفیس View را نیز پیاده سازی خواهد کرد.
در مورد طراحی یک برنامه "فرم ساز" در مطلب قبلی بحث شد ... حدودا سه سال قبل اینکار را برای شرکتی انجام دادم. یک برنامه درخواست خدمات نوشته شده با ASP.NET که مدیران برنامه میتوانستند برای آن فرم طراحی کنند؛ فرم درخواست پرینت، درخواست نصب نرم افزار، درخواست وام، درخواست پیک، درخواست آژانس و ... فرمهایی که تمامی نداشتند! آن زمان برای حل این مساله از فیلدهای XML استفاده کردم.
فیلدهای XML قابلیت نه چندان جدیدی هستند که از SQL Server 2005 به بعد اضافه شدهاند. مهمترین مزیت آنها هم امکان ذخیره سازی اطلاعات هر نوع شیءایی به عنوان یک فیلد XML است. یعنی همان زیرساختی که برای ایجاد یک برنامه فرم ساز نیاز است. ذخیره سازی آن هم آداب خاصی را طلب نمیکند. به ازای هر فیلد مورد نظر کاربر، یک نود جدید به صورت رشته معمولی باید اضافه شود و نهایتا رشته تولیدی باید ذخیره گردد. از دید ما یک رشته است، از دید SQL Server یک نوع XML واقعی؛ به همراه این مزیت مهم که به سادگی میتوان با T-SQL/XQuery/XPath از جزئیات اطلاعات این نوع فیلدها کوئری گرفت و سرعت کار هم واقعا بالا است؛ به علاوه بر خلاف مطلب قبلی در مورد dynamic components ، اینبار نیازی نیست تا به ازای هر یک فیلد درخواستی کاربر، واقعا یک فیلد جدید را به جدول خاصی اضافه کرد. داخل این فیلد XML هر نوع ساختار دلخواهی را میتوان ذخیره کرد. به عبارتی به کمک فیلدهایی از نوع XML میتوان داخل یک سیستم بانک اطلاعاتی رابطهای، schema-less کار کرد (un-typed XML) و همچنین از این اطلاعات ویژه، کوئریهای پیچیده هم گرفت.
تا جایی که اطلاع دارم، چند شرکت دیگر هم در ایران دقیقا از همین ایده فیلدهای XML برای ساخت برنامه فرم ساز استفاده کردهاند ...؛ البته مطلب جدیدی هم نیست؛ برنامههای فرم ساز اوراکل و IBM هم سالها است که از XML برای همین منظور استفاده میکنند. مایکروسافت هم به همین دلیل (شاید بتوان گفت مهمترین دلیل وجودی فیلدهای XML در SQL Server)، پشتیبانی توکاری از XML به عمل آورده است.
یا روش دیگری را که برای طراحی سیستمهای فرم ساز پیشنهاد میکنند استفاده از بانکهای اطلاعاتی مبتنی بر key-value مانند Redis یا RavenDb است؛ یا استفاده از بانکهای اطلاعاتی schema-less واقعی مانند CouchDb.
خوب ... اکنون سؤال این است که NHibernate برای کار با فیلدهای XML چه تمهیداتی را درنظر گرفته است؟
برای این منظور خاصیتی را که قرار است به یک فیلد از نوع XML نگاشت شود، با نوع XDocument مشخص خواهیم ساخت:
using System.Xml.Linq;
namespace TestModel
{
public class DynamicTable
{
public virtual int Id { get; set; }
public virtual XDocument Document { get; set; }
}
}
سپس باید جهت معرفی این نوع ویژه، به صورت صریح از XDocType استفاده کرد؛ یعنی نکتهی اصلی، استفاده از CustomType مرتبط است:
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using NHibernate.Type;
namespace TestModel
{
public class DynamicTableMapping : IAutoMappingOverride<DynamicTable>
{
public void Override(AutoMapping<DynamicTable> mapping)
{
mapping.Id(x => x.Id);
mapping.Map(x => x.Document).CustomType<XDocType>();
}
}
}
البته لازم به ذکر است که دو نوع NHibernate.Type.XDocType و NHibernate.Type.XmlDocType برای کار با فیلدهای XML در NHibernate وجود دارند. XDocType برای کار با نوع System.Xml.Linq.XDocument طراحی شده است و XmlDocType مخصوص نگاشت نوع System.Xml.XmlDocument است.
اکنون اگر به کمک کلاس SchemaExport ، اسکریپت تولید جدول متناظر با اطلاعات فوق را ایجاد کنیم به حاصل زیر خواهیم رسید:
if exists (select * from dbo.sysobjects
where id = object_id(N'[DynamicTable]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [DynamicTable]
create table [DynamicTable] (
Id INT IDENTITY NOT NULL,
Document XML null,
primary key (Id)
)
یک سری اعمال متداول ذخیره سازی اطلاعات و تهیه کوئری نیز در ادامه ذکر شدهاند:
//insert
object savedId = 0;
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var obj = new DynamicTable
{
Document = System.Xml.Linq.XDocument.Parse(
@"<Doc><Node1>Text1</Node1><Node2>Text2</Node2></Doc>"
)
};
savedId = session.Save(obj);
tx.Commit();
}
}
//simple query
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var entity = session.Get<DynamicTable>(savedId);
if (entity != null)
{
Console.WriteLine(entity.Document.Root.ToString());
}
tx.Commit();
}
}
//advanced query
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var list = session.CreateSQLQuery("select [Document].value('(//Doc/Node1)[1]','nvarchar(255)') from [DynamicTable] where id=:p0")
.SetParameter("p0", savedId)
.List();
if (list != null)
{
Console.WriteLine(list[0]);
}
tx.Commit();
}
}
و در پایان بدیهی است که جهت کار با امکانات پیشرفتهتر موجود در SQL Server در مورد فیلدهای XML ( برای نمونه: + و +) باید مثلا رویه ذخیره شده تهیه کرد (یا مستقیما از متد CreateSQLQuery همانند مثال فوق کمک گرفت) و آنرا در NHibernate مورد استفاده قرار داد. البته به این صورت کار شما محدود به SQL Server خواهد شد و باید در نظر داشت که در کل تعداد کمی بانک اطلاعاتی وجود دارند که نوعهای XML را به صورت توکار پشتیبانی میکنند.