ساخت ربات تلگرامی با #C
اما در چت آی دی همش به مشکل میخورم
لینکی که ارسال میکنم :
https://api.telegram.org/bot72988154:AAFRbBFec9guVnt8Hq0STMFNyfnKQrPZNwk/sendMessage?text=message&chat_id=72988154%27
و خطای دریافتی :
{"ok":false,"error_code":403,"description":"Error: Bot can't send messages to bot"}
چت آی دی باید چی باشه؟ وضعیت پشتیبانی از TLS 1.2 | نگارش دات نت |
پشتیبانی نمیشود. راه حلی هم ندارد. | NET 3.5. یا قبل از آن |
پشتیبانی نمیشود. اما اگر نگارش بالاتری نصب است، قطعه کد زیر را استفاده کنید:ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; | NET 4.0. |
پشتیبانی میشود، اما حالت پیشفرض نیست و باید دستی انتخاب شود:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | NET 4.5. |
حالت پیشفرض است و نیاز به تنظیمات خاصی ندارد. | NET 4.6. و یا بالاتر |
آغاز فصل سوم:
در فصل گذشته در مورد بسته بندی و توزیع اسمبلیها، بررسیهایی را انجام دادیم. در این نوع توزیع، فرض ما بر این بود که دسترسی به اسمبلیها، از طریق دایرکتوری خود اپلیکیشن میباشد؛ ولی برای اسمبلیهای عمومی، صحبتی به میان نیاوردیم. در این فصل، ما تمرکز خود را برای توزیع اسمبلیهای عمومی میگذاریم. اسمبلیهای عمومی این قابلیت را میدهند که از طریق چند اپلیکیشن قابل دسترسی باشند. سادهترین و قابل دسترسترین نمونهی این اسمبلیها، اسمبلیهای خود دات نت فریم ورک هستند؛ یا نمونهی دیگر، شرکتهای ثالثی مثل تلریک، که برای استفادهی دیگر برنامه نویسان اسمبلی میسازند.
مشکلی که در توزیع اسمبلیهای عمومی وجود دارد این است که شما باید این اطمینان را کسب کنید که اسمبلی شما، همیشه همان اسمبلی خواهد بود و تغییری در آن رخ نخواهد داد. فرض کنید که شما از یک اسمبلی که توسط شرکت تلریک تهیه شده است استفاده کردهاید و برنامهی شما به خوبی با آن کار میکند. ولی چه اتفاقی میافتد که اگر برنامهی دیگری بعد از شما نصب شود و از همان اسمبلی، منتها از نسخهی دیگر آن استفاده میکند؟ بله برنامهی شما احتمال زیادی دارد که در این حالت به مشکل بر بخورد یا اینکه شخص دیگری یک اسمبلی دیگری همنام اسمبلی و هم نسخهی اسمبلی شما تولید میکند. برای رفع این مشکلات مایکروسافت تمهیداتی را اندیشیده است که ما به آن میگوییم «اسمبلی با نام قوی Strong Name Assembly».
اسمبلیها به دو دسته تقسیم میشوند: اسمبلیهای با نام قوی و اسمبلی
هایی با نام ضعیف ( این مورد در مستندات مایکروسافت نیست و توسط نویسندهی کتاب، این
اصطلاح ایجاد شده است).
در قسمت دوم گفتیم که اسمبلیها از قسمتهایی چون جداول مانیفست، هدرها، متادیتاها و ... تشکیل میشوند. اسمبلیهای نام قوی هم به همین شکل هستند. فقط توسط جفت کلید عمومی و خصوصی محافظت و امضا میشوند که برای ناشر، یک کلید منحصر به فرد را ایجاد میکنند و به ناشر این اطمینان را میدهند که اگر جفت کلیدی را که در دست شما است، به کسی ندهید، هیچ کس دیگری نمیتواند اسمبلی را با مشخصات اسمبلی شما امضاء کند.
حال یک اسمبلی نام قوی، دارای چهار خصوصیت است: نام اسمبلی بدون پسوند، نگارش، فرهنگ (Culture) و کلید عمومی.
از آنجا که خود کلید عمومی بسیار بزرگ میباشد، ما برای استفادهی راحتتر، از توکن کلید عمومی استفاده میکنیم که طول کمتری دارد. توکن کلید عمومی، یک مقدار هش شده است که از کلید عمومی به دست میآید:
"MyTypes, Version=1.0.8123.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" "MyTypes, Version=1.0.8123.0, Culture="enUS", PublicKeyToken=b77a5c561934e089" "MyTypes, Version=2.0.1234.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" "MyTypes, Version=1.0.8123.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
استفاده از فضای نام
System.Reflection.AssemblyName
به شما اجازهی ساخت و دریافت اطلاعاتی را از اسمبلیها میدهد؛ هر نوع اطلاعاتی را که شامل 4 خصوصیت بالا میشود، به شما میدهد.
از آنجا که مطالب مربوطه به امضاء کردن اسمبلی، در سایت جاری موجود میباشند، این مباحث را میتوانید از طریق این مقاله "نام قوی" دنبال کنید تا در این باره گزافه گویی نکرده باشیم .
تصویر زیر توضیح بند بالا را نشان میدهد:
مختصری در مورد GAC
%SystemRoot%\Microsoft.NET\Assembly
هر اسمبلی را که توزیع میکنید، حتما حداقل به یک اسمبلی نام قوی ارجاع خواهد داشت؛ دلیل این گفته هم وجود فضای نام system.object در اسمبلی mscorlib است. برای همین، موقعیکه شما با csc، کامپایل میکنید، از سوئیچ reference استفاده میشود تا ارجاعی را به نام اسمبلی مورد نظر داشته باشد. اگر نام اسمبلی به طور کامل به همراه مسیر ذکر شود که مستقیما از همانجا فراخوانی میشود؛ در غیر این صورت مسیرهای زیر مورد بررسی قرار میگیرند:
- مسیر پوشهی کاری برنامه
- مسیر کامپایلر CSC
- هر مسیری که با سوئیچ lib به کامپایلر معرفی کرده باشید.
- هر مسیری که با متغیرهای Lib Environment مشخص شده باشند.
/reference:System.Drawing.dll
در ضمن اسمبلیهای موجود در مسیر کامپایلر به هیچ عنوان اهمیتی به نوع ماشین نمیدهند؛ چون متادیتاهای اسمبلی آنها اهمیت دارند نه کد IL آنها. کد IL فقط در زمان اجرا، برای ما اهمیت دارد و در زمان کامپایل، همان متادیتا کفایت میکند و در نهایت برنامه موقع اجرا، با توجه به نوع ماشین x86,x36,ARM، اسمبلی مورد نیاز خود را از طریق GAC فراهم میکند که هر یک از اسمبلیهای مخصوص هر ماشین، توسط GAC، در داخل زیر شاخههای مخصوص خود قرار گرفتهاند.
ویژگیهای C# 9.0
- تنظیمات مورد نیاز جهت شروع به کار
- امکان ساده سازی تعاریف اشیاء با Target Typing
- تبدیل شدن زبان به یک زبان اسکریپتی با معرفی ویژگی Top Level Programs
- امکان تعریف سادهتر خواص Immutable با معرفی ویژگی خواص Init-Only
- امکان تعریف سادهتر کلاسهای Immutable با معرفی نوع جدید record
- واژههای کلیدی جدید and، or و not
- امکان داشتن خروجیهای Covariant
- امکان اجرای خودکار کدها در زمان بارگذاری اولیهی یک اسمبلی
- بهبودهای کار با Lambdas
- امکان تعریف حلقهی foreach بر روی هر نوع مجموعهای از دادهها
- آشنایی با تنظیمات جدید کامپایلر C# 9.0
- امکان تعریف ویژگیها بر روی توابع محلی
- غنی سازی کامپایلر C# 9.0 با افزونهها
هنگام ایجاد یک Type جدید باید سعی کنیم دیتای داخلی Type را تا حد ممکن Immutable کنیم. حتی اگر نیاز داریم یک شیء را برگردانیم، بهتر است که یک instance جدید را برگردانیم، نه اینکه همان شیء موجود را تغییر دهیم. نتیحه این کار نهایتا به شفافیت بیشتر و Thread-Safe بودن منجر خواهد شد.
مثال:
public class Rectangle { public int Length { get; set; } public int Height { get; set; } public void Grow(int length, int height) { Length += length; Height += height; } } Rectangle r = new Rectangle(); r.Length = 5; r.Height = 10; r.Grow(10, 10);// r.Length is 15, r.Height is 20, same instance of r
// After public class ImmutableRectangle { int Length { get; } int Height { get; } public ImmutableRectangle(int length, int height) { Length = length; Height = height; } public ImmutableRectangle Grow(int length, int height) => new ImmutableRectangle(Length + length, Height + height); } ImmutableRectangle r = new ImmutableRectangle(5, 10); r = r.Grow(10, 10);// r.Length is 15, r.Height is 20, is a new instance of r
یکی از موارد با اهمیت در سبک کد نویسی تابعی را در مثال زیر ببینید:
public static void Main() { Console.WriteLine(GetSalutation(DateTime.Now.Hour)); } // imparitive, mutates state to produce a result /*public static string GetSalutation(int hour) { string salutation; // placeholder value if (hour < 12) salutation = "Good Morning"; else salutation = "Good Afternoon"; return salutation; // return mutated variable }*/ public static string GetSalutation(int hour) => hour < 12 ? "Good Morning" : "Good Afternoon";
در قسمت قبلی درباره توابع HOF صحبت کردیم. به طور خلاصه توابعی که یک تابع را به عنوان ورودی میگیرند و یک تابع را به عنوان خروجی برمیگردانند. به مثال زیر توجه کنید:
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { int count = 0; foreach (TSource element in source) { checked { if (predicate(element)) { count++; } } } return count; }
ترکیب توابع به عمل پیوند دادن چند تابع ساده، برای ایجاد توابعی پیچیده گفته میشود. دقیقا مانند عملی که در ریاضیات انجام میشود. خروجی هر تابع به عنوان ورودی تابع بعدی مورد استفاده قرار میگیرد و در آخر ما خروجی آخرین فراخوانی را به عنوان نتیجه دریافت میکنیم. ما میتوانیم در #C به روش برنامه نویسی تابعی، توابع را با یکدیگر ترکیب کنیم. به مثال زیر توجه کنید:
public static class Extensions { public static Func<T, TReturn2> Compose<T, TReturn1, TReturn2>(this Func<TReturn1, TReturn2> func1, Func<T, TReturn1> func2) { return x => func1(func2(x)); } } public class Program { public static void Main(string[] args) { Func<int, int> square = (x) => x * x; Func<int, int> negate = x => x * -1; Func<int, string> toString = s => s.ToString(); Func<int, string> squareNegateThenToString = toString.Compose(negate).Compose(square); Console.WriteLine(squareNegateThenToString(2)); } }
یکی از روشهای مهم در سبک برنامه نویسی تابعی، فراخوانی متدها به صورت زنجیرهای و پاس دادن خروجی یک متد به متد بعدی، به عنوان ورودی است. به عنوان مثال کلاس String Builder یک مثال خوب از این نوع پیاده سازی است. کلاس StringBuilder از پترن Fluent Builder استفاده میکند. ما میتوانیم با اکستنشن متد هم به همین نتیجه برسیم. نکته مهم در مورد کلاس StringBuilder این است که این کلاس، شیء string را mutate نمیکند؛ به این معنا که هر متد، تغییری در object ورودی نمیدهد و یک خروجی جدید را بر میگرداند.
string str = new StringBuilder() .Append("Hello ") .Append("World ") .ToString() .TrimEnd() .ToUpper();
public static class Extensions { public static StringBuilder AppendWhen(this StringBuilder sb, string value, bool predicate) => predicate ? sb.Append(value) : sb; } public class Program { public static void Main(string[] args) { // Extends the StringBuilder class to accept a predicate string htmlButton = new StringBuilder().Append("<button").AppendWhen(" disabled", false).Append(">Click me</button>").ToString(); } }
نوعهای اضافی درست نکنید ، به جای آن از کلمهی کلیدی yield استفاده کنید!
گاهی ما نیاز داریم لیستی از آیتمها را به عنوان خروجی یک متد برگردانیم. اولین انتخاب معمولا ایجاد یک شیء از جنس List یا به طور کلیتر Collection و سپس استفاده از آن به عنوان نوع خروجی است:
public static void Main() { int[] a = { 1, 2, 3, 4, 5 }; foreach (int n in GreaterThan(a, 3)) { Console.WriteLine(n); } } /*public static IEnumerable<int> GreaterThan(int[] arr, int gt) { List<int> temp = new List<int>(); foreach (int n in arr) { if (n > gt) temp.Add(n); } return temp; }*/ public static IEnumerable<int> GreaterThan(int[] arr, int gt) { foreach (int n in arr) { if (n > gt) yield return n; } }
در قسمت قبلی به طور کلی درباره برنامه نویسی Imperative صحبت کردیم. در مثال زیر یک نمونه از تبدیل یک متد که با استایل Imperative نوشته شده به declarative را میبینید. شما میتوانید ببینید که چقدر کوتاهتر و خواناتر شده:
List<int> collection = new List<int> { 1, 2, 3, 4, 5 }; // Imparative style of programming is verbose List<int> results = new List<int>(); foreach(var num in collection) { if (num % 2 != 0) results.Add(num); } // Declarative is terse and beautiful var results = collection.Where(num => num % 2 != 0);
Immutable Collection
در مورد اهمیت immutable قبلا صحبت کردیم؛ Immutable Collection ها، کالکشنهایی هستند که به جز زمانیکه ایجاد میشنود، اعضای آنها نمیتوانند تغییر کنند. زمانیکه یک آیتم به آن اضافه یا کم شود، یک لیست جدید، برگردانده خواهد شد. شما میتوانید انواع این کالکشنها را در این لینک ببینید.
به نظر میرسد که ایجاد یک کالکشن جدید میتواند سربار اضافی بر روی استفاده از حافظه داشته باشد، اما همیشه الزاما به این صورت نیست. به طور مثال اگر شما f(x)=y را داشته باشید، مقادیر x و y به احتمال زیاد یکسان هستند. در این صورت متغیر x و y، حافظه را به صورت مشترک استفاده میکنند. به این دلیل که هیچ کدام از آنها Mutable نیستند. اگر به دنبال جزییات بیشتری هستید این مقاله به صورت خیلی جزییتر در مورد نحوه پیاده سازی این نوع کالکشنها صحبت میکند. اریک لپرت یک سری مقاله در مورد Immutable ها در #C دارد که میتوانید آن هار در اینجا پیدا کنید.
Thread-Safe Collections
Collection | توضیحات |
ConcurrentDictionary | پیاده سازی thread safe از دیکشنری key-value |
ConcurrentQueue | پیاده سازی thread safe از صف (اولین ورودی ، اولین خروجی) |
ConcurrentStack | پیاده سازی thread safe از پشته (آخرین ورودی ، اولین خروجی) |
ConcurrentBag | پیاده سازی thread safe از لیست نامرتب |
این کلاسها در واقع همه مشکلات ما را حل نخواهند کرد؛ اما بهتر است که در ذهن خود داشته باشیم که بتوانیم به موقع و در جای درست از آنها استفاده کنیم.
در این قسمت از مقاله سعی شد با روشهای خیلی ساده، با مفاهیم اولیه برنامه نویسی تابعی درگیر شویم. در ادامه مثالهای بیشتری از الگوهایی که میتوانند به ما کمک کنند، خواهیم داشت.
پشتیبانی از دات نت فریم ورکهای سه و سه و نیم دقیقا در تاریخ "سهشنبه 23 فروردین 1390" پایان خواهد یافت. توصیه مایکروسافت ارتقاء برنامههای موجود به دات نت فریم ورک سه و نیم، سرویس پک یک و یا دات نت 4 است. طول عمر دات نت فریم ورک سه و نیم، سرویس پک یک به صورت Component محاسبه میشود؛ به این معنا که اگر دقت کرده باشید این نگارش به صورت پیش فرض جزئی از ویندوز 7 یا ویندوز سرور 2008 است. بنابراین تا زمانیکه این دو سیستم عامل توسط مایکروسافت پشتیبانی میشوند، اجزای آنها نیز همانند دات نت فریم ورک سه و نیم، سرویس پک یک، پشتیبانی خواهند شد.
جدول کامل طول عمر نگارشهای مختلف دات نت فریم ورک را در اینجا میتوانید ملاحظه کنید.
مایکروسافت اخیرا علاوه بر تکمیل ORM های خود مانند LINQ to SQL و همچنین Entity framework ، لایه دیگری را نیز بر روی ADO.NET جهت کسانی که به هر دلیلی دوست ندارند با ORMs کار کنند و از نوشتن کوئریهای مستقیم SQL لذت میبرند، ارائه داده است که Microsoft.Data library نام دارد و از قابلیتهای جدید زبان سی شارپ مانند واژه کلیدی dynamic استفاده میکند.
در ادامه قصد داریم جهت بررسی تواناییهای این کتابخانه از بانک اطلاعاتی معروف Northwind استفاده کنیم. این بانک اطلاعاتی را از اینجا میتوانید دریافت کنید.
مراحل استفاده از Microsoft.Data library:
الف) این اسمبلی جدید به همراه پروژه WebMatrix ارائه شده است. بنابراین ابتدا باید آنرا دریافت کنید: +
لازم به ذکر است که این کتابخانه اخیرا به WebMatrix.Data.dll تغییر نام یافته است. (اگر وب را جستجو کنید فقط به Microsoft.Data.dll اشاره شده است)
ب) پس از نصب، ارجاعی را از اسمبلی WebMatrix.Data.dll به پروژه خود اضافه نمائید. این اسمبلی در صفحهی Add References ظاهر نمیشود و باید کامپیوتر خود را برای یافتن آن جستجو کنید که عموما در آدرس زیر قرار دارد:
C:\Program Files\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\WebMatrix.Data.dll
ج) اتصال به بانک اطلاعاتی
پیش فرض اصلی این کتابخانه بانک اطلاعاتی SQL Server CE است. بنابراین اگر قصد استفاده از پروایدرهای دیگری را دارید باید به صورت صریح آنرا ذکر نمائید:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="systemData:defaultProvider" value="System.Data.SqlClient" />
</appSettings>
<connectionStrings>
<add name="Northwind"
connectionString="Data Source=(local);Integrated Security = true;Initial Catalog=Northwind"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
این تعاریف در فایل web.config و یا app.config برنامه وب یا ویندوزی شما قرار خواهند گرفت.
د) نحوهی تعریف کوئریها و دریافت اطلاعات
using System;
using WebMatrix.Data;
namespace TestMicrosoftDataLibrary
{
class Program
{
static void Main(string[] args)
{
getProducts();
Console.Read();
Console.WriteLine("Press a key ...");
}
private static void getProducts()
{
using (var db = Database.Open("Northwind"))
{
foreach (var product in db.Query("select * from products where UnitsInStock < @0", 20))
{
Console.WriteLine(product.ProductName + " " + product.UnitsInStock);
}
}
}
}
}
همچنین نکتهی مهم دیگر آن نحوهی تعریف پارامتر در آن است (همان 0@ ذکر شده) که نسبت به ADO.NET کلاسیک به شدت ساده شدهاست (و نوشتن کوئریهای امن و SQL Injection safe را تسهیل میکند).
در اینجا Database.Open کار گشودن name ذکر شده در فایل کانفیگ برنامه را انجام خواهد داد. اگر بخواهید این تعاریف را در کدهای خود قرار دهید (که اصلا توصیه نمیشود)، میتوان از متد Database.OpenConnectionString استفاده نمود.
یا مثالی دیگر: استفاده از LINQ حین تعریف کوئریها:
private static void getCustomerFax()
{
using (var db = Database.Open("Northwind"))
{
var product = db.Query("SELECT * FROM [Customers] WHERE City=@0", "Paris").FirstOrDefault();
if (product != null)
Console.WriteLine(product.Fax);
else
Console.WriteLine("not found.");
}
}
ه) اجرای کوئریها بر روی بانک اطلاعاتی
private static void ExecQuery()
{
using (var db = Database.Open("Northwind"))
{
int affectedRecords = db.Execute("UPDATE [Customers] SET fax = fax + '*' WHERE City = @0", "Paris");
Console.WriteLine("Affected records: {0}", affectedRecords);
}
}
با استفاده از متد Execute آن میتوان کوئریهای دلخواه خود را بر روی بانک اطلاعاتی اجرا کرد. خروجی آن تعداد رکورد تغییر کرده است.
و) نحوهی اجرای یک رویه ذخیره شده و نمایش خروجی آن
private static void ExecSPShowResult()
{
using (var db = Database.Open("Northwind"))
{
var customer = db.Query("exec CustOrderHist @0", "ALFKI").FirstOrDefault();
if (customer != null)
{
Console.WriteLine(customer.ProductName);
}
}
}
ز) اجرای یک تابع و نمایش خروجی آن
private static void useFuncs()
{
using (var db = Database.Open("Northwind"))
{
var query = db.Query("SELECT dbo.FN_GET_CATEGORY_TREE(@0) as Rec1", 3);
foreach(var tree in query)
{
Console.WriteLine(tree.Rec1);
}
}
}
سؤال : آیا WebMatrix.Data.dll بهتر است یا استفاده از ORMs ؟
در اینجا چون از قابلیتهای داینامیک زبان سی شارپ 4 استفاده میشود، کامپایلر درکی از اشیاء خروجی و خواص آنها برای مثال tree.Rec1 (در مثال آخر) ندارد و تنها در زمان اجرا است که مشخص میشود آیا یک چنین ستونی در خروجی کوئری وجود داشته است یا خیر. اما حین استفاده از ORMs این طور نیست و Schema یک بانک اطلاعاتی پیشتر از طریق نگاشتهای جداول به اشیاء دات نتی، به کامپایلر معرفی میشوند و همین امر سبب میشود تا اگر ساختار بانک اطلاعاتی تغییر کرد، پیش از اجرای برنامه و در حین کامپایل بتوان مشکلات را دقیقا مشاهده نمود و سپس برطرف کرد.
ولی در کل استفاده از این کتابخانه نسبت به ADO.NET کلاسیک بسیار سادهتر بوده، میتوان اشیاء و خواص آنها را مطابق نام جداول و فیلدهای بانک اطلاعاتی تعریف کرد و همچنین تعریف پارامترها و برنامه نویسی امن نیز در آن بسیار سادهتر شده است.
برای مطالعه بیشتر:
Introduction to Microsoft.Data.dll
WCF Routing Service چیست؟
Routing Service به عنوان سرویس مسیریابی WCF در دات نت 4 معرفی شد. به وسیله Routing Service میتوان Endpoint Configuration مقصدهای مختلف را با هم تجمیع کرد و در نتیجه تعداد تنظیمات برای Endpoint در سمت کلاینت کاهش پیدا میکند به طوری که کلاینت فقط با یک مقصد در ارتباط است. مقصد کلاینت همان Routing Service میباشد که در این سرور درخواستهای رسیده از کلاینتها با توجه به فیلتر انجام شده به مقصد اصلی ارسال خواهند شد.
با استفاده از Routing Service معماری سیستم به صورت تغییر پیدا میکند:
اهداف:
موارد زیر اهداف و مزایای استفاده از Routing Service است:
»Service versioning
»Content-based routing scenario
»Service partitioning
»Protocol bridging
هر کدام از موارد بالا در طی پستهای جداگانه شرح داده خواهند شد.
بررسی یک مثال:
دو Contract به صورت زیر تعریف میکنیم:
[ServiceContract] public interface ICalculatorV1 { [OperationContract] int Add(int a, int b); } [ServiceContract] public interface ICalculatorV2 { [OperationContract] int Sub(int a, int b); }
public class CalculatorV1 : ICalculatorV1 { public int Add(int a, int b) { return a + b; } } public class CalculatorV2 : ICalculatorV2 { public int Sub(int a, int b) { return a - b; } }
system.serviceModel> <services> <service name="WCFRoutingSample.CalculatorV1"> <host> <baseAddresses> <add baseAddress = "http://localhost:8732/CalculatorServiceV1/" /> </baseAddresses> </host> <endpoint address ="" binding="basicHttpBinding" contract="WCFRoutingSample.ICalculatorV1"> <identity> <dns value="localhost"/> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> <service name="WCFRoutingSample.CalculatorV2"> <host> <baseAddresses> <add baseAddress = "http://localhost:8733/CalculatorServiceV2/" /> </baseAddresses> </host> <endpoint address ="" binding="basicHttpBinding" contract="WCFRoutingSample.ICalculatorV2"> <identity> <dns value="localhost"/> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpGetEnabled="True"/> <serviceDebug includeExceptionDetailInFaults="False" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
حال باید RoutingService را به صورت زیر هاست نماییم:
class Program { static void Main(string[] args) { var host = new ServiceHost(typeof(RoutingService)); host.Open(); Console.WriteLine("Server is running."); Console.ReadLine(); host.Close(); } }
مهمترین بخش تنظیمات مربوط به Routing Service است:
<system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="routingBehv"> <routing routeOnHeadersOnly="false" filterTableName="filters"/> <serviceDebug includeExceptionDetailInFaults="true"/> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> <routing> <filters> <filter name="CalV1ServiceFilter" filterType="EndpointName" filterData="Calv1Service"/> <filter name="CalV2ServiceFilter" filterType="EndpointName" filterData="Calv2Service"/> </filters> <filterTables> <filterTable name="filters"> <add filterName="CalV1ServiceFilter" endpointName="Calv1Service" /> <add filterName="CalV2ServiceFilter" endpointName="Calv2Service"/> </filterTable> </filterTables> </routing> <services> <!-- Routing service with endpoint definition --> <service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="routingBehv"> <endpoint address="/Calv1" binding="basicHttpBinding" contract="System.ServiceModel.Routing.IRequestReplyRouter" name="Calv1Service"/> <endpoint address="/Calv2" binding="basicHttpBinding" contract="System.ServiceModel.Routing.IRequestReplyRouter" name="Calv2Service"/> <host> <baseAddresses> <add baseAddress="http://localhost:9000/CalculatorService"/> </baseAddresses> </host> </service> </services> <client> <endpoint address="http://localhost:8732/CalculatorServiceV1" binding="basicHttpBinding" contract="*" name="Calv1Service"/> <endpoint address="http://localhost:8733/CalculatorServiceV2" binding="basicHttpBinding" contract="*" name="Calv2Service"/> </client> </system.serviceModel>
در حال حاضر این کتابخانه از مفاهیم زیر پشتیبانی میکند:
- تبدیل کلاس به جدول با پشتیبانی از خصوصیت Table
- تبدیل پراپرتیها به ستون با پشتیبانی از خصوصیت هایی چون Column,Key,MaxLength,Required,Notmapped,DatabaseGenerated,Index
- پشتیبانی از primarykey و کلیدهای ترکیبی
- کلید خارجی و روابط یک به چند و پشتیبانی از cascade on delete
- فیلد غیر نال
برای شروع ابتدا کتابخانه مورد نظر را از Nuget با دستور زیر دریافت کنید:
Install-Package SQLite.CodeFirst
solution من شامل سه پروژه است یکی برای مدلها که شامل کلاسهای زیر برای تهیه یک دفترچه تلفن ساده است:
Person
public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection<PhoneBook> Numbers { get; set; } }
PhoneBook
public class PhoneBook { public int Id { get; set; } public string Field{ get; set; } public string Number { get; set; } public virtual Person Person { get; set; } }
پروژه بعدی به نام سرویس که جهت پیاده سازی کلاسهای EF است و دیگری هم یک پروژهی WPF جهت تست برنامه.
در پروژهی سرویس ما یک کلاس به نام Context داریم که مفاهیم مربوط به پیاده سازی Context در آن انجام شده است:
public class Context:DbContext { public Context():base("constr") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); var initializer = new InitialDb(modelBuilder); Database.SetInitializer(initializer); } public DbSet<PhoneBook> PhoneBook { get; set; } public DbSet<Person> Persons { get; set; } }
اول اینکه در سطر اول متد بازنویسی شده onModelCreating، قرارداد مربوط به نامگذاری جداول را حذف میکنیم چرا که در صورت نبودن این خط، اسامی که کلاس sqllite برای آن در نظر خواهد گرفت با اسامی که برای انجام عملیات CURD استفاده میشوند متفاوت خواهد بود. برای مثال برای Person جدولی به اسم People خواهد ساخت ولی برای درج، آن را در جدول Person انجام میدهد که به خاطر نبودن جدول با خطای چنین جدولی موجود نیست روبرو میشویم.
نکتهی دوم اینکه در همین کلاس Context ما یک پیاده سازی جدید بر روی کلاس InitialDb داشته ایم که در زیر نمونه کد آن را میبینید:
public class InitialDb:SQLite.CodeFirst.SqliteCreateDatabaseIfNotExists<Context> { public InitialDb(DbModelBuilder modelBuilder) : base(modelBuilder) { } protected override void Seed(Context context) { var person = new Person() { FirstName = "ali", LastName = "yeganeh", Numbers = new List<PhoneBook>() { new PhoneBook() { Field = "Work", Number = "031551234" }, new PhoneBook() { Field = "Mobile", Number = "09123456789" }, new PhoneBook() { Field = "Home", Number = "031554321" } } }; context.Persons.Add(person); base.Seed(context); } }
سپس در پروژهی اصلی WPF در فایل AppConfig رشته اتصالی مورد نظر را وارد نمایید:
<connectionStrings> <add name="constr" connectionString="data source=.\phonebook.sqlite;foreign keys=true" providerName="System.Data.SQLite" /> </connectionStrings>
خطا:
The ADO.NET provider with invariant name 'System.Data.SQLite' is either not registered in the machine or application config file, or could not be loaded
قسمتی از فایل app.config:
<entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> <provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" /> </providers> </entityFramework> <system.data> <DbProviderFactories> <remove invariant="System.Data.SQLite.EF6" /> <remove invariant="System.Data.SQLite" /> <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" /> </DbProviderFactories> </system.data>
کد Load پروژه WPF:
public MainWindow() { InitializeComponent(); var context=new Context(); var list= context.Persons.ToList(); var s = ""; foreach (var person in list) { s += person.FirstName + " " + person.LastName + " has these numbers:" + Environment.NewLine; foreach (var number in person.Numbers) { s += number.Field + " : " + number.Number + Environment.NewLine; } s += Environment.NewLine; } MessageBox.Show(s); }
دانلود مثال