بهبود SEO در ASP.NET MVC
if (absoluteUrlToLower.Contains("http://www.") || absoluteUrlToLower.Contains("https://www.")) return absoluteUrlToLower;
Generic Host چیست؟
Generic Host یکی از ویژگیهای جدید ASP.NET Core 2.1 است. هدف آن جداسازی HTTP pipeline برنامه، از Web Host API آن است. یکی از مزایای اینکار، امکان استفادهی از آن نه فقط در پروژههای وب، بلکه در پروژههای کنسول نیز میباشد. به این ترتیب میتوان کارهای غیر HTTP را از برنامهی وب مجزا کرد تا به کارآیی بیشتری رسید و برای این منظور اینترفیس IHostedService را که در فضای نام Microsoft.Extensions.Hosting قرار دارد، برای ثبت کارهای پسزمینهی خارج از اعمال web host جاری، ارائه دادهاند:
namespace Microsoft.Extensions.Hosting { public interface IHostedService { Task StartAsync(CancellationToken cancellationToken); Task StopAsync(CancellationToken cancellationToken); } }
یک مثال: معرفی کار پسزمینهای که هر دو ثانیه یکبار انجام میشود
در SampleHostedService زیر، عبارت Hosted service executing به همراه زمان جاری، هر دو ثانیه یکبار لاگ میشود و اگر برنامه را توسط دستور dotnet run اجرا کنید، میتوانید خروجی آنرا در کنسول، مشاهده کنید:
using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace MvcTest { public class SampleHostedService : IHostedService { private readonly ILogger<SampleHostedService> _logger; public SampleHostedService(ILogger<SampleHostedService> logger) { _logger = logger; } public async Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("Starting Hosted service"); while (!cancellationToken.IsCancellationRequested) { _logger.LogInformation("Hosted service executing - {0}", DateTime.Now); await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); } } public Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("Stopping Hosted service"); return Task.CompletedTask; } } }
namespace MvcTest { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IHostedService, SampleHostedService>();
services.AddHostedService<SampleHostedService>();
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
Startup.cs(82,56): error CS0104: 'IHostingEnvironment' is an ambiguous reference between 'Microsoft.AspNetCore.Hosting.IHostingEnvironment' and 'Microsoft.Extensions.Hosting.IHostingEnvironment'
مشکلات پیاده سازی کار پسزمینهی SampleHostedService فوق
هر چند اگر مثال فوق را اجرا کنید، خروجی مناسبی را دریافت خواهید کرد، اما دارای این اشکال مهم نیز هست:
D:\MvcTest>dotnet run info: MvcTest.SampleHostedService[0] Starting Hosted service info: MvcTest.SampleHostedService[0] Hosted service executing - 02/19/2019 14:45:10 info: MvcTest.SampleHostedService[0] Hosted service executing - 02/19/2019 14:45:12 info: MvcTest.SampleHostedService[0] Hosted service executing - 02/19/2019 14:45:14 Ctrl+C Application is shutting down... Hosting environment: Development Content root path: D:\MvcTest Now listening on: https://localhost:5001 Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down.
از دیدگاه ASP.NET Core، یک کار پس زمینه زمانی خاتمه یافته محسوب میشود که متد StartAsync، مقدار Task.CompletedTask را بازگرداند؛ در غیراینصورت، در حال اجرا درنظر گرفته میشود و چون در پیاده سازی فوق این نکته رعایت نشدهاست، این Task همواره در حال اجرا و خاتمه نیافته محسوب میشود و نوبت به مابقی کارها نخواهد رسید. همچنین در قسمت StopAsync نیز بهتر است یک فیلد CancellationTokenSource تعریف شدهی در سطح کلاس را مورد استفاده قرار داد و متد Cancel آنرا فراخوانی کرد تا اطلاع رسانی صحیحی را به متد StartAsync در مورد خاتمهی برنامه، انجام دهد.
برای این منظور و جهت ساده سازی و پیاده سازی تمام این نکات، از اینترفیس خام IHostedService، یک کلاس abstract به نام BackgroundService نیز در فضای نام Microsoft.Extensions.Hosting پیش بینی شدهاست:
namespace Microsoft.Extensions.Hosting { public abstract class BackgroundService : IHostedService, IDisposable { protected BackgroundService(); public virtual void Dispose(); public virtual Task StartAsync(CancellationToken cancellationToken); public virtual Task StopAsync(CancellationToken cancellationToken); protected abstract Task ExecuteAsync(CancellationToken stoppingToken); } }
namespace MvcTest { public class PrinterHostedService : BackgroundService { private readonly ILogger<SampleHostedService> _logger; public PrinterHostedService(ILogger<SampleHostedService> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Starting Hosted service"); while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Hosted service executing - {0}", DateTime.Now); await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken); } } } }
namespace MvcTest { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddHostedService<PrinterHostedService>();
D:\MvcTest>dotnet run Hosting environment: Development infoContent root path: D:\MvcTest Now listening on: https://localhost:5001 Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down. : MvcTest.SampleHostedService[0] Starting Hosted service info: MvcTest.SampleHostedService[0] Hosted service executing - 02/19/2019 15:00:23 info: MvcTest.SampleHostedService[0] Hosted service executing - 02/19/2019 15:00:25 info: MvcTest.SampleHostedService[0] Hosted service executing - 02/19/2019 15:00:27 Application is shutting down... ^C
یک نکته: تزریق وابستگی DbContext برنامه در یک سرویس کار پسزمینه
IHostedServiceها با طول عمر singleton به سیستم تزریق وابستگیها معرفی میشوند. در این حالت اگر سرویسهایی با طول عمر transient و یا scoped را به آنها تزریق کنید، دیگر طول عمر مدنظر شما را نداشته و آنها هم به صورت singleton عمل خواهند کرد. هر چند خود سیستم تزریق وابستگیهای NET Core. با صدور استثنائی، از این مساله جلوگیری میکند (در این مورد در مطالب «مهارتهای تزریق وابستگیها در برنامههای NET Core. - قسمت چهارم - پرهیز از الگوی Service Locator در برنامههای وب» و همچنین «قسمت سوم - رهاسازی منابع سرویسهای IDisposable» بیشتر بحث شدهاست). یک چنین مواردی را به صورت زیر با تزریق IServiceScopeFactory و ساخت صریح یک Scope میتوان مدیریت کرد:
using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; public abstract class ScopedBackgroundService : BackgroundService { private readonly IServiceScopeFactory _serviceScopeFactory; public ScopedBackgroundService(IServiceScopeFactory serviceScopeFactory) { _serviceScopeFactory = serviceScopeFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { using (var scope = _serviceScopeFactory.CreateScope()) { await ExecuteInScope(scope.ServiceProvider, stoppingToken); } } public abstract Task ExecuteInScope(IServiceProvider serviceProvider, CancellationToken stoppingToken); }
طراحی سرویس کارهای پسزمینهی زمانبندی شده
ASP.NET Core، متد ExecuteAsync را یکبار بیشتر اجرا نمیکند. بنابراین پیاده سازی تایمری که بخواهد برای مثال ارسال ایمیلهای خبرنامهی سایت را هر روز ساعت 11 شب انجام دهد، به خود ما واگذار شدهاست. برای پیاده سازی بهتر این تایمر میتوان از کتابخانهی NCrontab که توسط نویسندهی کتابخانهی معروف ELMAH تهیه شدهاست، استفاده کرد که با برنامههای NET Core. نیز سازگاری دارد:
dotnet add package ncrontab
┌───────────── minute (0 - 59) │ ┌───────────── hour (0 - 23) │ │ ┌───────────── day of month (1 - 31) │ │ │ ┌───────────── month (1 - 12) │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday; │ │ │ │ │ 7 is also Sunday on some systems) │ │ │ │ │ │ │ │ │ │ * * * * *
* * * * * * - - - - - - | | | | | | | | | | | +--- day of week (0 - 6) (Sunday=0) | | | | +----- month (1 - 12) | | | +------- day of month (1 - 31) | | +--------- hour (0 - 23) | +----------- min (0 - 59) +------------- sec (0 - 59)
using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using NCrontab; using static NCrontab.CrontabSchedule; public abstract class ScheduledScopedBackgroundService : ScopedBackgroundService { private CrontabSchedule _schedule; private DateTime _nextRun; protected abstract string Schedule { get; } public ScheduledScopedBackgroundService(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory) { _schedule = CrontabSchedule.Parse(Schedule, new ParseOptions { IncludingSeconds = true }); _nextRun = _schedule.GetNextOccurrence(DateTime.Now); } public override async Task ExecuteInScope(IServiceProvider serviceProvider, CancellationToken stoppingToken) { do { var now = DateTime.Now; if (now > _nextRun) { await ScheduledExecuteInScope(serviceProvider, stoppingToken); _nextRun = _schedule.GetNextOccurrence(DateTime.Now); } await Task.Delay(1000, stoppingToken); //1 second delay } while (!stoppingToken.IsCancellationRequested); } public abstract Task ScheduledExecuteInScope(IServiceProvider serviceProvider, CancellationToken stoppingToken); }
روش استفادهی از آن برای تعریف یک وظیفهی جدید نیز به صورت زیر است:
using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; public class MyScheduledTask : ScheduledScopedBackgroundService { private readonly ILogger<MyScheduledTask> _logger; public MyScheduledTask( IServiceScopeFactory serviceScopeFactory, ILogger<MyScheduledTask> logger) : base(serviceScopeFactory) { _logger = logger; } protected override string Schedule => "*/10 * * * * *"; //Runs every 10 seconds public override Task ScheduledExecuteInScope(IServiceProvider serviceProvider, CancellationToken stoppingToken) { _logger.LogInformation("MyScheduledTask executing - {0}", DateTime.Now); return Task.CompletedTask; } }
روش معرفی آن به سیستم نیز مانند قبل است:
namespace MvcTest { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddHostedService<MyScheduledTask>();
D:\MvcTest>dotnet run Hosting environment: Development Content root path: D:\MvcTest Now listening on: https://localhost:5001 Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down. info: MyScheduledTask[0] MyScheduledTask executing - 02/19/2019 19:18:50 info: MyScheduledTask[0] MyScheduledTask executing - 02/19/2019 19:19:00 info: MyScheduledTask[0] MyScheduledTask executing - 02/19/2019 19:19:10 Application is shutting down... ^C
برای کپی کردن فایل mdf و فایل ldf ، یک بانک اطلاعاتی ، بدون این که سرویس SQL Server را متوقف کنیم و یا این که آن بانک اطلاعاتی را Detach کنیم ، میتوانیم از نرم افزار HoboCopy.exe استفاده نمایید . برای این کار ابتدا این فایل را در یک مسیری کپی نمایید . به طور مثال آن را در مسیر C:\Windows\System32 کپی کنید . سپس وارد PowerShell شوید . البته باید به صورت Run as administrator این کار را انجام دهید .
سپس در آن مسیر نام فایل اجرای HoboCopy و سپس آدرس فایل مبداء (آدرس فایلهای بانک اطلاعاتی) و در نهایت آدرس فولدری که بناست اطلاعات mdf و ldf در آنجا کپی شوند . i:\MyDB آدرس مبداء میباشد و آدرس i:\My ، آدرس مقصد میباشد .
var users = context.Users.Include(x => x.Articles).ToList();
SELECT [u].[Id], [u].[FirstName], [u].[LastName], [a].[Id], [a].[Approved], [a].[AuthorId], [a].[Body], [a].[PubDate], [a].[Subject] FROM [Users] AS [u] LEFT JOIN [Articles] AS [a] ON [u].[Id] = [a].[AuthorId] ORDER BY [u].[Id], [a].[Id]
شکل یک
همانطور که در عکس فوق مشاهده میکنید، کاربر با شناسهی 1، ده مقاله را منتشر کردهاست که به ازای تعداد مقالات، سه فیلد شناسه کاربر، نام و نام خانوادگی، تکرار شدهاست و همین اتفاق برای کاربر با شناسهی 2 هم تکرار شدهاست. قطعا در اکثر نرم افزارها، نیاز به چنین کوئریها و دادههایی زیاد است و جلوگیری از این تکرار دادهها، میتواند بر روی کارایی نرم افزار تاثیر گذار باشد.
Cartesian explosion
اجرای یک Join بین جداول با رابطهی one to many، منجر به تکرار ستونهای جدول طرف one، به تعداد رکوردهای مرتبط میشود. این اتفاق باعث هدر رفت منابع و همچنین کند شدن اجرای کوئری خواهد شد که این مشکل تحت عنوان Cartesian explosion problem شناخته میشود.
از نسخه EF Core5.0، امکانی اضافه شدهاست که کمک میکند این مشکل را برطرف کنیم و سرعت اجرای کوئریها سریعتر شود. Entity Framework به صورت پیش فرض، کوئریها را در قالب یک دستور (یک رفت و برگشت) انجام میدهد، اما میتوان این رفتار را با استفاده از قابلیت SplitQuery تغییر داد.
متد ()SplitQuery
با استفاده از این متد، به Entity Framework الزام میکنیم که بجای استفاده از Join در یک کوئری، کوئریهای جداگانهای را بر روی دیتابیس اجرا کند. برای کوئری اول که در بالا نوشتیم، به صورت زیر میتوانیم SplitQuery را اعمال کنیم:
var users = context.Users.AsSplitQuery().Include(x => x.Articles).ToList();
کوئری حاصل از کد فوق به صورت زیر میباشد:
-- First Part SELECT [u].[Id], [u].[FirstName], [u].[LastName] FROM [Users] AS [u] ORDER BY [u].[Id] -- Second Part SELECT [a].[Id], [a].[Approved], [a].[AuthorId], [a].[Body], [a].[PubDate], [a].[Subject], [u].[Id] FROM [Users] AS [u] INNER JOIN [Articles] AS [a] ON [u].[Id] = [a].[AuthorId] ORDER BY [u].[Id]
همانطور که مشاهده میکنید، دو کوئری تولید شده است که کوئری اول برای دریافت لیست کاربران و کوئری دوم برای لیست مقالات تولید شدهاست. این تغییر باعث شدهاست که فیلدهای مورد نیاز از جدول کاربران، به تعداد مقالات هر کاربر تکرار نشود.
شکل 2- خروجی حاصل بعد از اجرا به صورت SplitQuery
فعال سازی به صورت سراسری
همانطور که بیان شد، EF به صورت پیش فرض کوئریها را در قالب یک درخواست اجرا میکند. اگر تمایل دارید خاصیت SplitQuery بر روی تمامی کوئریها اعمال شود، میتوانید به صورت زیر این امکان را به صورت سراسری اعمال نمایید.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlServer( @"Server=(localdb)\mssqllocaldb;Database=EFQuerying;", o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)); }
اگر SplitQuery را به صورت سراسری فعال کردید و نیاز داشتید جایی یک کوئری را به همان روش SignleQuery اجرا کنید، میتوانید از متد SingleQuery به صورت زیر استفاده نمایید.
var users = context.Users.AsSingleQuery().Include(x => x.Articles).ToList();
عکس زیر مقایسه ای بین اجرای کوئریها به صورت Single و Split میباشد:
مبنع: thinktecture
در رابطه با SplitQuery موارد زیر مطرح میباشد :
- زمانیکه کوئری تبدیل به دو یا چند کوئری میشود، ممکن است بعد از اجرا کوئری اول و قبل از اجرای کوئری دوم، یک به روزرسانی انجام شود که ممکن است consistency نقض شود.
- در این حالت، چندین درخواست و رفت و برگشت اجرا میشود که همین میتواند باعث تاخیر و افزایش زمان گردد.
public boolean isNetworkAvailable(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); }
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
البته توصیه ما نیز استفاده از این کد هست و توصیه میشود که استثناءها یا وضعیت خروجیها را کنترل نمایید. روشهای زیر تنها تحلیل کوچکی برای بررسی وضعیت اینترنت است یا اینکه واقعا به این بررسیهای نیاز داشته باشید. در غیر این صورت برای بسیاری از برنامهها همین کد کفایت میکند. راه حلهای پایین مواردی است که از تجربه خود به دست آوردهام تا شاید راهنمایی برای افرادی باشد که میخواهند این کار را آغاز کنند تا در وقتشان صرفه جویی شود.
مثالهای زیر وضعیتهایی را نشان میدهند که شبکه موجود است ولی اینترنتی در دسترس نیست:
- مودم وای فای را فعال کرده است، ولی اینترنت را در اختیار ندارد که این علل میتواند عدم ثابت شدن چراغ DSL یا راه اندازی مجدد مودم باشد که وای فای زودتر از DSL فعال میشود و یا اینکه اشتراک شما تمام شده است یا در شبکه به مشکل برخورد کردهاید.
- شما از طریق mobile data قصد اتصال دارید. در این حالت یا اعتبار شما پایان یافته است یا شبکه آنان دچار اختلال است.
- شما در یک محیط اداری هستید که به عنوان مثال سیستمشان توسط روترهای میکروتیک هدایت میشود. در این حالت شما میتوانید وارد شبکه بدون کلمه عبور آنان شوید. ولی نیاز دارید که حتما صفحه لاگین را رد نمایید تا اینترنت در اختیار شما قرار بگیرد.
در همه مثالهای بالا اینترنتی وجود ندارد، ولی تکه کد بالا true را برخواهد گرداند.
public boolean IsInternetConnected() { try { Process ipProcess = Runtime.getRuntime().exec("/system/bin/ping -c 1 4.2.2.4"); int exitValue = ipProcess.waitFor(); return (exitValue == 0); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return false; }
System.Diagnostics.Process.Start("processName");
فرمان یا دستور بالا به شرح زیر است:
در سیستم عامل لینکوس تمام برنامههای سیستمی مورد نیاز، در شاخه bin قرار میگیرند و پینگ، یکی از آن هاست. سوییچ c هم که مخفف count است، به معنی تعداد درخواستهای یک اکو است که در این دستور گفتهایم تنها یک درخواست اکو echo ارسال کن. (ااطلاعات بیشتر در مورد دستور پینگ ).
در خط بعدی از آنجا که این دستور، یک دستور زمان بر است، باید مدتی در این کد توقف شود تا مقدار مورد نظر دریافت شود. در صورتی که مقدار 0 بازگردانده شود، اکو پاسخ داده شده است و یعنی اینکه شما به اینترنت متصلید. (مشاهده کدهای وضعیتی ICMP )
وجود catchهای بالا الزامی است از آنجا که متدهای استفاده شده توسط استثناءهای زیر throw شدهاند، جاوا شما را ملزم به استفاده از catchهای این استثناها خواهد کرد.
public Process exec(String prog) throws java.io.IOException { return exec(prog, null, null); }
public abstract int waitFor() throws InterruptedException;
برای اجرا این تکه کد شما نیاز به مجوز اتصال به اینترنت دارید:
<uses-permission android:name="android.permission.INTERNET"/>
نکته مهم اینکه نگران اجرای این دستور در گوشی کاربر نباشید. این دستور نیاز به مجوز روت ندارد.
تکه کد زیر صفحه گوگل را درخواست میکند و در نهایت وضعیت کد http آن را دریافت میکنیم و اگر این کد وضعیت برابر 200 بود به این معنی است که اینترنت متصل میباشد. ولی در یک سیستم میکروتیک که هنوز وارد سیستم آن نشده باشید، به صفحه لاگین هدایت میشوید و وضعیت دیگری را دریافت خواهید مانند آدرس درخواستی شما redirect شده است یا اینکه باز هم کد 200 را دریافت میکنید که در بیشتر حالات هم به همین شکل است. برای رفع این مسئله بهتر است url فعلی را با url درخواستی مطابقت دهیم. برای این قضیه گوگل در بخش Handling Network Sign-On این صفحه چنین کدی را پیشنهاد داده است:
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); try { InputStream in = new BufferedInputStream(urlConnection.getInputStream()); if (!url.getHost().equals(urlConnection.getURL().getHost())) { // we were redirected! Kick the user out to the browser to sign on? ... } finally { urlConnection.disconnect(); } }
ولی با توجه به تحقیقات و مشاهداتی که کردهام، در بعضی از گوشیها این کد کارکرد مناسبی ندارد و برای خودم هم پاسخی دریافت نکردم. اگر واقعا باز هم مصر هستید که این وضعیت را بررسی کنید، میتواند بررسی یک url با محتوای خاص باشد و بعد از دریافت این صفحه محتوای آن را بررسی کنید.
ولی با این همه بسیاری از برنامهها از همان تکه کد بالا استفاده میکنند و با مدیریت استثناءها سعی در جلوگیری از خطا دارند. در غیر این صورت شما باید مدام در حال بررسی وضعیت اینترنت به شکل بالا باشید که بسیار زمان بر خواهد شد.
در صورتی که شما روش بهتری را برای بررسی وضعیت اینترنت دارید یا راه حل خاصی به نظرتان میرسد بسیار عالی خواهد بود که آن را با ما به اشتراک بگذارید.
بررسی حالتهای مختلف نصب RavenDB
الف) استفاده از RavenDB در حالت مدفون شده یا Embedded
حالت Embedded به این معنا است که RavenDB درون پروسه برنامه شما اجرا خواهد شد و نه به صورت پروسهای مجزا. این حالت برای ارائه ساده برنامههای دسکتاپ بسیار مناسب است؛ یا حتی توزیع برنامههای سبک ASP.NET بدون نیاز به نصب بانک اطلاعاتی خاصی بر روی وب سرور.
برای کار با RavenDB در حالت Embedded ابتدا فایلهای مورد نیاز آنرا از طریق نیوگت دریافت کنید:
PM> Install-Package RavenDB.Embedded -Pre
var documentStore = new EmbeddableDocumentStore { DataDirectory = @"~/app_data/ravendb" }; documentStore.Initialize();
امکان تعریف DataDirectory در فایل کانفیگ برنامه نیز وجود دارد. فقط در این حالت باید دقت داشت که نام مسیر، با DataDir شروع میشود و نه DataDirectory :
<connectionStrings> <add name="Local" connectionString="DataDir = ~\Data"/>
چند نکته جالب در مورد حالت Embedded
- امکان اجرای درون حافظهای RavenDB نیز وجود دارد:
var documentStore = new EmbeddableDocumentStore{RunInMemory = true}.Initialize()
- اجرای حالت Embedded به صورت Embedded HTTP:
در حالت Embedded دیگر دسترسی به برنامه سیلورلایت Raven studio وجود ندارد. اگر علاقمند به کار با آن بودید، خاصیت UseEmbeddedHttpServer شیء EmbeddableDocumentStore را به true تنظیم کنید. سپس فایل Raven.Studio.xap را در ریشه وب سایت خود قرار دهید. اکنون مانند قبل آدرس localhost:8080/raven/studio.html برقرار خواهد بود.
همچنین سرور Http این بانک اطلاعاتی را نیز میتوان دستی راه اندازی کرد. متد NonAdminHttp.EnsureCanListenToWhenInNonAdminContext بررسی میکند که آیا برنامه مجوز راه اندازی یک سرور را بر روی پورت مثلا 8080 دارد یا خیر.
NonAdminHttp.EnsureCanListenToWhenInNonAdminContext(8080); // Start the HTTP server manually var server = new RavenDbHttpServer(documentStore.Configuration, documentStore.DocumentDatabase); server.Start();
ب) نصب RavenDB به صورت سرویس ویندوز NT
اگر مایل باشیم تا RavenDB را نیز مانند SQL Server به صورت یک سرویس ویندوز NT نصب کنیم تا همواره در پس زمینه سرور در حال اجرا باشد، کنسول پاورشل ویندوز را گشوده و سپس فرمان ذیل را صادر کنید:
d:\ravendb\server> .\raven.server.exe /install
و اگر خواستید این سرویس را عزل کنید، دستور ذیل را در پاورشل ویندوز صادر کنید:
d:\ravendb\server> .\raven.server.exe /uninstall
ج) نصب RavenDB به صورت یک پروسه IIS (یا اجرا شده توسط IIS)
فایلهای مورد نیاز حالت اجرای RavenDB را به صورت یک پروسه مجزای IIS از نیوگت دریافت کنید:
PM> Install-Package RavenDB.AspNetHost -Pre
یک نکته
تمام بستههای مورد نیاز را یکجا از آدرس http://ravendb.net/download نیز میتوان دریافت کرد. در نگارشهای جدید، بسته نصاب نیز برای این بانک اطلاعاتی تهیه شده است که برای نمونه توزیع آنرا جهت حالت نصب در IIS سادهتر میکند.
خطای زیر را مشاده کردم .
An assembly specified in the application dependencies manifest (MyWebApp.deps.json) was not found: package: 'Microsoft.ApplicationInsights.AspNetCore', version: '2.1.1' path: 'lib/netstandard1.6/Microsoft.ApplicationInsights.AspNetCore.dll' This assembly was expected to be in the local runtime store as the application was published using the following target manifest files:
<PropertyGroup> <PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest> </PropertyGroup>