support full text searching using Postgresql v10’s new full text searching features.
دوره و کتاب رایگان Angular 5
استفاده از کلاسهای *My وی بی در #C
مطلب فوق در مورد VS2005 صحیح است. در VS2008 به صورت زیر باید عمل کرد:
using Microsoft.VisualBasic.Devices;
Computer mc = new Computer();
bool isAvailable = mc.Network.IsAvailable;
پس از فعالسازی لاگ کردن عملیات در EF Core، مشکلی که در بین انبوهی از اطلاعات لاگ شده رخ میدهد این است که این کوئری خاص دقیقا مرتبط به کدام قسمت برنامه است؟ برای یافتن سادهتر کوئریها و ارتباط دادن آنها با کدهای قسمتهای مختلف برنامه، در EF Core 2.2، قابلیتی تحت عنوان TagWith افزوده شدهاست:
var orders = (from b in context.Orders.TagWith(@"List of top 10 orders") orderby b.Price descending select b).Take(10).ToList();
-- List of top 10 orders SELECT TOP(@__p_1) [b].[Id], [b].[Name], [b].[Price] FROM [Orders] AS [b] ORDER BY [b].[Price] DESC
1) دریافت کتابخانههای لازم
نیاز به کتابخانههای Lucene.NET و همچنین Lucene.Net Contrib است که هر دو مورد را به سادگی توسط NuGet میتوانید دریافت و نصب کنید.
Highlighter استفاده شده، در کتابخانه Lucene.Net Contrib قرار دارد. به همین جهت این مورد را نیز باید جداگانه دریافت کرد.
2) تهیه منبع داده
در اینجا جهت سادگی کار فرض کنید که لیستی از مطالب را به فرمت زیر دراختیار داریم:
public class Post { public int Id { set; get; } public string Title { set; get; } public string Body { set; get; } }
3) تبدیل اطلاعات به فرمت Lucene.NET
همانطور که عنوان شد نیاز است هر رکورد از اطلاعات خود را به شیء Document نگاشت کنیم. نمونهای از اینکار را در متد ذیل مشاهده مینمائید:
static Document MapPostToDocument(Post post) { var postDocument = new Document(); postDocument.Add(new Field("Id", post.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); postDocument.Add(new Field("Title", post.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); postDocument.Add(new Field("Body", post.Body, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); return postDocument; }
کار با ایجاد یک وهله از شیء Document شروع شده و سپس اطلاعات به صوت فیلدهایی به این سند اضافه میشوند.
توضیحات آرگومانهای مختلف سازنده کلاس Field:
- در ابتدا نام فیلد مورد نظر ذکر میگردد.
- سپس مقدار متناظر با آن فیلد، به صورت رشته باید معرفی شود.
- آرگومان سوم آن مشخص میکند که اصل اطلاعات نیز علاوه بر ایندکس شدن باید در فایلهای Lucene ذخیره شوند یا خیر. توسط Field.Store.YES مشخص میکنیم که بله؛ علاقمندیم تا اصل اطلاعات نیز از طریق Lucene قابل بازیابی باشند. این مورد جهت نمایش سریع نتایج جستجوها میتواند مفید باشد. اگر قرار نیست اطلاعاتی را از این فیلد خاص به کاربر نمایش دهید میتوانید از گزینه Field.Store.NO استفاده کنید. همچنین امکان فشرده سازی اطلاعات ذخیره شده با انتخاب گزینه Field.Store.COMPRESS نیز میسر است.
- توسط آرگومان چهارم آن تعیین خواهیم کرد که اطلاعات فیلد مورد نظر ایندکس شوند یا خیر. مقدار Field.Index.NOT_ANALYZED سبب عدم ایندکس شدن فیلد Id میشوند (چون قرار نیست روی id در قسمت جستجوی عمومی سایت، جستجویی صورت گیرد). به کمک مقدار Field.Index.ANALYZED، مقدار معرفی شده، ایندکس خواهد شد.
- پارامتر پنجم آنرا جهت سرعت عمل در نمایان سازی/برجسته کردن و highlighting عبارات جستجو شده در متنهای یافت شده معرفی کردهایم. الگوریتمهای متناظر با این روش در فایلهای Lucene.Net Contrib قرار دارند.
یک نکته
اگر اطلاعاتی را که قرار است ایندکس کنید از نوع HTML میباشند، بهتر است تمام تگهای آنرا پیش از افزودن به لوسین حذف کنید. به این ترتیب نتایج جستجوی دقیقتری را میتوان شاهد بود. برای این منظور میتوان از متد ذیل کمک گرفت:
public static string RemoveHtmlTags(string text) { return string.IsNullOrEmpty(text) ? string.Empty : Regex.Replace(text, @"<(.|\n)*?>", string.Empty); }
4) تهیه Full text index به کمک Lucene.NET
تا اینجا توانستیم اطلاعات خود را به فرمت اسناد لوسین تبدیل کنیم. اکنون ثبت و تبدیل آنها به فایلهای Full text search لوسین به سادگی زیر است:
static readonly Lucene.Net.Util.Version _version = Lucene.Net.Util.Version.LUCENE_29; public static void CreateIdx(IEnumerable<Post> dataList) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version); using (var writer = new IndexWriter(directory, analyzer, create: true, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { foreach (var post in dataList) { writer.AddDocument(MapPostToDocument(post)); } writer.Optimize(); writer.Commit(); writer.Close(); directory.Close(); } }
ذکر version در اینجا ضروری است؛ از این جهت که اگر ایندکسی با فرمت مثلا LUCENE_29 تهیه شود ممکن است با نگارش بعدی این کتابخانه سازگار نباشد و در صورت ارتقاء، نتایج جستجوی انجام شده، کاملا بیربط نمایش داده شوند. با ذکر صریح نگارش، دیگر این اتفاق رخ نخواهد داد.
نکته
StandardAnalyzer توکار لوسین، امکان دریافت لیستی از واژههایی که نباید ایندکس شوند را نیز دارا است. اطلاعات بیشتر در اینجا.
5) به روز رسانی ایندکسها
به کمک سه متد ذیل میتوان اطلاعات ایندکسهای موجود را به روز یا حذف کرد:
public static void UpdateIndex(Post post) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version); using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { var newDoc = MapPostToDocument(post); indexWriter.UpdateDocument(new Term("Id", post.Id.ToString()), newDoc); indexWriter.Commit(); indexWriter.Close(); directory.Close(); } } public static void DeleteIndex(Post post) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version); using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { indexWriter.DeleteDocuments(new Term("Id", post.Id.ToString())); indexWriter.Commit(); indexWriter.Close(); directory.Close(); } } public static void AddIndex(Post post) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); var analyzer = new StandardAnalyzer(_version, getStopWords()); using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED)) { var searchQuery = new TermQuery(new Term("Id", post.Id.ToString())); indexWriter.DeleteDocuments(searchQuery); var newDoc = MapPostToDocument(post); indexWriter.AddDocument(newDoc); indexWriter.Commit(); indexWriter.Close(); directory.Close(); } }
محل فراخوانی این متدها هم میتواند در کنار متدهای به روز رسانی اطلاعات اصلی در بانک اطلاعاتی برنامه باشند. اگر رکوردی اضافه یا حذف شده، ایندکس متناظر نیز باید به روز شود.
6) جستجو در اطلاعات ایندکس شده و نمایش آنها به همراه نمایان/برجسته سازی عبارات جستجو شده
قسمت نهایی کار با لوسین و اطلاعات ایندکسهای تهیه شده، کوئری گرفتن از آنها است. متدهای کامل مورد نیاز را در ذیل مشاهده میکنید:
public static void Query(string term) { var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex")); using (var searcher = new IndexSearcher(directory, readOnly: true)) { var analyzer = new StandardAnalyzer(_version); var parser = new MultiFieldQueryParser(_version, new[] { "Body", "Title" }, analyzer); var query = parseQuery(term, parser); var hits = searcher.Search(query, 10).ScoreDocs; if (hits.Length == 0) { term = searchByPartialWords(term); query = parseQuery(term, parser); hits = searcher.Search(query, 10).ScoreDocs; } FastVectorHighlighter fvHighlighter = new FastVectorHighlighter(true, true); foreach (var scoreDoc in hits) { var doc = searcher.Doc(scoreDoc.doc); string bestfragment = fvHighlighter.GetBestFragment( fvHighlighter.GetFieldQuery(query), searcher.GetIndexReader(), docId: scoreDoc.doc, fieldName: "Body", fragCharSize: 400); var id = doc.Get("Id"); var title = doc.Get("Title"); var score = scoreDoc.score; Console.WriteLine(bestfragment); } searcher.Close(); directory.Close(); } } private static Query parseQuery(string searchQuery, QueryParser parser) { Query query; try { query = parser.Parse(searchQuery.Trim()); } catch (ParseException) { query = parser.Parse(QueryParser.Escape(searchQuery.Trim())); } return query; } private static string searchByPartialWords(string bodyTerm) { bodyTerm = bodyTerm.Replace("*", "").Replace("?", ""); var terms = bodyTerm.Trim().Replace("-", " ").Split(' ') .Where(x => !string.IsNullOrEmpty(x)) .Select(x => x.Trim() + "*"); bodyTerm = string.Join(" ", terms); return bodyTerm; }
اکثر سایتها را که بررسی کنید، جستجوی بر روی یک فیلد را توضیح دادهاند. در اینجا نحوه جستجو بر روی چند فیلد را به کمک MultiFieldQueryParser ملاحظه میکنید.
نکتهی مهمی را هم که در اینجا باید به آن دقت داشت، حساس بودن لوسین به کوچکی و بزرگی نام فیلدهای معرفی شده است و در صورت عدم رعایت این مساله، جستجوی شما نتیجهای را دربر نخواهد داشت.
در ادامه برای parse اطلاعات، از متد کمکی parseQuery استفاده شده است. ممکن است به ParseException بخاطر یک سری حروف خاص بکارگرفته شده در عبارات مورد جستجو برسیم. در اینجا میتوان توسط متد QueryParser.Escape، اطلاعات دریافتی را اصلاح کرد.
سپس نحوه استفاده از کوئری تهیه شده و متد Search را ملاحظه میکنید. در اینجا بهتر است تعداد رکوردهای بازگشت داده شده را تعیین کرد (به کمک آرگومان دوم متد جستجو) تا بیجهت سرعت عملیات را پایین نیاورده و همچنین مصرف حافظه سیستم را نیز بالا نبریم.
ممکن است تعداد hits یا نتایج حاصل صفر باشد؛ بنابراین بد نیست خودمان دست به کار شده و به کمک متد searchByPartialWords، ورودی کاربر را بر اساس زبان جستجوی ویژه لوسین اندکی بهینه کنیم تا بتوان به نتایج بهتری دست یافت.
در آخر نحوه کار با ScoreDocs یافت شده را ملاحظه میکنید. اگر محتوای فیلد را در حین ایندکس سازی ذخیره کرده باشیم، به کمک متد doc.Get میتوان به اطلاعات کامل آن نیز دست یافت.
همچنین نکته دیگری را که در اینجا میتوان ملاحظه کرد استفاده از FastVectorHighlighter میباشد. به کمک این Highlighter ویژه میتوان نتایج جستجو را شبیه به نتایج نمایش داده شده توسط موتور جستجوی گوگل درآورد. برای مثال اگر شخصی ef code first را جستجو کرد، توسط متد GetBestFragment، بهترین جزئی که شامل بیشترین تعداد حروف جستجو شده است، یافت گردیده و همچنین به کمک تگهای B، ضخیم نمایش داده خواهند شد.
همانطور که قول داده بودم، به اصول GRASP میپردازیم.
اصول GRASP-General Responsibility Assignment
Software Principles
این اصول به بررسی نحوه تقسیم وظایف بین کلاسها و مشارکت اشیاء برای به انجام رساندن یک مسئولیت میپردازند. اینکه هر کلاس در ساختار نرم افزار چه وظیفهای دارد و چگونه با کلاسهای دیگر مشارکت میکند تا یک عملکرد به سیستم اضافه گردد. این اصول به چند بخش تقسیم میشوند:
- کنترلر ( Controller )
- ایجاد کننده ( Creator )
- انسجام قوی ( High Cohesion )
- واسطه گری ( Indirection )
- دانای اطلاعات ( Information Expert )
- اتصال ضعیف ( Low Coupling )
- چند ریختی ( Polymorphism )
- حفاظت از تاثیر تغییرات ( Protected Variations )
- مصنوع خالص ( Pure Fabrication )
Controller
این الگو بیان میکند که مسئولیت پاسخ به رویدادهای (Events ) یک سناریوی محدود مانند یک مورد کاربردی ( Use Case ) باید به عهده یک کلاس غیر UI باشد. کنترلر باید کارهایی را که نیاز است در پاسخ رویداد انجام شود، به دیگران بسپرد و نتایج را طبق درخواست رویداد بازگرداند. در اصل، کنترلر دریافت کننده رویداد، راهنمای مسیر پردازش برای پاسخ به رویداد و در نهایت برگرداننده پاسخ به سمت مبداء رویداد است. در زیر مثالی را میبینیم که رویداد اتفاق افتاده توسط واسط گرافیکی به سمت یک handler (که متدی است با ورودیِ فرستنده و آرگمانهای مورد نیاز) در کنترلر فرستاده میشود. این روش event handling، در نمونههای وب فرم و ویندوز فرم دیده میشود. به صورتی خود کلاسهای .Net وظیفه Event Raising از سمت UI با کلیک روی دکمه را انجام میدهد:
public class UserController { protected void OnClickCreate(object sender, EventArgs e) { // call validation services // call create user services } }
در مثال بعد عملیات مربوط به User در یک WebApiController پاسخ داده میشود. در اینجا به جای استفاده از Event Raising برای کنترل کردن رویداد، از فراخوانی یک متد در کنترلر توسط درخواست HttpPost انجام میگیرد. در اینجا نیاز است که در سمت کلاینت درخواستی را ارسال کنیم:
public class UserWebApiController { [HttpPost] public HttpResponseMessage Create(UserViewModel user) { // call validation services // call create user services } }
Creator :
این اصل میگوید شیء ای میتواند یک شیء دیگر را بسازد ( instantiate ) که: (اگر کلاس B بخواهد کلاس A را instantiate کند)
- کلاس B شیء از کلاس A را در خود داشته باشد؛
- یا اطلاعات کافی برای instantiate کردن از A را داشته باشد؛
- یا به صورت نزدیک با A در ارتباط باشد؛
- یا بخواهد شیء A را ذخیره کند.
از آنجایی که این اصل بدیهی به نظر میرسد، با مثال نقض، درک بهتری را نسبت به آن میتوان پیدا کرد:
// سازنده public class B { public static A CreateA(string name, string lastName, string job) { return new A() { Name =name, LastName = lastName, Job = job }; } } // ایجاد شونده public class A { public string Name { get; set; } public string LastName { get; set; } public string Job { get; set; } } public class Context { public void Main() { var name = "Rasoul"; var lastName = "Abbasi"; var job = "Developer"; var obj = B.CreateA(name, lastName, job); } }
و اما چرا این مثال، اصل Creator را نقض میکند. در مثال میبینید که کلاس B، یک شیء از نوع A را در متد Main کلاس Context ایجاد میکند. کلاس B فقط یک متد برای تولید A دارد و در عملیات تولید A هیچ منطق خاصی را پیاده سازی نمیکند.کلاس B شیء ای را از کلاس A ، در خود ندارد، با آن ارتباط نزدیک ندارد و آنرا ذخیره نمیکند. با اینکه کلاس B اطلاعات کافی را برای تولید A از ورودی میگیرد، ولی این کلاس Context است که اطلاعات کافی را ارسال مینماید. اگر در کلاس B منطقی اضافه بر instance گیریِ ساده وجود داشت (مانند بررسی صحت و اعتبار سنجی)، میتوانستیم بگوییم کلاس B از یک مجموعه عملیات instance گیری با خبر است که کلاس Context نباید از آن خبر داشته باشد. لذا اکنون هیچ دلیلی وجود ندارد که وظیفه تولید A را در Context انجام ندهیم و این مسئولیت را به کلاس B منتقل کنیم. این مورد ممکن است در ذهن شما با الگوی Factory تناقض داشته باشد. ولی نکته اصلی در الگو Factory انجام عملیات instance گیری با توجه به منطق برنامه است؛ یعنی وظیفهای که کلاس Context نباید از آن خبر داشته باشد را به کلاس Factory منتقل میکنیم. در غیر اینصورت ایجاد کلاس Factory بی معنا خواهد بود (مگر به عنوان افزایش انعطاف پذیری معماری که بتوان به راحتی نوع پیاده سازی یک واسط را تغییر داد).
High Cohesion :
این اصل اشاره به یکی از اصول اساسی طراحی نرم افزار دارد. انسجام واحدهای نرم افزاری باعث افزایش خوانایی، سهولت اشکال زدایی، قابلیت نگهداری و کاهش تاثیر زنجیرهای تغییرات میشود. طبق این اصل، مسئولیتهای هر واحد باید مرتبط باشد. لذا اجزایی کوچک با مسئولیتهای منسجم و متمرکز بهتر از اجزایی بزرگ با مسئولیتهای پراکنده است. اگر واحدهای سازنده نرم افزار انسجام ضعیفی داشته باشند، درک همکاریها، استفاده مجدد آنها، نگه داری نرم افزار و پاسخ به تغییرات سختتر خواهد شد.
در مثال زیر نقض این اصل را مشاهده میکنیم:
class Controller { public void CreateProduct(string name, int categoryId) { } public void EditProduct(int id, string name) { } public void DeleteProduct(int id) { } public void CreateCategory(string name) { } public void EditCategory(int id, string name) { } public void DeleteCategory(int id) { } }
همانطور که میبینید، کلاس
کنترلر ما، مسئولیت مدیریت Product و Category را بر عهده دارد. بزرگ شدن این کلاس، باعث سختتر شدن
خواندن کد و رفع اشکال میگردد. با جداسازی کنترلر مربوط به Product از Category میتوان انسجام را بالا برد.
Indirection :
این اصل بیان میکند که با تعریف یک واسط بین دو مولفه نرم افزاری میتوان میزان اتصال نرم افزار را کاهش داد. بدین ترتیب وظیفه هماهنگی ارتباط دو مؤلفه، به عهده این واسط خواهد بود و نیازی نیست دادههای ورودی و خروجی دو مؤلفه، هماهنگ باشند. در اینجا واسط، از وابستگی بین دو مؤلفه با پنهان کردن ضوابط هر مؤلفه از دیگری و ایجاد وابستگی ضعیف خود با دو مؤلفه، باعث کاهش اتصال کلی طراحی میگردد.
الگوهای Adapter و Delegate و همچنین نقش کنترلر در الگوی معماری MVC از این اصل پیروی میکنند.
class SenderA { public Mediator mediator { get; } public SenderA() { mediator = new Mediator(); } public void Send(string message, string reciever) { mediator.Send(message, reciever); } } class SenderB { public Mediator mediator { get; } public SenderB() { mediator = new Mediator(); } public void Send(string message) { } } public class RecieverA { public void DoAction(string message) { // انجام عملیات بر اساس پیغام دریافت شده switch (message) { case "create": break; case "delete": break; default: break; } } } public class RecieverB { public void DoAction(string message) { // انجام عملیات بر اساس پیغام دریافت شده switch (message) { case "edit": break; case "rollback": break; default: break; } } } class Mediator { internal void Send(string message, string reciever) { switch (reciever) { case "A": var recieverObjA = new RecieverA(); recieverObjA.DoAction(message); break; case "B": var recieverObjB = new RecieverB(); recieverObjB.DoAction(message); break; default: break; } } } class IndirectionContext { public void Main() { var senderA = new SenderA(); senderA.Send("rollback", "B"); var senderB = new SenderA(); senderB.Send("create", "A"); } }
در این مثال کلاس Mediator به عنوان واسط ارتباطی بین کلاسهای Sender و Receiver قرار گرفته و نقش تحویل پیغام را دارد.
در مقاله بعدی، به بررسی سایر اصول GRASP خواهم پرداخت.
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>
از خروجی GetBuffer استریم نباید استفاده شود:
return File(memoryStream.GetBuffer(), "application/pdf", "Test.pdf");
return File(memoryStream.ToArray(), "application/pdf", "Test.pdf");
| Method | Runtime | Mean | Rank | Allocated | |-------------- |--------- |-----------------:|-----:|----------:| | Min | .NET 6.0 | 656,027.606 ns | 2 | 41 B | | Min | .NET 7.0 | 10,243.608 ns | 1 | - | | | | | | | | Max | .NET 6.0 | 660,551.869 ns | 2 | 41 B | | Max | .NET 7.0 | 10,294.283 ns | 1 | - | | | | | | | | Average | .NET 6.0 | 561,299.193 ns | 2 | 41 B | | Average | .NET 7.0 | 13,850.906 ns | 1 | - | | | | | | | | Sum | .NET 6.0 | 579,482.666 ns | 2 | 41 B | | Sum | .NET 7.0 | 34,728.896 ns | 1 | - | | | | | | | | Count | .NET 6.0 | 569,091.900 ns | 2 | 41 B | | Count | .NET 7.0 | 34,944.719 ns | 1 | - |
- انتخاب User مناسب برای Service های SQL Server | meyex.romanaco.com
- راهنمای مطلق اسکرام به زبان فارسی | blog.irscrum.com
- Unit Testing Practical Lessons & Practices | channel9.msdn.com
- Using lambdas - C++ vs. C# vs. C++/CX vs. C++/CLI | www.codeproject.com
- Windows phone training | www.microsoft.com
- ReSharper 6.1 منتشر شد | blogs.jetbrains.com
- معرفی فریم ورک CSSایی به نام Twitter bootstrap | cyberrabbits.net
- نکاتی که حین تهیه خروجی CSV سازگار با اکسل باید به آنها دقت داشت | stackoverflow.com