- Update extension to handle upcoming breaking change to launch.json configurations in VS Code 1.28.
- Fixed launch.json
envFile
option on Windows - Fixed a bug where OmniSharp flame was red inspite of OmniSharp loading the projects without any errors.
- Fixed a problem with tracking virtual documents from other providers
Fixed bug where LangVersion property was not read correctly from project file, blocking C# 7.1 development.
Fixed long-standing problem with renaming files.
Added csharp.referencesCodeLens.enabled and csharp.testsCodeLens.enabled options to allow disabling/enabling for the 'references' and 'run/debug test' code lenses independently.
EF Code First #7
من یک پروژه را با CodeFirst شروع کردم اما یه جایی اشتباه کردم فکر کنم اشتباهم توی یکی از Mappingها باشه. اگه لطف کنید ببینید مشکل چیه.بدون استفاده از Mapping مشکلی نیست و دیتا بیس با روابطی که میخوام ایجاد میشه اما وقتی از Mapping استفاده میکنم با این خطا مواجه میشم:
public class Driver { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string NationalCode { get; set; } public string CellPhone { get; set; } public string LicenseNumber { get; set; } public bool IsDriverAssistance { get; set; } [InverseProperty("Driver")] public virtual ICollection<Transference> Transferences { get; set; } [InverseProperty("DriverAssistance")] public virtual ICollection<Transference> TransferencesForAssistance { get; set; } [InverseProperty("Driver")] public virtual ICollection<Tanker> Tankers { get; set; } [InverseProperty("DriverAssistance")] public virtual ICollection<Tanker> TankersForAssistance { get; set; } }
public class Transference { public string Id { get; set; } public DateTime Date { get; set; } public Int16 Lytrazh { get; set; } public bool IsEMS { get; set; } public DateTime LoadingDate { get; set; } public DateTime DeliveryDate { get; set; } [InverseProperty("Transferences")] public virtual Driver Driver { get; set; } [InverseProperty("TransferencesForAssistance")] public virtual Driver DriverAssistance { get; set; } public virtual TypeOfTanker TypesOfTanker { get; set; } public virtual Tanker Tanker { get; set; } public virtual Consumer Consumer { get; set; } }
public class TransferenceConfig : EntityTypeConfiguration<Transference> { public TransferenceConfig() { // one-to-many this.HasRequired(x => x.Consumer) .WithMany(x => x.Transferences); // one-to-many this.HasRequired(x => x.TypesOfTanker) .WithMany(x => x.Transferences); // one-to-many this.HasRequired(x => x.Tanker) .WithMany(x => x.Transferences); // one-to-many this.HasRequired(x => x.Driver) .WithMany(x => x.Transferences); // one-to-many this.HasRequired(x => x.DriverAssistance) .WithMany(x => x.Transferences); } }
ابتدا میخواهیم یک الگو یا Template را درست کنیم و بعدها از روی آن، نامهی جدیدی
را ایجاد کنیم و فیلدهایش را پرکنیم. برای اینکار یک سند جدید را در Word
ایجاد و به سربرگ Mailings مراجعه میکنیم. سپس دکمهی Select Recipients
را بزنید. در ادامه از منوی باز شده، Type a NewList را بزنید. با اینکار پنجرهای باز
میشود. در اینجا دکمهی Customize Columns را بزنید. این پنجره شامل فیلدهایی میشود که
میتوانید از آن استفاده کنید و بر روی سند قرار دهید و داخل برنامه با پیدا کردن
این فیلدها میتوانید بجای آنها، مقدار مورد نظرتان را پاس دهید. حالا شما نیاز
دارید تا از طریق دکمهی Add، تمامی فیلدهای لازم یک نامه را بسازید. پس از این کار، در هر دو پنجره ،
دکمهی OK را بزنید. بدین صورت یک پنجرهی ذخیره برای شما باز میشود تا این فیلدهایی را
که ایجاد کردید، به عنوان یک دیتابیس کوچک ذخیره شود که تمامی فیلدها را
دارا میباشد و هر موقع که خواستید دوباره میتوانید از همین فیلدها استفاده
کنید.
حالا میرسیم به قرار دادن این فیلدها داخل سند. با ذخیره
کردن فیلدها، تمامی گزینههای سربرگ Mailings فعال میشود. شما برای اینکه
فیلدی را بر روی سند قرار دهید، روی Insert Merge Field کلیک و متناسب با نیازتان،
فیلدها را قرار دهید و الگو را طراحی کنید. یک نمونه:
حالا فایل را با پسوند DOT. ذخیره کنید. در ادامه این فایل را در دیتابیس، به این روش ذخیره کنید:
String FilePath = "Template Path" // Converting File to ByteArray byte[] FileBuffer = System.IO.File.ReadAllBytes(FilePath); // Now you can insert this file buffer to DB
الان، الگوی ما آمادهاست و میتوانیم از طریق برنامه، به این الگو دسترسی داشته باشیم و به آن پارامتر ارسال کنیم.
روش ارسال پارامترها به الگوهای Word
حالا
فرضا شما یک فرم دارید که از کاربر، اطلاعاتی را دریافت میکند و میخواهید همین
اطلاعات را به Word ارسال کنید. برای اینکار ابتدا باید
یک نمونه از الگویی را که طراحی کردهایم، داخل سیستم ذخیره کنیم. یعنی باید آنرا از
دیتابیس فراخوانی کنیم و آن آرایهی بایتی را، بر روی سیستم، تبدیل به فایل
کنیم. سپس از سمت برنامه، تمامی فیلدهای موجود در این الگو را خوانده و بجای تک تک آنها، مقدار مناسبی را قرار دهیم. در نهایت این فایل را توسط کدنویسی بر
روی سیستم کاربر ذخیره میکنیم. فایل را تبدیل به آرایه بایتی میکنیم، داخل
دیتابیس درج میکنیم و فایل را از سیستم کاربر حذف میکنیم.
بنابراین در ادامه ابتدا
Assembly مربوط به MicroSoft.Office.Interop.Word را به رفرنسهای پروژه اضافه
میکنیم و سربرگش را هم Using میکنیم.
حالا میرسیم به کد نویسی:
کدهای زیر را به صورت سراسری داخل فرم تعریف میکنیم:
//LOCATION OF THE TEMPLATE FILE ON THE MACHINE; Object oTemplatePath = string.Format("{0}\\NewDocument.dot", Application.StartupPath); //OBJECT OF MISSING "NULL VALUE" Object oMissing = System.Reflection.Missing.Value; //OBJECTS OF FALSE AND TRUE Object oTrue = true; Object oFalse = false; //CREATING OBJECTS OF WORD AND DOCUMENT Microsoft.Office.Interop.Word.Application oWord = null; Microsoft.Office.Interop.Word.Document oWordDoc = null;
// Fetching Template ByteArray From Database => Byte[] YourTemplateByteArray = Fetch Template; System.IO.File.WriteAllBytes(oTemplatePath.ToString(), YourByteArray); oWord = new Microsoft.Office.Interop.Word.Application(); oWordDoc = new Microsoft.Office.Interop.Word.Document(); //Adding A New Document From A Template oWordDoc = oWord.Documents.Add(ref oTemplatePath, ref oMissing, ref oMissing, ref oMissing); int iTotalFields = 0; // Finding Mailmerge Fields foreach(Microsoft.Office.Interop.Word.Field myMergeField in oWordDoc.Fields) { iTotalFields++; Microsoft.Office.Interop.Word.Range rngFieldCode = myMergeField.Code; String fieldText = rngFieldCode.Text; // Only Get The Mailmerge Fields if (fieldText.StartsWith(" MERGEFIELD")) { // Gives The Fieldnames as Entered in .DOT File string fieldName = fieldText.Substring(12, fieldText.IndexOf(" ", 12) - 12); switch (fieldName) { case "Letter_No": myMergeField.Select(); oWord.Selection.TypeText(txtLetterNo.Text); break; case "Letter_Date": myMergeField.Select(); oWord.Selection.TypeText(DateTime.Now); break; case "Letter_Has_Attachment": myMergeField.Select(); oWord.Selection.TypeText("دارد یا ندارد"); break; // And So On default: break; } } } //Showing The Document To The User oWord.Visible = true;
در ادامه یک دکمه را برای ذخیرهی فایل ورد قرار میدهیم. زمانیکه کاربر تایپ کردنش تمام شد و هنوز برنامهی ورد در حال اجراست، این دکمه را اجرا میکند. دقت کنید برنامهی ورد نباید بسته شود؛ باید باز باشد. بعد دکمهی ذخیره را میزنیم. با کدنویسی، برنامهی Word را خودمان میبندیم؛ نیازی به دخالت کاربر نیست.
oWordDoc.Save(); //Closing the file oWordDoc.Close(ref oFalse, ref oMissing, ref oMissing); //Quitting the application oWord.Quit(ref oMissing, ref oMissing, ref oMissing); byte[] FileBuffer = System.IO.File.ReadAllBytes(oTemplatePath.ToString ()); // Now Insert The FileBuffer Into Database as A Letter
خوب؛ کار تمام است! حالا فیلد FileBuffer را باید بسته به کدنویسی خودتان، داخل دیتابیس ذخیره کنید که برای بعدها بتوانید آنرا واکشی کرده و به کاربر نمایش دهید. این هم نمونهی نهایی جایگذاری فیلدها:
این آموزش را خیلی سال پیش در این تاپیک داخل فوروم برنامه نویس نوشته بودم.
نگاهی به محتوای JSON Web Token تولیدی
اگر مطلب قسمت قبل را پیگیری کرده باشید، پس از لاگین، یک چنین خروجی را در کنسول توسعه دهندگان مرورگر میتوان مشاهده کرد که همان return Ok(new { access_token = jwt }) دریافتی از سمت سرور است:
اکنون این رشتهی طولانی را در حافظه کپی کرده و سپس به سایت https://jwt.io/#debugger-io مراجعه و در قسمت دیباگر آن، این رشتهی طولانی را paste میکنیم تا آنرا decode کند:
برای نمونه payload آن حاوی یک چنین اطلاعاتی است:
{ "jti": "b2921057-32a4-fbb2-0c18-5889c1ab8e70", "iss": "https://localhost:5001/", "iat": 1576402824, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "1", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Vahid N.", "DisplayName": "Vahid N.", "http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata": "1", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Admin", "nbf": 1576402824, "exp": 1576402944, "aud": "Any" }
استخراج اطلاعات کاربر وارد شدهی به سیستم، از JSON Web Token دریافتی
همانطور که در payload توکن دریافتی از سرور نیز مشخص است، اطلاعات ارزشمندی از کاربر، به همراه آن ارائه شدهاند و مزیت کار با آن، عدم نیاز به کوئری گرفتن مداوم از سرور و بانک اطلاعاتی، جهت دریافت مجدد این اطلاعات است. بنابراین اکنون در برنامهی React خود، قصد داریم مشابه کاری را که سایت jwt.io انجام میدهد، پیاده سازی کرده و به این اطلاعات دسترسی پیدا کنیم و برای مثال DisplayName را در Navbar نمایش دهیم. برای این منظور فایل app.js را گشوده و تغییرات زیر را به آن اعمال میکنیم:
- میخواهیم اطلاعات کاربر جاری را در state کامپوننت مرکزی App قرار دهیم. سپس زمانیکه کار رندر کامپوننت NavBar درج شدهی در متد رندر آن فرا میرسد، میتوان این اطلاعات کاربر را به صورت props به آن ارسال کرد؛ و یا به هر کامپوننت دیگری در component tree برنامه.
- بنابراین ابتدا کامپوننت تابعی بدون حالت App را تبدیل به یک کلاس کامپوننت استاندارد مشتق شدهی از کلاس پایهی Component میکنیم. اکنون میتوان state را نیز به آن اضافه کرد:
class App extends Component { state = {};
- برای decode کردن توکن، نیاز به نصب کتابخانهی زیر را داریم:
> npm install --save jwt-decode
import jwtDecode from "jwt-decode"; // ... class App extends Component { state = {}; componentDidMount() { try { const jwt = localStorage.getItem("token"); const currentUser = jwtDecode(jwt); console.log("currentUser", currentUser); this.setState({ currentUser }); } catch (ex) { console.log(ex); } }
- اکنون میتوان شیء currentUser را به صورت props، به کامپوننت NavBar ارسال کرد:
render() { return ( <React.Fragment> <ToastContainer /> <NavBar user={this.state.currentUser} /> <main className="container">
نمایش اطلاعات کاربر وارد شدهی به سیستم در NavBar
پس از ارسال شیء کاربر به صورت props به کامپوننت src\components\navBar.jsx، کدهای این کامپوننت را به صورت زیر جهت نمایش نام کاربر جاری وارد شدهی به سیستم تغییر میدهیم:
const NavBar = ({ user }) => {
سپس میتوان لینکهای Login و Register را به صورت شرطی رندر کرد و نمایش داد:
{!user && ( <React.Fragment> <NavLink className="nav-item nav-link" to="/login"> Login </NavLink> <NavLink className="nav-item nav-link" to="/register"> Register </NavLink> </React.Fragment> )}
شبیه به همین حالت را برای هنگامیکه کاربر، تعریف شدهاست، جهت نمایش نام او و لینک به Logout، نیاز داریم:
{user && ( <React.Fragment> <NavLink className="nav-item nav-link" to="/logout"> Logout </NavLink> <NavLink className="nav-item nav-link" to="/profile"> {user.DisplayName} </NavLink> </React.Fragment> )}
فعلا تا پیش از پیاده سازی Logout، برای آزمایش آن، به کنسول توسعه دهندگان مرورگر مراجعه کرده و توکن ذخیره شدهی در ذیل قسمت application->storage را دستی حذف کنید. سپس صفحه را ریفرش کنید. اینبار لینکهای به Login و Register نمایان میشوند.
یک مشکل! در این حالت (زمانیکه توکن حذف شدهاست)، از طریق قسمت Login به برنامه وارد شوید. هرچند این قسمتها به درستی کار خود را انجام میدهند، اما هنوز در منوی بالای سایت، نام کاربری و لینک به Logout ظاهر نشدهاند. علت اینجا است که در کامپوننت App، کار دریافت توکن در متد componentDidMount انجام میشود و این متد نیز تنها یکبار در طول عمر برنامه فراخوانی میشود. برای رفع این مشکل به src\components\loginForm.jsx مراجعه کرده و بجای استفاده از history.push برای هدایت کاربر به صفحهی اصلی برنامه، نیاز خواهیم داشت تا کل برنامه را بارگذاری مجدد کنیم. یعنی بجای:
this.props.history.push("/");
window.location = "/";
پیاده سازی Logout کاربر وارد شدهی به سیستم
برای logout کاربر تنها کافی است توکن او را از local storage حذف کنیم. به همین جهت مسیریابی جدید logout را که به صورت لینکی به NavBar اضافه کردیم:
<NavLink className="nav-item nav-link" to="/logout"> Logout </NavLink>
import Logout from "./components/logout"; // ... class App extends Component { render() { return ( // ... <Switch> // ... <Route path="/logout" component={Logout} />
import { Component } from "react"; class Logout extends Component { componentDidMount() { localStorage.removeItem("token"); window.location = "/"; } render() { return null; } } export default Logout;
بهبود کیفیت کدهای نوشته شده
اگر به کامپوننت App دقت کنید، کلید token استفاده شدهی در آن، در چندین قسمت برنامه مانند login و logout، تکرار و پراکنده شدهاست. بنابراین بهتر است جزئیات پیاده سازی مرتبط با اعتبارسنجی کاربران، به ماژول مختص به آنها (src\services\authService.js) منتقل شود تا سایر قسمتهای برنامه، به صورت یکدستی از آن استفاده کنند و اگر در این بین نیاز به تغییری بود، فقط یک ماژول نیاز به تغییر، داشته باشد.
برای این منظور، ابتدا متد login قبلی را طوری تغییر میدهیم که کار ذخیره سازی توکن را نیز در authService.js انجام دهد:
const tokenKey = "token"; export async function login(email, password) { const { data: { access_token } } = await http.post(apiEndpoint + "/login", { email, password }); console.log("JWT", access_token); localStorage.setItem(tokenKey, access_token); }
const { data } = this.state; await auth.login(data.username, data.password); window.location = "/";
همینکار را برای logout نیز در authService انجام داده:
export function logout() { localStorage.removeItem(tokenKey); }
import * as auth from "../services/authService"; class Logout extends Component { componentDidMount() { auth.logout();
import jwtDecode from "jwt-decode"; //... export function getCurrentUser() { try { const jwt = localStorage.getItem(tokenKey); const currentUser = jwtDecode(jwt); console.log("currentUser", currentUser); return currentUser; } catch (ex) { console.log(ex); return null; } }
import * as auth from "./services/authService"; class App extends Component { state = {}; componentDidMount() { const currentUser = auth.getCurrentUser(); this.setState({ currentUser }); }
جای دیگری که از localStorage استفاده شده، متد doSumbit کامپوننت ثبت نام کاربران است. این قسمت را نیز به صورت زیر به authService اضافه میکنیم:
export function loginWithJwt(jwt) { localStorage.setItem(tokenKey, jwt); }
import * as auth from "../services/authService"; // ... const response = await userService.register(this.state.data); auth.loginWithJwt(response.headers["x-auth-token"]);
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-27-backend.zip و sample-27-frontend.zip
- A bug is fixed in loop unroller which might lead to wrong condition codes being generated in the unrolled loop.
- Fixed a bug that caused ServiceHub.Host.CLR.x64 to stop working.
- Fixes issue in Chinese version of compiler errors C4533 and C2362 which resulted in incorrect order of string substitution for those languages.
- Improved stability of the Diagnostic Tools and Performance Profiler.
- Code generation problem causing crash with pure virtual destructor in const object
- Fixed a constant evaluation regression from 16.5 Preview 2
- Fixed an issue where MacOS couldn't be paired.
- Fixed an issue where the Diagnostic Tools while debugging and Performance Profiler fail to launch on XBox devices.
امکان انجام محاسبات سمت کلاینت در EF Core
var test1 = context.Blogs .Where(blog => String.Compare(blog.Url, "A", StringComparison.Ordinal) > 0) .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog]
var test2 = context.Blogs .Where(blog => String.Compare(blog.Url, "B") > 0) .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog] // WHERE [blog].[Url] > N'B'
var test3 = context.Blogs .Where(blog => blog.Url.Equals("C", StringComparison.OrdinalIgnoreCase)) .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog]
var test3_1 = context.Blogs .Where(blog => blog.Url.Equals("C_1")) .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog] // WHERE [blog].[Url] = N'C_1'
var test4 = context.Blogs .Where(blog => blog.Url.StartsWith("D")) .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog] // WHERE [blog].[Url] LIKE N'D' + N'%' AND (LEFT([blog].[Url], LEN(N'D')) = N'D')
var test5 = context.Blogs .Where(blog => EF.Functions.Like(blog.Url, "S_i%")) .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog] // WHERE [blog].[Url] LIKE N'S_i%'
var test6 = context.Blogs .Where(blog => blog.Url.ToUpper() == "E") .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog] // WHERE UPPER([blog].[Url]) = N'E'
var test7 = context.Blogs .Where(blog => blog.Url.ToUpperInvariant() == "F") .ToList(); // SELECT [blog].[BlogId], [blog].[Url] // FROM [Blogs] AS [blog]
When we started the Google Code project hosting service in 2006, the world of project hosting was limited. We were worried about reliability and stagnation, so we took action by giving the open source community another option to choose from. Since then, we’ve seen a wide variety of better project hosting services such as GitHub and Bitbucket bloom. Many projects moved away from Google Code to those other systems. To meet developers where they are, we ourselves migrated nearly a thousand of our own open source projects from Google Code to GitHub.
var article = "date datetime datelocal Groups dnttips the elements Groups the elements dnttips the".Split(' '); var result = article.GroupBy(x => x).Select(x => new { Word = x.Key, Count = x.Count() }); foreach (var item in result) { Console.WriteLine($"{item.Word} : {item.Count}"); }
date : 1 datetime : 1 datelocal : 1 Groups : 2 dnttips : 2 the : 3 elements : 2