At the Build 2016 conference, Microsoft announced the Microsoft Bot Framework, a package of tools for building intelligent bots that could be integrated with a variety of communication platforms. With Microsoft Bot Framework Succinctly, you can jump right into building your own bots with the framework, whether you want to make bots for personal use only, or customer-facing bots for your business. Author Ed Freitas provides clear guidance from setting up a bot project in Visual Studio and writing your first bare-bones bot, to publishing one capable of searching for current flight prices.
- Introduction
- Bot Framework Overview
- Our First Bot
- Publishing Our Bot
- The QPX Express API
- Airfare Alert Bot
به زبان ساده، وقتی شما متغیر جدیدی را ایجاد میکنید، با توجه به نوع (Type) آن متغیر، "مقدار" متغیر شما در Stack یا Heap قرار خواهد گرفت.
Stack
این ساختمان داده، داخل Memory پیاده سازی شده است و تعدادی از متغیرهایی را که ما داخل کد ایجاد میکنیم، در این نوع ساختمان داده از Memory نگهداری میشوند.
شرط قرار گرفتن مقدار یک متغیر داخل Stack این است که متغیر از نوع Value Type باشد. در زبان #C، بطور کلی Struct و Enumها Value Type هستند و بصورت پیشفرض داخل Stack قرار میگیرند. تمامی ValueTypeها در #C، بطور implicit از System.ValueType ارث بری میکنند.
Typeهای زیر، Value Typeهای پیشفرض تعریف شدهی در زبان #C هستند که به آنها Simple Type نیز گفته میشوند:
Represents | Type |
Boolean value | bool |
8-bit unsigned integer | byte |
16-bit Unicode character | char |
128-bit precise decimal values with 28-29 significant digits | decimal |
64-bit double-precision floating point type | double |
32-bit single-precision floating point type | float |
32-bit signed integer type | int |
64-bit signed integer type | long |
8-bit signed integer type | sbyte |
16-bit signed integer type | short |
32-bit unsigned integer type | uint |
64-bit unsigned integer type | ulong |
16-bit unsigned integer type | ushort |
اگر سورس هرکدام از این تایپها مانند Int32 را در ریپازیتوری CoreFX مایکروسافت بررسی کنید، متوجه خواهید شد که تمامی این تایپها از نوع Struct تعریف شدهاند و همانطور که گفتیم، بطور پیشفرض، Structها داخل Stack قرار خواهند گرفت.
طول عمر متغیرهایی که داخل Stack قرار گرفتهاند، منحصر به پایان اجرای یک متد است. بدین معنا که بعد از به پایان رسیدن یک متد، تمامی متغیرهای مورد استفاده در آن متد، از حافظه Stack بطور خودکار حذف خواهند شد. متغیرهایی که داخل Stack قرار میگیرند، نوع و حجم مقادیرشان بر اساس Type ای دارند، در زمان Compile-Time مشخص است.
public static int Add(int number1, int number2) { // number1 is on the stack (function parameter) // number2 is on the stack (function parameter) int sum = number1 + number2; // sum is on the stack (local variable) return sum; }
در زبان #C و در مرحله Compile-Time، کدها به زبان IL (مخفف Intermediate Language) ترجمه میشوند که با نامهای MSIL (مخفف Microsoft Intermediate Language ) و CIL (مخفف Common Intermediate Language ) نیز، این زبان شناخته میشود. ساختار این زبان Stack-based بوده و با شناخت آن، با مفهوم Stack نیز بهتر میتوانیم آشنا شویم.
IL زبانی است که CLR (مخفف Common Language Runtime) را که همان Runtime مایکروسافت است، شناخته و اجرا میکند. قابل ذکر است که Runtime مایکروسافت Open-Source بوده و سورس آن با نام CoreCLR در گذشته از این آدرس و در حال حاضر با نام Runtime از این آدرس قابل دسترسی است.
C:\Program Files (x86)\Microsoft SDKs\Windows\{version}\Bin\ildasm.exe
.method private hidebysig static int32 Add(int32 number1, int32 number2) cil managed { .locals init (int32 V_0, int32 V_1) IL_0001: ldarg.0 // Stack is: [2] IL_0002: ldarg.1 // Stack is: [2, 5] IL_0003: add // Stack is: [7] IL_0004: stloc.0 // Stack is: [] and V_0's value is: 7 IL_0005: ldloc.0 // Stack is: [7] IL_0006: stloc.1 // Stack is: [] and V_1's value is: 7 IL_0009: ldloc.1 // Stack is: [7] IL_000a: ret // Return [7] }
میتوانید لیست دستورات مورد استفاده در CIL را از اینجا ببینید.
در ادامه، خط به خط، خروجی حاصل را بررسی میکنیم:
2- از کلمه کلیدی ldarg (مخفف Load Argument) برای لود کردن آرگومان یا همان پارامتر ورودی متد، داخل Stack استفاده میشود.
• ldarg.0 به معنای لود کردن پارامتر ورودی اول، داخل Stack است و با فراخوانی آن، Stack Frame دارای یک عضو که مقدار آن 2 است، میشود.
3- با استفاده از کلمه کلیدی add، مقادیر موجود در Stack با یکدیگر جمع میشوند و Stack Frame دارای یک عضو که مقدار آن 7 است، میشود.
4- با استفاده از کلمه کلیدی stloc (مخفف Store Local)، آخرین عضو موجود در Stack، داخل متغیر محلی ذکر شده، قرار گرفته و ذخیره میشود.
5- با استفاده از کلمه کلیدی ldloc (مخفف Load Local)، میتوان متغیر محلی ذخیره شده را داخل Stack قرار داد.
6- در نهایت، مقدار 7، داخل متغیر 1 یا همان V_1 با دستور stloc.1 بار دیگر ذخیره، با ldloc.1 لود شده و با استفاده از دستور ret، برگشت داده میشود.
* نکته: اگر کدها را بطور دقیق بررسی کرده باشید، احتمالا فکر کرده اید که چه نیازی به ایجاد یک متغیر اضافی و ریختن نتیجه داخل آن و سپس برگشت دادن نتیجه، در مرحله 6 است؟!
* نکته: احتمالا تا به اینجا دلیل بوجود آمدن StackOverflowException را متوجه شده باشید. فضای Stack محدود است. این فضا در سیستمهای 32 بیت برابر با 1 مگابایت و در سیستمهای 64 بیت برابر با 4 مگابایت است (Reference). اگر حجم متغیرهایی که روی استک Push میشوند، این محدودیت را رد کنند و یا اگر یک متد بطور دائم خودش را صدا بزند (Recursive) و هیچگاه از آن خارج نشود، با خطای StackOverflowException مواجه میشوید.
Heap
.Heap: a group of things placed, thrown, or lying one on another
در مقابل ساختار ترتیبی و منظم Stack، ساختار Heap قرار دارد. Heap قسمتی از حافظه است که ساختار، ترتیب و Layout خاصی ندارد.
وقتی یک متغیر از نوع string را ایجاد میکنیم، مقدار آن داخل Heap و Memory-Address آن متغیر روی Heap، در Stack نگه داری میشود:
public static void SayHi() { string name = "Moien"; }
در این مثال، چون string یک class است، مقدار آن داخل heap ذخیره شده و آدرس آن قسمت (segment) از memory، روی Stack قرار میگیرد:
.method private hidebysig static void SayHi() cil managed { .locals init (string V_0) IL_0001: ldstr "Moien" // Stack is: [memory-address of string in heap] IL_0006: stloc.0 IL_0007: ret }
به متغیرهایی که مقادیرشان داخل Heap ذخیره میشوند، Reference-Type گفته میشود.
* نکته: در این مثال متغیری به نام name ایجاد شده که از آن هیچ استفادهای نشده است. در زمان JIT-Compilation، با توجه با Optimizationهای موجود در سطح CLR، این متد بطور کلی اضافه تشخیص داده شده و از آن صرفنظر خواهد شد.
Boxing and Unboxing
به فرایند تبدیل یک Value-Type مانند int که بصورت پیشفرض داخل Stack ذخیره میشود، به یک object که در داخل Heap ذخیره میشود، Boxing گفته میشود. انجام این عمل باعث allocation بر روی memory میشود که سربار زیادی دارد.
با انجام عمل Boxing، قادر خواهیم بود تا بعنوان مثال یک عدد را بر خلاف روال عادی آن، روی Heap ذخیره کنیم:
public static void Boxing() { const int number = 5; object boxedNumber = number; // implicit boxing using implicit cast object boxedNumber = (object)number; // explicit boxing using direct cast }
در ابتدا عدد 5 روی Stack ذخیره شده بود، اما با Box کردن آن، یعنی قرار دادن مقدار آن داخل یک object، مقدار از Stack به Heap انتقال داده شده و allocation اتفاق خواهد افتاد:
.method public hidebysig static void Boxing() cil managed { .locals init (object V_0) IL_0001: ldc.i4.5 // Stack is: [5] IL_0002: box [System.Runtime]System.Int32 // Stack is: [memory-address of 5 in heap] IL_0007: stloc.0 IL_0008: ret }
به عکس این عمل، یعنی تبدیل یک Reference-Type به یک Value-Type، اصطلاحا Unboxing گفته میشود:
public static void Unboxing() { object boxedNumber = 5; int number = (int)boxedNumber; }
که نتیجه آن، به این صورت خواهد بود:
.method public hidebysig static void Unboxing() cil managed { .locals init (object V_0, int32 V_1) IL_0001: ldc.i4.5 // Stack is: [5] IL_0002: box [System.Runtime]System.Int32 // Stack is: [memory-address of 5 in heap] IL_0007: stloc.0 // Stack is: [] IL_0008: ldloc.0 // Stack is: [memory-address of 5 in heap] IL_0009: unbox.any [System.Runtime]System.Int32 // Stack is: [5] IL_000e: stloc.1 // Stack is: [] IL_000f: ret }
تلاش تیمهای مایکروسافت طی سالهای اخیر، باعث افزایش Performance فوق العاده در NET Core. و ASP.NET Core شده است. یکی از دلایل این Performance، جلوگیری بسیار زیاد از allocation در کدهای خود NET. است، که این امر به واسطه اولویت قرار دادن استفاده از Structها میسر گردیده است.
برخلاف Stack که طول عمر متغیرهای موجود در آن، در انتهای یک متد پایان مییابند، متغیرهای allocate شدهی در Heap به این شکل نبوده و در صورت حذف نکردن آنها بصورت دستی، تا پایان طول عمر اجرای برنامه داخل memory باقی خواهند ماند. اینجا، جاییست که Garbage Collector در NET. وارد عمل میشود.
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
T attachedEntity = set.Find(entity.Id); var attachedEntry = dbContext.Entry(attachedEntity); attachedEntry.CurrentValues.SetValues(entity);
public DbContext() { this.Configuration.ProxyCreationEnabled = false; this.Configuration.LazyLoadingEnabled = false; this.Configuration.AutoDetectChangesEnabled = false; }
dbContext.Entry(entity).State = EntityState.Unchanged ; dbContext.Entry(entity).State = EntityState.Added ; //or Dbset.Add(entity) dbContext.Entry(entity).State = EntityState.Modified ; dbContext.Entry(entity).State = EntityState.Deleted ; // or Dbset.Remove(entity)
dbContext.Entry(entity).State = EntityState.Detached;
// group id=19 Name="General" var customer = new Customer(); customer.Group = group; customer.Name = "mohammadi"; dbContext.Entry(customer).State = EntityState.Added; var customerstate = dbContext.Entry(customer).State;// customerstate=EntityState.Added var groupstate = dbContext.Entry(group);// groupstate=EntityState.Added
// group id=19 Name="General" var customer = new Customer(); customer.Group = group; customer.Name = "mohammadi"; dbContext.Entry(group).State = EntityState.Unchanged; dbContext.Entry(customer).State = EntityState.Added; var customerstate = dbContext.Entry(customer).State;// customerstate=EntityState.Added var groupstate = dbContext.Entry(group);// groupstate=EntityState.Unchanged
// group id=19 Name="General" var customer = new Customer(); customer.Group = group; customer.Name = "mohammadi"; dbContext.Entry(customer).State = EntityState.Unchanged; dbContext.Entry(customer).State = EntityState.Added; var customerstate = dbContext.Entry(customer).State;// customerstate=EntityState.Added var groupstate = dbContext.Entry(group);//// groupstate=EntityState.Unchanged
var customer = dbContext.Customers.FirstOrDefault(); var customerAsNoTracking = dbContext.Customers.AsNoTracking().FirstOrDefault(); var customerstate = dbContext.Entry(customer).State;// customerstate=EntityState.Unchanged var customerstateAsNoTracking = dbContext.Entry(customerAsNoTracking).State;// customerstate=EntityState.Detached
var Entries = dbContext.ChangeTracker.Entries(); var AddedEntries = dbContext.ChangeTracker.Entries().Where(entityEntry => entityEntry.State==EntityState.Added); var ModifiedEntries = dbContext.ChangeTracker.Entries().Where(entityEntry => entityEntry.State==EntityState.Modified); var UnchangedEntries = dbContext.ChangeTracker.Entries().Where(entityEntry => entityEntry.State==EntityState.Unchanged); var DeletedEntries = dbContext.ChangeTracker.Entries().Where(entityEntry => entityEntry.State==EntityState.Deleted); var DetachedEntries = dbContext.ChangeTracker.Entries().Where(entityEntry => entityEntry.State==EntityState.Detached);//* not working !
var CustomersWithGroup = dbContext.Customers.AsNoTracking().Include("Group").ToList(); var CustomerFull = dbContext.Customers.AsNoTracking().Include("Group").Include("Bills").Include("Bills.BillDetails").ToList();
public virtual void AddOrUpdate(T entity) { if (entity.Id == 0) Add(entity); else Update(entity); }
5) اگر بخواهیم موجودیتهای رابطه ای در دیتا گرید ویو (ویندوز فرم) نشون بدیم باید چه کار کنیم؟
گرید ویو در ویندوز فرم قادر به نشون دادن فیلدهای رابطه ای نیست برای حل این مشکل میتونیم یک نوع ستون جدید برای گرید ویو تعریف کنیم و برای نشون دادن فیلدهای رابطه ای از این نوع ستون استفاده کنیم:
public class DataGridViewChildRelationTextBoxCell : DataGridViewTextBoxCell { protected override object GetValue(int rowIndex) { try { var bs = (BindingSource)DataGridView.DataSource; var cl = (DataGridViewChildRelationTextBoxColumn)DataGridView.Columns[ColumnIndex]; return getChildValue(bs.List[rowIndex], cl.DataPropertyName).ToString(); } catch (Exception) { return ""; } } private object getChildValue(object dataSource, string childMember) { int nextPoint = childMember.IndexOf('.'); if (nextPoint == -1) return dataSource.GetType().GetProperty(childMember).GetValue(dataSource, null); string proName = childMember.Substring(0, nextPoint); object newDs = dataSource.GetType().GetProperty(proName).GetValue(dataSource, null); return getChildValue(newDs, childMember.Substring(nextPoint + 1)); } } public class DataGridViewChildRelationTextBoxColumn : DataGridViewTextBoxColumn { public string DataMember { get; set; } public DataGridViewChildRelationTextBoxColumn() { CellTemplate = new DataGridViewChildRelationTextBoxCell(); } }
نحوه استفاده را در ادامه میبینید. این روش توسط ویزارد گریدویو هم قابل استفاده است. موقع Add کردن Column نوع اون رو روی DataGridViewChildRelationTextBoxColumn تنظیم کنید.
GroupNameColumn= new DataGridViewChildRelationTextBoxColumn(); //from your class GroupNameColumn.HeaderText = "گروه مشتری"; GroupNameColumn.DataPropertyName = "Group.Name"; //EF Property: Customer.Group.Name GroupNameColumn.Visible = true; GroupNameColumn.Width = 300; DataGridView.Columns.Add(GroupNameColumn);
خلاصهای را در مورد SQL Server CE قبلا در این سایت مطالعه کردهاید. در ادامه خلاصهای کاربردی را از تنظیمات و نکات مرتبط به کار با SQL-CE به کمک NHibernate ملاحظه خواهید نمود:
1) دریافت SQL-CE 4.0
همین مقدار برای استفاده از SQL-CE 4.0 به کمک NHibernate کفایت میکند و حتی نیازی به نصب سرویس پک یک VS 2010 هم نیست.
2) ابزار سازی جهت ایجاد یک بانک اطلاعاتی خالی SQL-CE
using System;
using System.IO;
namespace NHibernate.Helper.DbSpecific
{
public class SqlCEDbHelper
{
const string engineTypeName = "System.Data.SqlServerCe.SqlCeEngine, System.Data.SqlServerCe";
/// <summary>
/// note: this method will delete existing db and then creates a new one.
/// </summary>
/// <param name="filename"></param>
/// <param name="password"></param>
public static void CreateEmptyDatabaseFile(string filename, string password = "")
{
if (File.Exists(filename))
File.Delete(filename);
var type = System.Type.GetType(engineTypeName);
var localConnectionString = type.GetProperty("LocalConnectionString");
var createDatabase = type.GetMethod("CreateDatabase");
var engine = Activator.CreateInstance(type);
string connectionStr = string.Format("Data Source='{0}';Password={1};Encrypt Database=True", filename, password);
if (string.IsNullOrWhiteSpace(password))
connectionStr = string.Format("Data Source='{0}'", filename);
localConnectionString.SetValue(
obj: engine,
value: connectionStr,
index: null);
createDatabase.Invoke(engine, new object[0]);
}
/// <summary>
/// use this method to compact or encrypt existing db or decrypt it to a new db with all records
/// </summary>
/// <param name="sourceConnection"></param>
/// <param name="destConnection"></param>
public static void CompactDatabase(string sourceConnection, string destConnection)
{
var type = System.Type.GetType(engineTypeName);
var engine = Activator.CreateInstance(type);
var localConnectionString = type.GetProperty("LocalConnectionString");
localConnectionString.SetValue(
obj: engine,
value: sourceConnection,
index: null);
var compactDatabase = type.GetMethod("Compact");
compactDatabase.Invoke(engine, new object[] { destConnection });
}
}
}
کلاس فوق، یک کلاس عمومی است و مرتبط به NHibernate نیست و در همه جا قابل استفاده است.
متد CreateEmptyDatabaseFile یک فایل بانک اطلاعاتی خالی با فرمت مخصوص SQL-CE را برای شما تولید خواهد کرد. به این ترتیب میتوان بدون نیاز به ابزار خاصی، سریعا یک بانک خالی را تولید و شروع به کار کرد. در این متد اگر کلمه عبوری را وارد نکنید، بانک اطلاعاتی رمزنگاری شده نخواهد بود و اگر کلمه عبور را وارد کنید، دیتابیس اولیه به همراه کلیه اعمال انجام شده بر روی آن در طول زمان، با کمک الگوریتم AES به صورت خودکار رمزنگاری خواهند شد. کل کاری را هم که باید انجام دهید ذکر این کلمه عبور در کانکشن استرینگ است.
متد CompactDatabase، یک متد چند منظوره است. اگر بانک اطلاعاتی SQL-CE رمزنگاری نشدهای دارید و میخواهید کل آنرا به همراه تمام اطلاعات درون آن رمزنگاری کنید، میتوانید جهت سهولت کار از این متد استفاده نمائید. آرگومان اول آن به کانکشن استرینگ بانکی موجود و آرگومان دوم به کانکشن استرینگ بانک جدیدی که تولید خواهد شد، اشاره میکند.
همچنین اگر یک بانک اطلاعاتی SQL-CE رمزنگاری شده دارید و میخواهید آنرا به صورت یک بانک اطلاعاتی جدید به همراه تمام رکوردهای آن رمزگشایی کنید، باز هم میتوان از این متد استفاده کرد. البته بدیهی است که کلمه عبور را باید داشته باشید و این کلمه عبور جایی درون فایل بانک اطلاعاتی ذخیره نمیشود. در این حالت در کانکشن استرینگ اول باید کلمه عبور ذکر شود و کانکشن استرینگ دوم نیازی به کلمه عبور نخواهد داشت.
فرمت کلی کانکشن استرینگ SQL-CE هم به شکل زیر است:
Data Source=c:\path\db.sdf;Password=1234;Encrypt Database=True
البته این برای حالتی است که قصد داشته باشید بانک اطلاعاتی مورد استفاده را رمزنگاری کنید یا از یک بانک اطلاعاتی رمزنگاری شده استفاده نمائید. اگر بانک اطلاعاتی شما کلمه عبوری ندارد، ذکر Data Source=c:\path\db.sdf کفایت میکند.
این کلاس هم از این جهت مطرح شد که NHibernate میتواند ساختار بانک اطلاعاتی را بر اساس تعاریف نگاشتها به صورت خودکار تولید و اعمال کند، «اما» بر روی یک بانک اطلاعاتی خالی SQL-CE از قبل تهیه شده (در غیراینصورت خطای The database file cannot be found. Check the path to the database را دریافت خواهید کرد).
نکته:
اگر دقت کرده باشید در این کلاس engineTypeName به صورت رشته ذکر شده است. چرا؟
علت این است که با ذکر engineTypeName به صورت رشته، میتوان از این کلاس در یک کتابخانه عمومی هم استفاده کرد، بدون اینکه مصرف کننده نیازی داشته باشد تا ارجاع مستقیمی را به اسمبلی SQL-CE به برنامه خود اضافه کند. اگر این ارجاع وجود داشت، متدهای یاد شده کار میکنند، در غیراینصورت در گوشهای ساکت و بدون دردسر و بدون نیاز به اسمبلی خاصی برای روز مبادا قرار خواهند گرفت.
3) ابزار مرور اطلاعات بانک اطلاعاتی SQL-CE
با استفاده از management studio خود SQL Server هم میشود با بانکهای اطلاعاتی SQL-CE کار کرد، اما ... اینبار برخلاف نگارش کامل اس کیوال سرور، با یک نسخهی بسیار بدوی، که حتی امکان rename فیلدها را هم ندارد مواجه خواهید شد. به همین جهت به شخصه برنامه SqlCe40Toolbox را ترجیح میدهم و اطمینان داشته باشید که امکانات آن برای کار با SQL-CE از امکانات ارائه شده توسط management studio مایکروسافت، بیشتر و پیشرفتهتر است!
4) تنظیمات NHibernate جهت کار با SQL-CE
الف) پس از نصب SQL-CE ، فایلهای آنرا در مسیر C:\Program Files\Microsoft SQL Server Compact Edition\v4.0 میتوان یافت. درایور ADO.NET آن هم در مسیر C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop قرار دارد. بنابراین در ابتدا نیاز است تا ارجاعی را به اسمبلی System.Data.SqlServerCe.dll به برنامه خود اضافه کنید (نام پوشه desktop آن هم غلط انداز است. از این جهت که نگارش 4 آن، به راحتی در برنامههای ذاتا چند ریسمانی ASP.Net بدون مشکل قابل استفاده است).
نکته مهم: در این حالت NHibernate قادر به یافتن فایل درایور یاد شده نخواهد بود و پیغام خطای «Could not create the driver from NHibernate.Driver.SqlServerCeDriver» را دریافت خواهید کرد. برای رفع آن، اسمبلی System.Data.SqlServerCe.dll را در لیست ارجاعات برنامه یافته و در برگه خواص آن، خاصیت «Copy Local» را true کنید. به این معنا که NHibernate این اسمبلی را در کنار فایل اجرایی برنامه شما جستجو خواهد کرد.
ب) مطلب بعد، تنظیمات ابتدایی NHibernate است جهت شناساندن SQL-CE . مابقی مسایل (نکات mapping، کوئریها و غیره) هیچ تفاوتی با سایر بانکهای اطلاعاتی نخواهد داشت و یکی است. به این معنا که اگر برنامه شما از ویژگیهای خاص بانکهای اطلاعاتی استفاده نکند (مثلا اگر از رویههای ذخیره شده اس کیوال سرور استفاده نکرده باشد)، فقط با تغییر کانکشن استرینگ و معرفی dialect و driver جدید، به سادگی میتواند به یک بانک اطلاعاتی دیگر سوئیچ کند؛ بدون اینکه حتی بخواهید یک سطر از کدهای اصلی برنامه خود را تغییر دهید.
تنها نکته جدید آن این متد است:
private Configuration getConfig()
{
var configure = new Configuration();
configure.SessionFactoryName("BuildIt");
configure.DataBaseIntegration(db =>
{
db.ConnectionProvider<DriverConnectionProvider>();
db.Dialect<MsSqlCe40Dialect>();
db.Driver<SqlServerCeDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionString = ConnectionString;
db.Timeout = 10;
//for testing ...
db.LogFormattedSql = true;
db.LogSqlInConsole = true;
});
return configure;
}
که در آن نحوه تعریف MsSqlCe40Dialect و SqlServerCeDriver مشخص شده است.
نکته حاشیهای!
در این مثال primary key از نوع identity تعریف شده و بدون مشکل کار کرد. همین را اگر با EF تست کنید، این خطا را دریافت میکنید: «Server-generated keys and server-generated values are not supported by SQL Server Compact». بله، EF نمیتواند با primary key از نوع identity حین کار با SQL-CE کار کند. برای رفع آن توصیه شده است که از Guid استفاده کنید!
نکته تکمیلی:
استفاده از Dialect سفارشی در NHibernate
نکته پایانی!
و در پایان باید اشاره کرد که SQL-CE یک بانک اطلاعاتی نوشته شده با دات نت نیست (با CPP نوشته شده است و نصب آن هم نیاز به ران تایم به روز VC را دارد). به این معنا که جهت سیستمهای 64 بیتی و 32 بیتی باید نسخه مناسب آنرا توزیع کنید. یا اینکه Target platform پروژه جاری دات نت خود را بر روی X86 قرار دهید (نه بر روی Any CPU پیش فرض) و در این حالت تنها یک نسخه X86 بانک اطلاعاتی SQL-CE و همچنین برنامه خود را برای تمام سیستمها توزیع کنید.
کار با کلیدهای اصلی و خارجی در EF Code first
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
- مطالعه مسیر آموزشی "Entity Framework Code-First"
- مطالعه مسیر آموزشی "Asp.NET MVC"
- مطالعه مقالات مربوط به "Asp.net Identity"
- مطالعه مسیر آموزشی "اصول طراحی شی گرا SOLID" و دوره "بررسی مفاهیم معکوس سازی وابستگیها و ابزارهای مرتبط با آن"
- انجمن
- ارتباط دوستی
- سیستم ترفیع رتبه
- Themeable
- سیستم Following
- صفحات داینامیک
- سیستم پیام رسانی
- امکان ساخت گروههای شخصی برای انتشار مطالب خود (توسط کاربران) با اعمال دسترسی مختلف
- پیغام خصوصی
- وبلاگ
- نظرسنجی ها
- مدیریت کاربران با دسترسیها داینامیک
- اخبار
- آگهی ها
/// <summary> /// Represents the lable /// </summary> public class Tag { #region Ctor /// <summary> /// Create one instance of <see cref="Tag"/> /// </summary> public Tag() { Id = SequentialGuidGenerator.NewSequentialGuid(); } #endregion #region Properties /// <summary> /// sets or gets Tag's identifier /// </summary> public virtual Guid Id { get; set; } /// <summary> /// sets or gets Tag's name /// </summary> public virtual string Name { get; set; } #endregion #region NavigationProperties /// <summary> /// sets or gets Tag's posts /// </summary> public virtual ICollection<BlogPost> BlogPosts { get; set; } #endregion }
/// <summary> /// Represents the Post's Draft /// </summary> public class BlogDraft { #region Ctor /// <summary> /// create one instance of <see cref="BlogDraft"/> /// </summary> public BlogDraft() { Id = SequentialGuidGenerator.NewSequentialGuid(); } #endregion #region Properties /// <summary> /// gets or sets Id of post's draft /// </summary> public virtual Guid Id { get; set; } /// <summary> /// gets or sets body of post's draft /// </summary> public virtual string Body { get; set; } /// <summary> /// gets or set title of post's draft /// </summary> public virtual string Title { get; set; } /// <summary> /// gets or sets tags of post's draft that seperated using ',' /// </summary> public virtual string TagNames { get; set; } /// <summary> /// gets or sets value indicating whether this draft is ready to publish /// </summary> public virtual bool IsReadyForPublish { get; set; } /// <summary> /// ges ro sets DateTime that this draft added /// </summary> public virtual DateTime CreatedOn { get; set; } /// <summary> /// gets or sets information of User-Agent /// </summary> public virtual string Agent { get; set; } /// <summary> /// gets or sets date that this draft publish as ready /// </summary> public virtual DateTime? ReadyForPublishOn { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets Id of user that he is owner of this draft /// </summary> public virtual long OwnerId { get; set; } /// <summary> /// gets or sets user that he is owner of this draft /// </summary> public virtual User Owner { get; set; } #endregion }
/// <summary> /// Section of Rating /// </summary> public enum RatingSection { News, Announcement, ForumTopic, BlogComment, NewsComment, PollComment, AnnouncementComment, ForumPost, ... } /// <summary> /// Represents Rating Record regard by section type for Rating System /// </summary> public class UserRating { #region Ctor /// <summary> /// Create one instance of <see cref="UserRating"/> /// </summary> public UserRating() { Id = SequentialGuidGenerator.NewSequentialGuid(); } #endregion #region Properties /// <summary> /// gets or sets Id of Rating Record /// </summary> public virtual Guid Id { get; set; } /// <summary> /// gets or sets value of rate /// </summary> public virtual double RatingValue { get; set; } /// <summary> /// gets or sets Section's Id /// </summary> public virtual long SectionId { get; set; } /// <summary> /// gets or sets Section /// </summary> public virtual RatingSection Section { get; set; } #endregion #region Navigation Properties /// <summary> /// gets or sets user that rate one section /// </summary> public virtual User Rater { get; set; } /// <summary> /// gets or sets Rater Id that rate one section /// </summary> public virtual long RaterId { get; set; } #endregion }
/// <summary> /// Represent the rating as ComplexType /// </summary> [ComplexType] public class Rating { /// <summary> /// sets or gets total of rating /// </summary> public virtual double? TotalRating { get; set; } /// <summary> /// sets or gets rater's count /// </summary> public virtual long? RatersCount { get; set; } /// <summary> /// sets or gets average of rating /// </summary> public virtual double? AverageRating { get; set; } }
/// <summary> /// Represents a base class for every content in system /// </summary> public abstract class BaseContent { #region Properties /// <summary> /// get or set identifier of record /// </summary> public virtual long Id { get; set; } /// <summary> /// gets or sets date of publishing content /// </summary> public virtual DateTime PublishedOn { get; set; } /// <summary> /// gets or sets Last Update's Date /// </summary> public virtual DateTime ModifiedOn { get; set; } /// <summary> /// gets or sets the blog pot body /// </summary> public virtual string Body { get; set; } /// <summary> /// gets or sets the content title /// </summary> public virtual string Title { get; set; } /// <summary> /// gets or sets value indicating Custom Slug /// </summary> public virtual string SlugUrl { get; set; } /// <summary> /// gets or sets meta title for seo /// </summary> public virtual string MetaTitle { get; set; } /// <summary> /// gets or sets meta keywords for seo /// </summary> public virtual string MetaKeywords { get; set; } /// <summary> /// gets or sets meta description of the content /// </summary> public virtual string MetaDescription { get; set; } /// <summary> /// gets or sets /// </summary> public virtual string FocusKeyword { get; set; } /// <summary> /// gets or sets value indicating whether the content use CanonicalUrl /// </summary> public virtual bool UseCanonicalUrl { get; set; } /// <summary> /// gets or sets CanonicalUrl That the Post Point to it /// </summary> public virtual string CanonicalUrl { get; set; } /// <summary> /// gets or sets value indicating whether the content user no Follow for Seo /// </summary> public virtual bool UseNoFollow { get; set; } /// <summary> /// gets or sets value indicating whether the content user no Index for Seo /// </summary> public virtual bool UseNoIndex { get; set; } /// <summary> /// gets or sets value indicating whether the content in sitemap /// </summary> public virtual bool IsInSitemap { get; set; } /// <summary> /// gets or sets a value indicating whether the content comments are allowed /// </summary> public virtual bool AllowComments { get; set; } /// <summary> /// gets or sets a value indicating whether the content comments are allowed for anonymouses /// </summary> public virtual bool AllowCommentForAnonymous { get; set; } /// <summary> /// gets or sets viewed count by rss /// </summary> public virtual long ViewCountByRss { get; set; } /// <summary> /// gets or sets viewed count /// </summary> public virtual long ViewCount { get; set; } /// <summary> /// Gets or sets the total number of comments /// <remarks>The same as if we run Item.Comments.where(a=>a.Status==Status.Approved).Count() /// We use this property for performance optimization (no SQL command executed) /// </remarks> /// </summary> public virtual int ApprovedCommentsCount { get; set; } /// <summary> /// Gets or sets the total number of comments /// <remarks>The same as if we run Item.Comments.where(a=>a.Status==Status.UnApproved).Count() /// We use this property for performance optimization (no SQL command executed)</remarks></summary> public virtual int UnApprovedCommentsCount { get; set; } /// <summary> /// gets or sets value indicating whether the content is logical deleted or hidden /// </summary> public virtual bool IsDeleted { get; set; } /// <summary> /// gets or sets rating complex instance /// </summary> public virtual Rating Rating { get; set; } /// <summary> /// gets or sets value indicating whether the content show with rssFeed /// </summary> public virtual bool ShowWithRss { get; set; } /// <summary> /// gets or sets value indicating maximum days count that users can send comment /// </summary> public virtual int DaysCountForSupportComment { get; set; } /// <summary> /// gets or sets information of User-Agent /// </summary> public virtual string Agent { get; set; } /// <summary> /// gets or sets icon name with size 200*200 px for snippet /// </summary> public virtual string SocialSnippetIconName { get; set; } /// <summary> /// gets or sets title for snippet /// </summary> public virtual string SocialSnippetTitle { get; set; } /// <summary> /// gets or sets description for snippet /// </summary> public virtual string SocialSnippetDescription { get; set; } /// <summary> /// gets or sets body of content's comment /// </summary> public virtual byte[] RowVersion { get; set; } /// <summary> /// gets or sets name of tags seperated by comma that assosiated with this content fo increase performance /// </summary> public virtual string TagNames { get; set; } /// <summary> /// gets or sets counter for Content's report /// </summary> public virtual int ReportsCount { get; set; } #endregion #region NavigationProperties /// <summary> /// get or set user that create this record /// </summary> public virtual User Author { get; set; } /// <summary> /// gets or sets Id of user that create this record /// </summary> public virtual long AuthorId { get; set; } /// <summary> /// get or set the tags integrated with content /// </summary> public virtual ICollection<Tag> Tags { get; set; } #endregion }
بخشهای مختلفی که در ابتدای مقاله مطرح شدند، دارای یکسری خصوصیات مشترک میباشند و برای این منظور این خصوصیات را در یک کلاس پایه کپسوله کردهایم. شاید تفکر شما این باشد که میخواهیم ارث بری TPH یا TPT را اعمال کنیم. ولی با توجه به سلیقهی شخصی، در این بخش قصد استفاده از ارث بری را ندارم.
نکتهای که وجود دارد فیلدهای ApprovedCommentsCount UnApprovedCommentsCount و TagNames میباشند که هنگام درج نظر جدید باید تعداد نظرات ذخیره شده را ویرایش کنیم و هنگام ویرایش خود پست یا خبر با ... و یا حتی ویرایش خود تگ یا حذف آن تگ باید TagNames که لیست برچسبهای محتوا را به صورت جدا شده با (,) از هم دیگر میباشد، ویرایش کنیم (جای بحث دارد).
مشخص است که هر یک از مطالب منتشر شده در بخشهای وبلاگ، اخبار، نظرسنجی و آگهیها، یک کابر ایجاد کننده (Author نامیدهایم) خواهد داشت و هر کاربر هم میتواند چندین مطلب را ایجاد کند. لذا رابطهی یک به چند بین تمام این بخشها مذکور و کاربر ایجاد خواهد شد.
مدل LinkBack
/// <summary> /// Represents link for implemention linkback /// </summary> public class LinkBack { #region Ctor /// <summary> /// create one instance of <see cref="LinkBack"/> /// </summary> public LinkBack() { CreatedOn = DateTime.Now; Id = SequentialGuidGenerator.NewSequentialGuid(); } #endregion #region Properties /// <summary> /// gets or sets link's Id /// </summary> public virtual Guid Id { get; set; } /// <summary> /// gets or sets text for show Link /// </summary> public virtual string Title { get; set; } /// <summary> /// gets or sets link's address /// </summary> public virtual string Url { get; set; } /// <summary> /// gets or set value indicating whether this link is internal o external /// </summary> public virtual LinkBackType Type { get; set; } /// <summary> /// gets or sets date that this record is added /// </summary> public virtual DateTime CreatedOn { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets Post that associated /// </summary> public virtual BlogPost Post { get; set; } /// <summary> /// gets or sets id of Post that associated /// </summary> public virtual long PostId { get; set; } #endregion } /// <summary> /// represents Type of ReferrerLinks /// </summary> public enum LinkBackType { /// <summary> /// Internal link /// </summary> Internal, /// <summary> /// External Link /// </summary> External }
مطمئنا در خیلی از وبلاگها مثل سایت جاری متوجه نمایش لینکها ارجاع دهندههای خارجی و داخلی در زیر مطلب شدهاید. کلاس LinkBack هم دقیقا برای این منظور در نظرگرفته شده است که عنوان صفحهای که این پست در آنجا لینک داده شده است، به همراه آدرس آن صفحه، در جدول حاصل از این کلاس ذخیره خواهند شد. نوع داده LinkBackType هم برای متمایز کردن رکوردهای درج شده به عنوان LinkBack در نظر گرفته شده است که بتوان آنها را متمایز کرد، به ارجاعات داخلی و خارجی.
مدل پست ها
/// <summary> /// Represents a blog post /// </summary> public class BlogPost : BaseContent { #region Ctor /// <summary> /// Create one Instance of <see cref="BlogPost"/> /// </summary> public BlogPost() { Rating = new Rating(); PublishedOn = DateTime.Now; } #endregion #region Properties /// <summary> /// gets or sets Status of LinkBack Notifications /// </summary> public virtual LinkBackStatus LinkBackStatus { get; set; } #endregion #region NavigationProperties /// <summary> /// get or set blog post's Reviews /// </summary> public virtual ICollection<BlogComment> Comments { get; set; } /// <summary> /// get or set collection of links that reference to this blog post /// </summary> public virtual ICollection<LinkBack> LinkBacks { get; set; } /// <summary> /// get or set Collection of Users that Contribute on this post /// </summary> public virtual ICollection<User> Contributors { get; set; } #endregion } /// <summary> /// Represents Status for ReferrerLinks /// </summary> public enum LinkBackStatus { [Display(Name ="غیرفعال")] Disable, [Display(Name = "فعال")] Enable, [Display(Name = "لینکها داخلی")] JustInternal, [Display(Name = "لینکها خارجی")] JustExternal }
/// <summary> /// Represents a base class for every comment in system /// </summary> public abstract class BaseComment { #region Properties /// <summary> /// get or set identifier of record /// </summary> public virtual long Id { get; set; } /// <summary> /// gets or sets date of creation /// </summary> public virtual DateTime CreatedOn { get; set; } /// <summary> /// gets or sets displayName of this comment's Creator if he/she is Anonymous /// </summary> public virtual string CreatorDisplayName { get; set; } /// <summary> /// gets or sets body of blog post's comment /// </summary> public virtual string Body { get; set; } /// <summary> /// gets or sets body of blog post's comment /// </summary> public virtual Rating Rating { get; set; } /// <summary> /// gets or sets informations of agent /// </summary> public virtual string UserAgent { get; set; } /// <summary> /// gets or sets siteUrl of Creator if he/she is Anonymous /// </summary> public virtual string SiteUrl { get; set; } /// <summary> /// gets or sets Email of Creator if he/she is anonymous /// </summary> public virtual string Email { get; set; } /// <summary> /// gets or sets status of comment /// </summary> public virtual CommentStatus Status { get; set; } /// <summary> /// gets or sets Ip Address of Creator /// </summary> public virtual string CreatorIp { get; set; } /// <summary> /// gets or sets datetime that is modified /// </summary> public virtual DateTime? ModifiedOn { get; set; } /// <summary> /// gets or sets counter for report this comment /// </summary> public virtual int ReportsCount { get; set; } #endregion #region NavigationProperties /// <summary> /// get or set user that create this record /// </summary> public virtual User Creator { get; set; } /// <summary> /// get or set Id of user that create this record /// </summary> public virtual long? CreatorId { get; set; } #endregion } public enum CommentStatus { /* 0 - approved, 1 - pending, 2 - spam, -1 - trash */ [Display(Name = "تأیید شده")] Approved = 0, [Display(Name = "در انتظار بررسی")] Pending = 1, [Display(Name = "جفنگ")] Spam = 2, [Display(Name = "زباله دان")] Trash = -1 }
/// <summary> /// Represents a blog post's comment /// </summary> public class BlogComment : BaseComment { #region Ctor /// <summary> /// Create One Instance for <see cref="BlogComment"/> /// </summary> public BlogComment() { Rating = new Rating(); CreatedOn = DateTime.Now; } #endregion #region NavigationProperties /// <summary> /// gets or sets BlogComment's identifier for Replying and impelemention self referencing /// </summary> public virtual long? ReplyId { get; set; } /// <summary> /// gets or sets blog's comment for Replying and impelemention self referencing /// </summary> public virtual BlogComment Reply { get; set; } /// <summary> /// get or set collection of blog's comment for Replying and impelemention self referencing /// </summary> public virtual ICollection<BlogComment> Children { get; set; } /// <summary> /// gets or sets post that this comment sent to it /// </summary> public virtual BlogPost Post { get; set; } /// <summary> /// gets or sets post'Id that this comment sent to it /// </summary> public virtual long PostId { get; set; } #endregion }
آینده #C به نقل از طراحان آن
The future of C#
Over the last year we shipped no less than three "point releases" of C# (7.1, 7.2 and 7.3), full of small but useful language features. Mads and Dustin will race you through a tour of these, before turning to some of the big things we have in store for the future: Nullable reference types, recursive patterns, asynchronous streams and more.
AspNetCore.Unobtrusive.Ajax
Unobtrusive Ajax Helpers (like MVC5 Ajax.BeignForm and Ajax.ActionLink) for ASP.NET Core
Features
- Works with Upload file.
- Works with
[AntiForgeryTokenValidation]
. - Has
[AjaxOnly]
attribute to limit actions. - Has
httpRequest.IsAjaxRequest()
extension method to check request is ajax and decide return PartialView or JSON result. - Additional overloads for ease of use.
- Adds necessary JS script automatically when you use a Ajax Helpers and remove JS script when you no longer use it (Optional - is by default).
- Uses CDN url instead of Embedded script (Optional - isn't by default).