There are two MySQL providers for Entity Framework Core:
- The official one from MySQL: MySql.Data.EntityFrameworkCore. As of now, the latest version is 8.0.19, and works with Entity Framework Core 2.1 (and probably also 2.2). Since EF Core 3.0 is a major version with breaking changes, you cannot use it with this provider.
- The Pomelo provider: Pomelo.EntityFrameworkCore.MySql. There is a 3.1 version of this provider.
In other words, if you want to use EF Core 3.0/3.1 with MySQL, at this point you need to use the Pomelo provider (or wait for the official MySQL one to get released).
اشتراکها
کتاب Docker مختصر و مفید
Containers have revolutionized software development, allowing developers to bundle their applications with everything they need, from the operating system up, into a single package. Docker is one of the most popular platforms for containers, allowing them to be hosted on-premises or on the cloud, and to run on Linux, Windows, and Mac machines. With Docker Succinctly by Elton Stoneman, learn the basics of building Docker images, sharing them on the Docker Hub, orchestrating containers to deliver large applications, and much more.
اشتراکها
10 ویژگی عالی از Visual Studio Code
Enter Visual Studio Code
– a free, lean, cross-platform version of your beloved Visual Studio.
With a lightweight installation, you can enjoy most full-featured
benefits of Visual Studio on a Windows, Mac or Linux machine. This
article highlights 10 of the compelling features of VS Code, which aims
to be your one-stop rich code editor across any developer platform.
بازخوردهای پروژهها
عدم برقراری ارتباط با پروتکل ssl
با سلام
برای ارتباط با سرور دارای ssl استثنای زیر صادر میشود.
The request was aborted: Could not create SSL/TLS secure channel.
The full source tree with all the code, test code and everything that together constitutes the “Windows source code” is more than a half terabyte in size, in more than 4 million files. You can spend a year (seriously) just drilling down the source tree, more than a half million folders containing the code for every component making up the OS workstation and server products and all their editions, tools, and associated developement kits, and see what’s in there, read the file names and try to figure out what does what. It would take a life (or two) to read it all.
در این بخش قصد داریم سئو را بر روی یک برنامهی نوشته شده با آنگلولار و Asp.net Mvc اعمال نماییم. انگولار جیاس، صفحات را با استفاده از جاوااسکریپت رندر میکند، ولی اکثر کرالرها نمیتوانند جاوااسکریپت را اجرا کنند و موقع اجرای صفحات سایت ما فقط یک div خالی را میبینند.
کاری که سرویس Prerender یا فیلتر سفارشی AjaxCrawlable برای ما انجام میدهد، درخواستهایی را که از طرف کرالرها آمدهاست را شناسایی میکند و مانند یک مرورگر، با استفاده از phantomjs آنرا اجرا میکند و نتیجهی کامل صفحات ما را به صورت اچ تی ام ال استاتیک برمیگرداند.
فانتوم جی اس، موتور اختصاصی برای شبیه سازی مرورگر مبتنی بر Webkit میباشد. فانتوم جی اس را میتوانید بر روی ویندوز، لینوکس و مک نصب نمایید. فانتوم جی اس یک Console در اختیار برنامه نویس قرار میدهد که میتوان توسط آن، برنامههای جاوااسکریپت را اجرا نمود. همچنین فانتوم جی اس میتواند اسکرین شاتی را نیز از محتوای وب سایت ما فراهم نماید.
برای اینکه صفحات انگولار جی اس،ایندکس شوند سه مرحله وجود دارند:
1- به کرالر اطلاع دهیم که رندر کردن سایت، توسط جاوااسکریپت انجام میگردد؛ با اضافه کردن متاتگ زیر در اچ تی ام ال سایت (البته در حالت استفاده HTML5 push state ) :
<meta name="fragment" content="!"> <base href="/">
http://www.example.com/?_escaped_fragment_=
ما در این مثال از HTML5 push state استفاده میکنیم. بنابراین لینکی مانند http://www.example.com/user/123 توسط کرالر به صورت زیر دیده میشود:
http://www.example.com/user/123?_escaped_fragment_=
3- اچ تی ام ال کاملا رندر شده توسط سایت ما به کرالر ارسال گردد.
برای رندر کردن اچ تی ام ال صفحات، چندین روش وجود دارد:
روش اول: میتوانیم از سرویسهای آمادهای همچون Prerender.io استفاده کنیم که سرویسهایی را برای زبانهای مختلف ارائه کردهاند. باتوجه به توضیحات نمونه استفاده از آن در Asp.Net Mvc کافیست در سایت Prerender.io ثبت نام کرده، Token را دریافت کنیم و در کانفیگ برنامه قرار دهیم و در کلاس PreStart قطعه کد زیر را قرار دهیم:
DynamicModuleUtility.RegisterModule(typeof(Prerender.io.PrerenderModule));
یکی از ابزارهای مناسب تست کردن اینکه صفحات توسط کرالر ایندکس میشوند یا خیر، برنامه screamingfrog میباشد.
در پنل Ajax آن، صفحات ایندکس شده ما نمایش داده میشوند. لینکی مشابه زیر را در مرورگر اجرا کرده، با ViewPage Source کردن آن میتوانید نتیجه اچ تی ام ال کاملا رندر شده را مشاهده نمایید.
http://www.example.com/user/123?_escaped_fragment_=
نسخه رایگان سرویس Prerender.io تا 250 صفحه را پوشش میدهد.
روش دوم: فیلتر سفارشی AjaxCrawlable. در اولین قدم نیاز به نصب فانتوم جی اس داریم: <package id="PhantomJS" version="1.9.2" targetFramework="net452" /> <package id="phantomjs.exe" version="1.9.2.1" targetFramework="net452" />
برای اینکه خطای معروف A potentially dangerous Request.Form value was detected from the client را دریافت نکنیم، کافیست قسمتهایی از آدرس را که شامل کاراکترهای خاصی مانند :// میباشند، از url حذف کنیم و در اکشن returnHtml قسمتهای حذف شده را به url اضافه نماییم.
کرالرها با مشاهده تگ fragment، تمام لینکها را به همراه کوئری استرینگ _escaped_fragment_ میفرستند، که ما در سرور باید آنرا با رشته خالی جایگزین نماییم.
public class AjaxCrawlableAttribute : System.Web.Mvc.ActionFilterAttribute { private const string Fragment = "_escaped_fragment_"; public override void OnActionExecuting(ActionExecutingContext filterContext) { var request = filterContext.RequestContext.HttpContext.Request; var url = request.Url.ToString(); if (request.QueryString[Fragment] != null && !url.Contains("HtmlSnapshot/returnHTML")) { url = url.Replace("?_escaped_fragment_=", string.Empty).Replace(request.Url.Scheme + "://", string.Empty); url = url.Split(':')[1]; filterContext.Result = new RedirectToRouteResult( new RouteValueDictionary { { "controller", "HtmlSnapshot" }, { "action", "returnHTML" }, { "url", url } }); } return; } }
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "HtmlSnapshot", url: "HtmlSnapshot/returnHTML/{*url}", defaults: new { controller = "HtmlSnapshot", action = "returnHTML", url = UrlParameter.Optional }); routes.MapRoute( name: "SPA", url: "{*catchall}", defaults: new { controller = "Home", action = "Index" }) }
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new AjaxCrawlableAttribute()); } }
ایجاد کنترلر HtmlSnapshot و متد returnHTML :
Url را به عنوان آرگومان به تابع page.open فایل جاوااسکریپتی فانتوم میدهیم و بعد از اجرای کامل، خروجی را درViewData قرار میدهیم
public ActionResult returnHTML(string url) { var prefix = HttpContext.Request.Url.Scheme + "://" + HttpContext.Request.Url.Host+":"; url = prefix+url; string appRoot = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); var startInfo = new ProcessStartInfo { Arguments = string.Format("{0} {1}", Path.Combine(appRoot, "Scripts\\seo.js"), url), FileName = Path.Combine(appRoot, "bin\\phantomjs.exe"), UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, StandardOutputEncoding = System.Text.Encoding.UTF8 }; var p = new Process(); p.StartInfo = startInfo; p.Start(); string output1 = p.StandardOutput.ReadToEnd(); p.WaitForExit(); ViewData["result"] = output1.Replace("<!-- ngView: -->", "").Replace("ng-view=\"\"", ""); return View(); }
در فایل renderHtml.cshtml
@{ Layout = null; } @Html.Raw(ViewBag.result)
ایجاد فایل seo.js در پوشه Scripts سایت :
در این بخش webpage را ایجاد میکنیم و آدرس صفحه را از[system.args[1 دریافت کرده و عملیات کپچر کردن را آغاز میکنیم و بعد از تکمیل اطلاعات در سرور، کد زیر اجرا میشود:
console.log(page.content)
var page = require('webpage').create(); var system = require('system'); var lastReceived = new Date().getTime(); var requestCount = 0; var responseCount = 0; var requestIds = []; var startTime = new Date().getTime();; page.onResourceReceived = function (response) { if (requestIds.indexOf(response.id) !== -1) { lastReceived = new Date().getTime(); responseCount++; requestIds[requestIds.indexOf(response.id)] = null; } }; page.onResourceRequested = function (request) { if (requestIds.indexOf(request.id) === -1) { requestIds.push(request.id); requestCount++; } }; function checkLoaded() { return page.evaluate(function () { return document.all["compositionComplete"]; }) != null; } // Open the page page.open(system.args[1], function () { }); var checkComplete = function () { // We don't allow it to take longer than 5 seconds but // don't return until all requests are finished if ((new Date().getTime() - lastReceived > 300 && requestCount === responseCount) || new Date().getTime() - startTime > 10000 || checkLoaded()) { clearInterval(checkCompleteInterval); console.log(page.content); phantom.exit(); } } // Let us check to see if the page is finished rendering var checkCompleteInterval = setInterval(checkComplete, 300);
صفحه Layout.Cshtml
<!DOCTYPE html> <html ng-app="appOne"> <head> <meta name="fragment" content="!"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta charset="utf-8" /> <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <meta name="viewport" content="width=device-width" /> <base href="/"> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") <script src="~/Scripts/angular/angular.js"></script> <script src="~/Scripts/angular/angular-route.js"></script> <script src="~/Scripts/angular/angular-animate.js"></script> <script> angular.module('appOne', ['ngRoute'], function ($routeProvider, $locationProvider) { $routeProvider.when('/one', { template: "<div>one</div>", controller: function ($scope) { } }) .when('/two', { template: "<div>two</div>", controller: function ($scope) { } }).when('/', { template: "<div>home</div>", controller: function ($scope) { } }); $locationProvider.html5Mode({ enabled: true }); }); </script> </head> <body> <div id="body"> <section ng-view></section> @RenderBody() </div> <div id="footer"> <ul class='xoxo blogroll'> <li><a href="one">one</a></li> <li><a href="two">two</a></li> </ul> </div> </body> </html>
چند نکته تکمیلی:
* فانتوم جی اس قادر به اجرای لینکهای فارسی (utf-8) نمیباشد.
* اگر خطای syntax error را دریافت کردید ممکن است پروژه شما در مسیری طولانی در روی هارد دیسک قرار داشته باشد.
As we’ve been moving forward on the Outdated Answers project, one of the things that’s been clear is that on questions with multiple answers, we need a way to surface newer answers that may be more current, while reducing the visibility of older answers that may no longer be correct or relevant