mklink /j C:\android-sdk "C:\Program Files (x86)\Android\android-sdk"
<AotAssemblies>true</AotAssemblies> <EnableLLVM>true</EnableLLVM>
mklink /j C:\android-sdk "C:\Program Files (x86)\Android\android-sdk"
<AotAssemblies>true</AotAssemblies> <EnableLLVM>true</EnableLLVM>
سورس پروژه رو با این تغییرات در گیتهاب هم منتشر کردم
Install-Package EasyCaching.InMemory
services.AddEasyCaching(options => { // use memory cache with a simple way options.UseInMemory(); }
// تنظیم یک کش با کلید - مقدار - زمان انقضا void Set<T>(string cacheKey, T cacheValue, TimeSpan expiration); Task SetAsync<T>(string cacheKey, T cacheValue, TimeSpan expiration); // تنظیم یک کش با مقدار و زمان انقضا که تایپ مقدار از نوع دیکشنری هست و کلید دیکشنری بعنوان کلید کش قرار میگیرد void SetAll<T>(IDictionary<string, T> value, TimeSpan expiration); Task SetAllAsync<T>(IDictionary<string, T> value, TimeSpan expiration); // تنظیم یک کش با کلید - مقدار - زمان انقضا // اگر کلیدی همنام وجود داشته باشد مقدار نادرست و در غیر اینصورت مقدار نادرست را برمیگرداند bool TrySet<T>(string cacheKey, T cacheValue, TimeSpan expiration); Task<bool> TrySetAsync<T>(string cacheKey, T cacheValue, TimeSpan expiration); // گرفتن یک کش با کلید CacheValue<T> Get<T>(string cacheKey); Task<CacheValue<T>> GetAsync<T>(string cacheKey); // CacheValue<T> Get<T>(string cacheKey, Func<T> dataRetriever, TimeSpan expiration); Task<CacheValue<T>> GetAsync<T>(string cacheKey, Func<Task<T>> dataRetriever, TimeSpan expiration); // گرفتن یک کش با چند کاراکتر پیشین کلید آن // برای مثال یک کلید با نام // MyKey // تنها با داشتن چند حرف اول // MyK // میتوانیم این کش را دریافت کنیم IDictionary<string, CacheValue<T>> GetByPrefix<T>(string prefix); Task<IDictionary<string, CacheValue<T>>> GetByPrefixAsync<T>(string prefix); // IDictionary<string, CacheValue<T>> GetAll<T>(IEnumerable<string> cacheKeys); Task<IDictionary<string, CacheValue<T>>> GetAllAsync<T>(IEnumerable<string> cacheKeys); // گرفتن تعداد کشهای با کاراکترهای پیشین کلید که میان چند کلید یکسان است int GetCount(string prefix = ""); Task<int> GetCountAsync(string prefix = ""); // گرفتن زمان انقضا باقیمانده از یک کش با کلید آن TimeSpan GetExpiration(string cacheKey); Task<TimeSpan> GetExpirationAsync(string cacheKey); // حذف کردن یک کش با کلید void Remove(string cacheKey); Task RemoveAsync(string cacheKey); // حذف کردن یک کش با چند کاراکتر پیشین کلید void RemoveByPrefix(string prefix); Task RemoveByPrefixAsync(string prefix); // حذف کردن چند کش با لیستی از کلیدها void RemoveAll(IEnumerable<string> cacheKeys); Task RemoveAllAsync(IEnumerable<string> cacheKeys); // بررسی وجود یا عدم وجود یک کش با کلید bool Exists(string cacheKey); Task<bool> ExistsAsync(string cacheKey); // حذف کردن همه کشها void Flush(); Task FlushAsync();
Install-Package EasyCaching.Redis
services.AddEasyCaching(option => { option.UseRedis(config => { config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379)); }); });
// حذف کردن یک کلید در صورت وجود bool KeyDel(string cacheKey); Task<bool> KeyDelAsync(string cacheKey); // تنظیم تاریخ انتضا به یک کلید موجود بر حسب ثانیه bool KeyExpire(string cacheKey, int second); Task<bool> KeyExpireAsync(string cacheKey, int second); // بررسی وجود یا عدم وجود یک کلید bool KeyExists(string cacheKey); Task<bool> KeyExistsAsync(string cacheKey); // گرفتن زمان انتقضا باقیمانده یک کلید long TTL(string cacheKey); Task<long> TTLAsync(string cacheKey); // جستجو بین همه کلیدها براساس فیلتر شامل بودن نام کلید از مقدار ورودی List<string> SearchKeys(string cacheKey, int? count = null);
// افزودن یک عدد (پیشقرض 1) به مقدار نوع عددی یک کلید long IncrBy(string cacheKey, long value = 1); Task<long> IncrByAsync(string cacheKey, long value = 1); // افزودن یک عدد (پیشقرض 1) به مقدار نوع عددی یک کلید double IncrByFloat(string cacheKey, double value = 1); Task<double> IncrByFloatAsync(string cacheKey, double value = 1); // تنظیم یک کلید و مقدار وقتی مقدار از نوع رشته باشد bool StringSet(string cacheKey, string cacheValue, TimeSpan? expiration = null, string when = ""); Task<bool> StringSetAsync(string cacheKey, string cacheValue, TimeSpan? expiration = null, string when = ""); // گرفتن کلید و مقدار آن وقتی مقدار از نوع رشته باشد string StringGet(string cacheKey); Task<string> StringGetAsync(string cacheKey); // گرفتن تعداد کاراکترهای مقدار یک کلید وقتی مقدار از نوع رشته باشد long StringLen(string cacheKey); Task<long> StringLenAsync(string cacheKey); // جایگزاری یک رشته درون رشته مقدار یک کلید بعد از شماره کاراکتر مشخص شده در ورودی برای مثال // "Hello World" // 6 , jack // "Hello jack" long StringSetRange(string cacheKey, long offest, string value); Task<long> StringSetRangeAsync(string cacheKey, long offest, string value); // گرفتن یک بازه از رشته مقدار یک کلید با شماره کاراکتر شروع و پایان string StringGetRange(string cacheKey, long start, long end); Task<string> StringGetRangeAsync(string cacheKey, long start, long end);
// شما میتوانید دو کلید با نامهای یکسان داشته باشید که در کلید تایپ دیکشنری مقدار خود باهم متفاوت هستند bool HMSet(string cacheKey, Dictionary<string, string> vals, TimeSpan? expiration = null); Task<bool> HMSetAsync(string cacheKey, Dictionary<string, string> vals, TimeSpan? expiration = null); // شما میتوانید دو کلید با نامهای یکسان داشته باشید که در ورودی فیلد باهم متفاوت هستند bool HSet(string cacheKey, string field, string cacheValue); Task<bool> HSetAsync(string cacheKey, string field, string cacheValue); // بررسی وجود یا عدم وجود یک کلید و فیلد bool HExists(string cacheKey, string field); Task<bool> HExistsAsync(string cacheKey, string field); // حذف کردن کلیدهای همنام موجود با همه فیلدهای متفاوت در حالت پیشفرض مگر اینکه کلید و نام فیلد را بهمراه آن مشخص کنید long HDel(string cacheKey, IList<string> fields = null); Task<long> HDelAsync(string cacheKey, IList<string> fields = null); // گرفتن مقدار با نام کلید و نام فیلد string HGet(string cacheKey, string field); Task<string> HGetAsync(string cacheKey, string field); // گرفتن فیلد و مقدار با کلید Dictionary<string, string> HGetAll(string cacheKey); Task<Dictionary<string, string>> HGetAllAsync(string cacheKey); // افزودن یک عدد (پیشقرض 1) به مقدار نوع عددی یک کلید و فیلد long HIncrBy(string cacheKey, string field, long val = 1); Task<long> HIncrByAsync(string cacheKey, string field, long val = 1); // گرفتن فیلدهای متفاوت یک کلید List<string> HKeys(string cacheKey); Task<List<string>> HKeysAsync(string cacheKey); // گرفتن تعداد فیلدهای متفاوت یک کلید long HLen(string cacheKey); Task<long> HLenAsync(string cacheKey); // گرفتن مقادیر یک کلید بدون در نظر گرفتن فیلدهای متفاوت List<string> HVals(string cacheKey); Task<List<string>> HValsAsync(string cacheKey); // گرفتن مقدار دیکشنری با کلید و نام فیلدها Dictionary<string, string> HMGet(string cacheKey, IList<string> fields); Task<Dictionary<string, string>> HMGetAsync(string cacheKey, IList<string> fields);
// گرفتن یک مقدار از لیست مقادیر با شماره ایندکس آن T LIndex<T>(string cacheKey, long index); Task<T> LIndexAsync<T>(string cacheKey, long index); // گرفتن تعداد مقادیر در لیست یک کلید long LLen(string cacheKey); Task<long> LLenAsync(string cacheKey); // گرفتن اولین مقدار از مقادیر یک لیست در یک کلید T LPop<T>(string cacheKey); Task<T> LPopAsync<T>(string cacheKey); // ایجاد یک کلید که لیستی از مقادیر را پشتیبانی میکند و میتوانید هر بار مقدار جدید به لیست آن اضافه کنید long LPush<T>(string cacheKey, IList<T> cacheValues); Task<long> LPushAsync<T>(string cacheKey, IList<T> cacheValues); // گرفتن مقادیر یک لیست از داده بر اساس شماره ایندکس شروع و پایان برای مثال مقادیر ۳ تا ۷ از ۱۰ مقدار List<T> LRange<T>(string cacheKey, long start, long stop); Task<List<T>> LRangeAsync<T>(string cacheKey, long start, long stop); // حذف کردن مقادیر یک لیست بر اساس تعداد وارد شده که بعد از مقدار وارد شده شروع به شمارش میشود long LRem<T>(string cacheKey, long count, T cacheValue); Task<long> LRemAsync<T>(string cacheKey, long count, T cacheValue); // افزودن یک مقدار به لیستی از مقادیر یک کلید با گرفتن شماره ایندکس bool LSet<T>(string cacheKey, long index, T cacheValue); Task<bool> LSetAsync<T>(string cacheKey, long index, T cacheValue); // بررسی میکند که لیست مقداری برای شماره ایندکس شروع و پایان درون خودش دارد یا خیر bool LTrim(string cacheKey, long start, long stop); Task<bool> LTrimAsync(string cacheKey, long start, long stop); // https://redis.io/commands/lpushx long LPushX<T>(string cacheKey, T cacheValue); Task<long> LPushXAsync<T>(string cacheKey, T cacheValue); // https://redis.io/commands/linsert long LInsertBefore<T>(string cacheKey, T pivot, T cacheValue); Task<long> LInsertBeforeAsync<T>(string cacheKey, T pivot, T cacheValue); // https://redis.io/commands/linsert long LInsertAfter<T>(string cacheKey, T pivot, T cacheValue); Task<long> LInsertAfterAsync<T>(string cacheKey, T pivot, T cacheValue); // https://redis.io/commands/rpushx long RPushX<T>(string cacheKey, T cacheValue); Task<long> RPushXAsync<T>(string cacheKey, T cacheValue); // https://redis.io/commands/rpush long RPush<T>(string cacheKey, IList<T> cacheValues); Task<long> RPushAsync<T>(string cacheKey, IList<T> cacheValues); // https://redis.io/commands/rpop T RPop<T>(string cacheKey); Task<T> RPopAsync<T>(string cacheKey);
// https://redis.io/commands/SAdd long SAdd<T>(string cacheKey, IList<T> cacheValues, TimeSpan? expiration = null); Task<long> SAddAsync<T>(string cacheKey, IList<T> cacheValues, TimeSpan? expiration = null); // https://redis.io/commands/SCard long SCard(string cacheKey); Task<long> SCardAsync(string cacheKey); // https://redis.io/commands/SIsMember bool SIsMember<T>(string cacheKey, T cacheValue); Task<bool> SIsMemberAsync<T>(string cacheKey, T cacheValue); // https://redis.io/commands/SMembers List<T> SMembers<T>(string cacheKey); Task<List<T>> SMembersAsync<T>(string cacheKey); // https://redis.io/commands/SPop T SPop<T>(string cacheKey); Task<T> SPopAsync<T>(string cacheKey); // https://redis.io/commands/SRandMember List<T> SRandMember<T>(string cacheKey, int count = 1); Task<List<T>> SRandMemberAsync<T>(string cacheKey, int count = 1); // https://redis.io/commands/SRem long SRem<T>(string cacheKey, IList<T> cacheValues = null); Task<long> SRemAsync<T>(string cacheKey, IList<T> cacheValues = null);
// https://redis.io/commands/ZAdd long ZAdd<T>(string cacheKey, Dictionary<T, double> cacheValues); Task<long> ZAddAsync<T>(string cacheKey, Dictionary<T, double> cacheValues); // https://redis.io/commands/ZCard long ZCard(string cacheKey); Task<long> ZCardAsync(string cacheKey); // https://redis.io/commands/ZCount long ZCount(string cacheKey, double min, double max); Task<long> ZCountAsync(string cacheKey, double min, double max); // https://redis.io/commands/ZIncrBy double ZIncrBy(string cacheKey, string field, double val = 1); Task<double> ZIncrByAsync(string cacheKey, string field, double val = 1); // https://redis.io/commands/ZLexCount long ZLexCount(string cacheKey, string min, string max); Task<long> ZLexCountAsync(string cacheKey, string min, string max); // https://redis.io/commands/ZRange List<T> ZRange<T>(string cacheKey, long start, long stop); Task<List<T>> ZRangeAsync<T>(string cacheKey, long start, long stop); // https://redis.io/commands/ZRank long? ZRank<T>(string cacheKey, T cacheValue); Task<long?> ZRankAsync<T>(string cacheKey, T cacheValue); // https://redis.io/commands/ZRem long ZRem<T>(string cacheKey, IList<T> cacheValues); Task<long> ZRemAsync<T>(string cacheKey, IList<T> cacheValues); // https://redis.io/commands/ZScore double? ZScore<T>(string cacheKey, T cacheValue); Task<double?> ZScoreAsync<T>(string cacheKey, T cacheValue);
// https://redis.io/commands/PfAdd bool PfAdd<T>(string cacheKey, List<T> values); Task<bool> PfAddAsync<T>(string cacheKey, List<T> values); // https://redis.io/commands/PfCount long PfCount(List<string> cacheKeys); Task<long> PfCountAsync(List<string> cacheKeys); // https://redis.io/commands/PfMerge bool PfMerge(string destKey, List<string> sourceKeys); Task<bool> PfMergeAsync(string destKey, List<string> sourceKeys);
// https://redis.io/commands/GeoAdd long GeoAdd(string cacheKey, List<(double longitude, double latitude, string member)> values); Task<long> GeoAddAsync(string cacheKey, List<(double longitude, double latitude, string member)> values); // https://redis.io/commands/GeoDist double? GeoDist(string cacheKey, string member1, string member2, string unit = "m"); Task<double?> GeoDistAsync(string cacheKey, string member1, string member2, string unit = "m"); // https://redis.io/commands/GeoHash List<string> GeoHash(string cacheKey, List<string> members); Task<List<string>> GeoHashAsync(string cacheKey, List<string> members); // https://redis.io/commands/GeoPos List<(decimal longitude, decimal latitude)?> GeoPos(string cacheKey, List<string> members); Task<List<(decimal longitude, decimal latitude)?>> GeoPosAsync(string cacheKey, List<string> members);
Install-Package EasyCaching.HybridCache Install-Package EasyCaching.InMemory Install-Package EasyCaching.Redis Install-Package EasyCaching.Bus.Redis
services.AddEasyCaching(option => // local option.UseInMemory("c1"); // distributed option.UseRedis(config => config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379)); }, "c2"); // combine local and distributed option.UseHybrid(config => // specify the local cache provider name after v0.5.4 config.LocalCacheProviderName = "c1" // specify the distributed cache provider name after v0.5.4 config.DistributedCacheProviderName = "c2" }); // use redis bus .WithRedisBus(busConf => busConf.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379)); }); });
let name: string; name = "Vahid"; // OK name = null; // OK name = undefined; // OK
let age: number; age = 24; // OK age = null; // OK age = undefined; // OK
{ "compilerOptions": { "strictNullChecks": true } }
let foo: string = null; // Error! Type 'null' is not assignable to type 'string'.
Uncaught ReferenceError: foo is not defined Uncaught TypeError: window.foo is not a function
// With strictNullChecks set to false let d: Array<number> = [null, undefined, 10, 15]; //OK let e: Array<string> = ["pie", null, ""]; //OK // With strictNullChecks set to true let d: Array<number> = [null, undefined, 10, 15]; // Error let e: Array<string> = ["pie", null, ""]; // Error
{ "compilerOptions": { "strict": true /* Enable all strict type-checking options. */ } }
{ "compilerOptions": { "strict": true, "noImplicitThis": false } }
let foo: string | null = null; // Okay!
let name: string | null; name = "Vahid"; // OK name = null; // OK name = undefined; // Error
let z = null;
testAssignedBeforeUseChecking() { let x: number; console.log(x); }
[ts] Variable 'x' is used before being assigned.
let z: number | undefined; console.log(z);
method1(x?: number) { }
method1(x?: number | undefined) { }
f(x: number): string { return x.toString(); } testTypeGuards() { let x: number | null | undefined; if (x) { this.f(x); // Ok, type of x is number here } else { this.f(x); // Error, type of x is number? here } }
Argument of type 'number | null | undefined' is not assignable to parameter of type 'number'. Type 'undefined' is not assignable to type 'number'.
getLength(s: string | null) { return s ? s.length : 0; }
isNumber(n: any): n is number { // type guard return typeof n === "number"; }
usedMb(usedBytes?: number): number | undefined { return this.isNumber(usedBytes) ? (usedBytes / (1024 * 1024)) : undefined; }
usedMb2(usedBytes?: number): number | undefined { return usedBytes ? (usedBytes / (1024 * 1024)) : undefined; }
formatUsedMb(): string { //ERROR: TS2531: Object is possibly undefined return this.usedMb(123).toFixed(0).toString(); }
formatUsed(): string { const usedMb = this.usedMb(123); return usedMb ? usedMb.toFixed(0).toString() : ""; }
export interface User { name: string; age?: number; }
printUserInfo(user: User) { if (user.age != null) { console.log(`${user.name}, ${user.age.toString()}`); } }
printUserInfo(user: User) { console.log(`${user.name}, ${user.age.toString()}`); }
printUserInfo(user: User) { console.log(`${user.name}, ${user.age!.toString()}`); }
[tslint] Forbidden non null assertion (no-non-null-assertion)
dotnet new webapi -o SampleApi
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" /> <PackageReference Include="Microsoft.AspNetCore.OData" Version="7.4.0" />
using Microsoft.AspNet.OData.Extensions;
services.AddControllers().AddNewtonsoftJson(); // Add .AddNewtonsoftJson() to services.AddControllers(); services.AddOData(); // add this new line
app.UseEndpoints(endpoints => // Existing code { endpoints.MapControllers(); // Existing code endpoints.EnableDependencyInjection(); // Add this new line endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(20); // Add this new line });
// replace: [HttpGet] // Existing code // with: [HttpGet, EnableQuery] // new code
public class Customer { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Gender Gender { get; set; } public AddressInfo Address { get; set; } } public class AddressInfo { public int StreetNo { get; set; } public string PostalCode { get; set; } } public enum Gender { Man, Woman, Other }
?$filter=Gender eq 'Woman'
?$filter=Gender eq 'Woman'&$orderby=Address/StreetNo
?$filter=contains(FirstName,'Ali')
«... در این قسمت پادکست، من با شایان صالحیان، مهندس ارشد ماشین لرنینگ در توییتر حرف زدم. شایان در توییتر تیمی رو مدیریت میکنه که مسئول الگوریتمهای تایم لاین و سیستم پیشنهاد دهی توییتر هستند. شایان قبلا در شرکت پیپال مشغول به کارآموزی بوده و شریف مهندسی کامپیوتر خونده و تجربه کار کردن با ایلان ماسک و جک دورسی رو داشته. با شایان از تجربیات اپلای کردن تو توییتر و نحوه مصاحبه پرسیدم و کلی در رابطه با اخراجهای گسترده توییتر و نحوه مدیریتی ایلان ماسک حرف زدیم ...»
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>TypeScript HTML App</title> <link rel="stylesheet" href="app.css" type="text/css" /> <script src="app.js"></script> </head> <body> <h1>Number Type in TypeScript</h1> <div id="content"/> </body> </html>
--target ES5
در قسمت بعد، قالب را هم از نوع empty انتخاب مینماییم.
در ادامه فایل project.json را باز کرده و در قسمت dependencies، تغییرات زیر را اعمال نمایید.
قبل از اینکه شما را از این همه وابستگی نگران کنم، باید عرض کنم فقط Microsoft.Owin , Microsoft.AspNetCore.Owin، پکیجهای اجباری هستند؛ باقی آنها برای نشان دادن انعطاف پذیری بالای این روش میباشند:
"dependencies": { "Microsoft.AspNet.OData": "5.9.1", "Microsoft.AspNet.SignalR": "2.2.1", "Microsoft.AspNet.WebApi.Client": "5.2.3", "Microsoft.AspNet.WebApi.Core": "5.2.3", "Microsoft.AspNet.WebApi.Owin": "5.2.3", "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Hosting": "1.0.0", "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Owin": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Net.Http": "2.2.29", "Microsoft.Owin": "3.0.1", "Microsoft.Owin.Diagnostics": "3.0.1", "Microsoft.Owin.FileSystems": "3.0.1", "Microsoft.Owin.StaticFiles": "3.0.1", "Newtonsoft.Json": "9.0.1" }, //etc...
بعد از ذخیره کردن این فایل، در پنجرهی Output خود شاهد دانلود شدن این پکیجها خواهید بود. در اینجا پکیجهای مربوط به Owin, Odata, SignalR را مشاهد میکنید. ضمن اینکه در کنار آن، AspNetCore.Mvc را نیز مشاهده میفرمایید. دلیل این کار این است که این دو نوع متفاوت قرار است در کنار هم کار کنند و هیچ مشکلی با دیگری ندارند.
در مسیر اصلی پروژهی خود کلاسی به نام OwinExtensions را با محتوای زیر بسازید:
namespace OwinCore { public static class OwinExtensions { public static IApplicationBuilder UseOwinApp( this IApplicationBuilder aspNetCoreApp, Action<IAppBuilder> configuration) { return aspNetCoreApp.UseOwin(setup => setup(next => { AppBuilder owinAppBuilder = new AppBuilder(); IApplicationLifetime aspNetCoreLifetime = (IApplicationLifetime)aspNetCoreApp.ApplicationServices.GetService(typeof(IApplicationLifetime)); AppProperties owinAppProperties = new AppProperties(owinAppBuilder.Properties); owinAppProperties.OnAppDisposing = aspNetCoreLifetime?.ApplicationStopping ?? CancellationToken.None; owinAppProperties.DefaultApp = next; configuration(owinAppBuilder); return owinAppBuilder.Build<Func<IDictionary<string, object>, Task>>(); })); } } }
یک Extension Method به نام UseOwinApp اضافه شده به IApplicationBuilder که مربوط به ASP.NET Core میباشد و درون آن نیز AppBuilder را که مربوط به Owin pipeline میباشد، نمونه سازی کردهایم که باعث میشود Owin pipeline بر روی ASP.NET Core pipeline سوار شود.
حال میخواهیم یک Middleware سفارشی را با استفاده از Owin نوشته و در Startup پروژه، آن را فراخوانی نماییم. کلاسی به نام AddSampleHeaderToResponseHeadersOwinMiddleware را با محتوای زیر تولید مینماییم:
namespace OwinCore { public class AddSampleHeaderToResponseHeadersOwinMiddleware : OwinMiddleware { public AddSampleHeaderToResponseHeadersOwinMiddleware(OwinMiddleware next) : base(next) { } public async override Task Invoke(IOwinContext context) { //throw new InvalidOperationException("ErrorTest"); context.Response.Headers.Add("Test", new[] { context.Request.Uri.ToString() }); await Next.Invoke(context); } } }
کلاسی است که از owinMiddleware ارث بری کرده و در متد override شدهی Invoke نیز با استفاده از IOwinContext، به پیاده سازی Middleware خود میپردازیم. Exception مربوطه را comment کرده (بعدا در مرحلهی تست از آن نیز استفاده مینماییم) و در خط بعدی در هدر response هر request، یک شیء را به نام Test و با مقدار Uri آن request، میسازیم.
خط بعدی هم اعلام میدارد که به Middleware بعدی برود.
در ادامه فایل Startup.cs را باز کرده و اینگونه متد Configure را تغییر دهید:
public void Configure(IApplicationBuilder aspNetCoreApp, IHostingEnvironment env) { aspNetCoreApp.UseOwinApp(owinApp => { if (env.IsDevelopment()) { owinApp.UseErrorPage(new ErrorPageOptions() { ShowCookies = true, ShowEnvironment = true, ShowExceptionDetails = true, ShowHeaders = true, ShowQuery = true, ShowSourceCode = true }); } owinApp.Use<AddSampleHeaderToResponseHeadersOwinMiddleware>(); }); }
مشاهده میفرمایید با استفاده از UserOwinApp میتوانیم Middlewareهای Owinی خود را register نماییم و نکتهی قابل توجه این است که در کنار آن نیز میتوانیم از IHostingEnviroment مربوط به ASP.NET core استفاده نماییم. owinApp.UseErrorPage از Microsoft.Owin.Diagnostics گرفته شده است و در خط بعدی نیز Middleware شخصی خود را register کردهایم. پروژه را run کرده و در response این را مشاهد مینمایید.
اکنون اگر در Middleware سفارشی خود، آن Exception را از حالت comment در بیاوریم، در صورتیکه در حالت development باشیم، با این صفحه مواجه خواهیم شد:
Exception مربوطه را به حالت comment گذاشته و ادامه میدهیم.
برای اینکه نشان دهیم Owin و ASP.NET Core pipeline در کنار هم میتوانند کار کنند، یک Middleware را از نوع ASP.NET Core نوشته و آن را register مینماییم. کلاسی جدیدی را به نام AddSampleHeaderToResponseHeadersAspNetCoreMiddlware با محتوای زیر میسازیم:
namespace OwinCore { public class AddSampleHeaderToResponseHeadersAspNetCoreMiddlware { private readonly RequestDelegate Next; public AddSampleHeaderToResponseHeadersAspNetCoreMiddlware(RequestDelegate next) { Next = next; } public async Task Invoke(HttpContext context) { //throw new InvalidOperationException("ErrorTest"); context.Response.Headers.Add("Test2", new[] { "some text" }); await Next.Invoke(context); } } }
متد Configure در Startup.cs را نیز اینگونه تغییر میدهیم
public void Configure(IApplicationBuilder aspNetCoreApp, IHostingEnvironment env) { aspNetCoreApp.UseOwinApp(owinApp => { if (env.IsDevelopment()) { owinApp.UseErrorPage(new ErrorPageOptions() { ShowCookies = true, ShowEnvironment = true, ShowExceptionDetails = true, ShowHeaders = true, ShowQuery = true, ShowSourceCode = true }); } owinApp.Use<AddSampleHeaderToResponseHeadersOwinMiddleware>(); }); aspNetCoreApp.UseMiddleware<AddSampleHeaderToResponseHeadersAspNetCoreMiddlware>(); }
اکنون AddSampleHeaderToResponseHeadersAspNetCoreMiddlware رجیستر شده است و بعد از run کردن پروژه و بررسی header response باید این را ببینیم
میبینید که به ترتیب اجرای Middlewareها، ابتدا Test مربوط به Owin و بعد آن Test2 مربوط به ASP.NET Core تولید شده است.
حال اجازه دهید Odata را با استفاده از Owin پیاده سازی نماییم. ابتدا کلاسی را به نام Product با محتوای زیر تولید نمایید:
namespace OwinCore { public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } }
حال کلاسی را به نام ProductsController با محتوای زیر میسازیم:
namespace OwinCore { public class ProductsController : ODataController { [EnableQuery] public IQueryable<Product> Get() { return new List<Product> { new Product { Id = 1, Name = "Test" , Price = 10 } } .AsQueryable(); } } }
اگر مقالهی پیاده سازی Crud با استفاده از OData را مطالعه کرده باشید، قاعدتا با این کدها آشنا خواهید بود. ضمن اینکه پرواضح است که OData هیچ وابستگی به entity framework ندارد.
برای config آن نیز در Startup.cs پروژه و متد Configure، تغییرات زیر را اعمال مینماییم.
public void Configure(IApplicationBuilder aspNetCoreApp, IHostingEnvironment env) { //aspNetCoreApp.UseMvc(); aspNetCoreApp.UseOwinApp(owinApp => { if (env.IsDevelopment()) { owinApp.UseErrorPage(new ErrorPageOptions() { ShowCookies = true, ShowEnvironment = true, ShowExceptionDetails = true, ShowHeaders = true, ShowQuery = true, ShowSourceCode = true }); } // owinApp.UseFileServer(); as like as asp.net core static files middleware // owinApp.UseStaticFiles(); as like as asp.net core static files middleware // owinApp.UseWebApi(); asp.net web api / odata / web hooks HttpConfiguration webApiConfig = new HttpConfiguration(); ODataModelBuilder odataMetadataBuilder = new ODataConventionModelBuilder(); odataMetadataBuilder.EntitySet<Product>("Products"); webApiConfig.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: "odata", model: odataMetadataBuilder.GetEdmModel()); owinApp.UseWebApi(webApiConfig); owinApp.MapSignalR(); //owinApp.Use<AddSampleHeaderToResponseHeadersOwinMiddleware>(); }); //aspNetCoreApp.UseMiddleware<AddSampleHeaderToResponseHeadersAspNetCoreMiddlware>(); }
برای config مخصوص Odata، به HttpConfiguration نیاز داریم. بنابراین instanceی از آن گرفته و برای مسیریابی Odata از آن استفاده مینماییم.
با استفاده از پیاده سازی که از استاندارد Owin انجام دادیم، مشاهده کردید که Odata را همانند یک پروژهی معمولی asp.netی، config نمودیم. در خط بعدی هم SignalR را مشاهده مینمایید.
اکنون اگر آدرس زیر را در مرورگر خود وارد نمایید، پاسخ زیر را از Odata دریافت خواهید کرد:
http://localhost:YourPort/odata/Products
بعد از فرستادن request فوق، باید response زیر را دریافت نمایید:
{ "@odata.context":"http://localhost:4675/odata/$metadata#Products","value":[ { "Id":1,"Name":"Test","Price":10 } ] }
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); } }
public class VideoStream { private readonly string _filename; private long _contentLength; public long FileLength { get { return _contentLength; } } public VideoStream(string videoPath) { _filename = videoPath; using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { _contentLength = video.Length; } } public async void WriteToStream(Stream outputStream, HttpContent content, TransportContext context) { try { var buffer = new byte[65536]; using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { var length = (int)video.Length; var bytesRead = 1; while (length > 0 && bytesRead > 0) { bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length)); await outputStream.WriteAsync(buffer, 0, bytesRead); length -= bytesRead; } } } catch (HttpException) { return; } finally { outputStream.Close(); } } }
while (length > 0 && bytesRead > 0) { bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length)); await outputStream.WriteAsync(buffer, 0, bytesRead); length -= bytesRead; }
public class VideoController : ApiController { [Route("api/video/{ext}/{fileName}")] public HttpResponseMessage Get(string ext, string fileName) { string videoPath = HostingEnvironment.MapPath(string.Format("~/Videos/{0}.{1}", fileName, ext)); if (File.Exists(videoPath)) { FileInfo fi = new FileInfo(videoPath); var video = new VideoStream(videoPath); var response = Request.CreateResponse(); response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)video.WriteToStream, new MediaTypeHeaderValue("video/" + ext)); response.Content.Headers.Add("Content-Disposition", "attachment;filename=" + fi.Name.Replace(" ", "")); response.Content.Headers.Add("Content-Length", video.FileLength.ToString()); return response; } else { return Request.CreateResponse(HttpStatusCode.NotFound); } } }
[Route("api/video/{ext}/{fileName}")]
api/video/mp4/sample
string videoPath = HostingEnvironment.MapPath(string.Format("~/Videos/{0}.{1}", fileName, ext));
var video = new VideoStream(videoPath);
var response = Request.CreateResponse(); response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)video.WriteToStream, new MediaTypeHeaderValue("video/" + ext));
response.Content.Headers.Add("Content-Disposition", "attachment;filename=" + fileName); response.Content.Headers.Add("Content-Length", video.FileLength.ToString());
if(File.Exists(videoPath)) { // removed for bravity } else { return Request.CreateResponse(HttpStatusCode.NotFound); }
public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } }
<div> <div> <video width="480" height="270" controls="controls" preload="auto"> <source src="/api/video/mp4/sample" type="video/mp4" /> Your browser does not support the video tag. </video> </div> </div>