- فعلا از طریق ویژگی فوق پشتیبانی نمیشود.
- فعلا از طریق ویژگی فوق پشتیبانی نمیشود.
[Route("working")] public ActionResult Working() { using (var connection = new SqlConnection(_connectionString)) { try { connection.Open(); } catch (SqlException) { return new HttpStatusCodeResult(503, "Generic error"); } } return new EmptyResult(); }
namespace MvcHealthCheckTest { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddHealthChecks() .AddCheck("sql", () => { using (var connection = new SqlConnection(Configuration["connectionString"])) { try { connection.Open(); } catch (SqlException) { return HealthCheckResult.Unhealthy(); } } return HealthCheckResult.Healthy(); }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseHealthChecks("/working");
public class SqlServerHealthCheck : IHealthCheck { private readonly IConfiguration _configuration; public SqlServerHealthCheck(IConfiguration configuration) { _configuration = configuration; } public Task<HealthCheckResult> CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) { using (var connection = new SqlConnection(_configuration["connectionString"])) { try { connection.Open(); } catch (SqlException) { return Task.FromResult(HealthCheckResult.Unhealthy()); } } return Task.FromResult(HealthCheckResult.Healthy()); } }
namespace MvcHealthCheckTest { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddHealthChecks() .AddCheck<SqlServerHealthCheck>("sql");
public Task<HealthCheckResult> CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) { using (var connection = new SqlConnection(_configuration["connectionString"])) { try { connection.Open(); } catch (SqlException) { return Task.FromResult(new HealthCheckResult( status: context.Registration.FailureStatus, description: "It is dead!")); } } return Task.FromResult(HealthCheckResult.Healthy("Healthy as a horse")); }
namespace MvcHealthCheckTest { public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseHealthChecks("/working", new HealthCheckOptions { ResponseWriter = async (context, report) => { var result = JsonConvert.SerializeObject(new { status = report.Status.ToString(), errors = report.Entries.Select(e => new { key = e.Key, value = Enum.GetName(typeof(HealthStatus), e.Value.Status) }) }); context.Response.ContentType = MediaTypeNames.Application.Json; await context.Response.WriteAsync(result); } });
public static void Main() { var li = Enumerable.Range(1, 10).ToList(); var sb = new StringBuilder(); //for (int i=0,j=1;;) //در اینجا میشه نوعهای متفاوتی تعریف کرد for ( (int i, bool first) = (0, true); i < li.Count; i++, first = false) { if (!first) sb.Append(", "); sb.Append(li[i]); } Console.WriteLine(sb.ToString()); }
در قسمت قبلی بروز رسانی موجودیتهای منفصل با WCF را بررسی کردیم. در این قسمت خواهیم دید چگونه میتوان تغییرات موجودیتها را تشخیص داد و عملیات CRUD را روی یک Object Graph اجرا کرد.
فرض کنید میخواهیم از سرویسهای Web API برای انجام عملیات CRUD استفاده کنیم، اما بدون آنکه برای هر موجودیت متدهایی مجزا تعریف کنیم. به بیان دیگر میخواهیم عملیات مذکور را روی یک Object Graph انجام دهیم. مدیریت دادهها هم با مدل Code-First پیاده سازی میشود. در مثال جاری یک اپلیکیشن کنسول خواهیم داشت که بعنوان یک کلاینت سرویس را فراخوانی میکند. هر پروژه نیز در Solution مجزایی قرار دارد، تا یک محیط n-Tier را شبیه سازی کنیم.
مدل زیر را در نظر بگیرید.
همانطور که میبینید مدل ما آژانسهای مسافرتی و رزرواسیون آنها را ارائه میکند. میخواهیم مدل و کد دسترسی دادهها را در یک سرویس Web API پیاده سازی کنیم تا هر کلاینتی که به HTTP دسترسی دارد بتواند عملیات CRUD را انجام دهد. برای ساختن سرویس مورد نظر مراحل زیر را دنبال کنید:
public class TravelAgent { public TravelAgent() { this.Bookings = new HashSet<Booking>(); } public int AgentId { get; set; } public string Name { get; set; } public virtual ICollection<Booking> Bookings { get; set; } } public class Booking { public int BookingId { get; set; } public int AgentId { get; set; } public string Customer { get; set; } public DateTime BookingDate { get; set; } public bool Paid { get; set; } public virtual TravelAgent TravelAgent { get; set; } }
public class Recipe3Context : DbContext { public Recipe3Context() : base("Recipe3ConnectionString") { } public DbSet<TravelAgent> TravelAgents { get; set; } public DbSet<Booking> Bookings { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<TravelAgent>().HasKey(x => x.AgentId); modelBuilder.Entity<TravelAgent>().ToTable("TravelAgents"); modelBuilder.Entity<Booking>().ToTable("Bookings"); } }
<connectionStrings> <add name="Recipe3ConnectionString" connectionString="Data Source=.; Initial Catalog=EFRecipes; Integrated Security=True; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>
protected void Application_Start() { // Disable Entity Framework Model Compatibilty Database.SetInitializer<Recipe1Context>(null); // The bidirectional navigation properties between related entities // create a self-referencing loop that breaks Web API's effort to // serialize the objects as JSON. By default, Json.NET is configured // to error when a reference loop is detected. To resolve problem, // simply configure JSON serializer to ignore self-referencing loops. GlobalConfiguration.Configuration.Formatters.JsonFormatter .SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; ... }
public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "ActionMethodSave", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }); }
public class TravelAgentController : ApiController { // GET api/travelagent [HttpGet] public IEnumerable<TravelAgent> Retrieve() { using (var context = new Recipe3Context()) { return context.TravelAgents.Include(x => x.Bookings).ToList(); } } /// <summary> /// Update changes to TravelAgent, implementing Action-Based Routing in Web API /// </summary> public HttpResponseMessage Update(TravelAgent travelAgent) { using (var context = new Recipe3Context()) { var newParentEntity = true; // adding the object graph makes the context aware of entire // object graph (parent and child entities) and assigns a state // of added to each entity. context.TravelAgents.Add(travelAgent); if (travelAgent.AgentId > 0) { // as the Id property has a value greater than 0, we assume // that travel agent already exists and set entity state to // be updated. context.Entry(travelAgent).State = EntityState.Modified; newParentEntity = false; } // iterate through child entities, assigning correct state. foreach (var booking in travelAgent.Bookings) { if (booking.BookingId > 0) // assume booking already exists if ID is greater than zero. // set entity to be updated. context.Entry(booking).State = EntityState.Modified; } context.SaveChanges(); HttpResponseMessage response; // set Http Status code based on operation type response = Request.CreateResponse(newParentEntity ? HttpStatusCode.Created : HttpStatusCode.OK, travelAgent); return response; } } [HttpDelete] public HttpResponseMessage Cleanup() { using (var context = new Recipe3Context()) { context.Database.ExecuteSqlCommand("delete from [bookings]"); context.Database.ExecuteSqlCommand("delete from [travelagents]"); } return Request.CreateResponse(HttpStatusCode.OK); } }
internal class Program { private HttpClient _client; private TravelAgent _agent1, _agent2; private Booking _booking1, _booking2, _booking3; private HttpResponseMessage _response; private static void Main() { Task t = Run(); t.Wait(); Console.WriteLine("\nPress <enter> to continue..."); Console.ReadLine(); } private static async Task Run() { var program = new Program(); program.ServiceSetup(); // do not proceed until clean-up is completed await program.CleanupAsync(); program.CreateFirstAgent(); // do not proceed until agent is created await program.AddAgentAsync(); program.CreateSecondAgent(); // do not proceed until agent is created await program.AddSecondAgentAsync(); program.ModifyAgent(); // do not proceed until agent is updated await program.UpdateAgentAsync(); // do not proceed until agents are fetched await program.FetchAgentsAsync(); } private void ServiceSetup() { // set up infrastructure for Web API call _client = new HttpClient {BaseAddress = new Uri("http://localhost:6687/")}; // add Accept Header to request Web API content negotiation to return resource in JSON format _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } private async Task CleanupAsync() { // call cleanup method in service _response = await _client.DeleteAsync("api/travelagent/cleanup/"); } private void CreateFirstAgent() { // create new Travel Agent and booking _agent1 = new TravelAgent {Name = "John Tate"}; _booking1 = new Booking { Customer = "Karen Stevens", Paid = false, BookingDate = DateTime.Parse("2/2/2010") }; _booking2 = new Booking { Customer = "Dolly Parton", Paid = true, BookingDate = DateTime.Parse("3/10/2010") }; _agent1.Bookings.Add(_booking1); _agent1.Bookings.Add(_booking2); } private async Task AddAgentAsync() { // call generic update method in Web API service to add agent and bookings _response = await _client.PostAsync("api/travelagent/update/", _agent1, new JsonMediaTypeFormatter()); if (_response.IsSuccessStatusCode) { // capture newly created travel agent from service, which will include // database-generated Ids for each entity _agent1 = await _response.Content.ReadAsAsync<TravelAgent>(); _booking1 = _agent1.Bookings.FirstOrDefault(x => x.Customer == "Karen Stevens"); _booking2 = _agent1.Bookings.FirstOrDefault(x => x.Customer == "Dolly Parton"); Console.WriteLine("Successfully created Travel Agent {0} and {1} Booking(s)", _agent1.Name, _agent1.Bookings.Count); } else Console.WriteLine("{0} ({1})", (int) _response.StatusCode, _response.ReasonPhrase); } private void CreateSecondAgent() { // add new agent and booking _agent2 = new TravelAgent {Name = "Perry Como"}; _booking3 = new Booking { Customer = "Loretta Lynn", Paid = true, BookingDate = DateTime.Parse("3/15/2010")}; _agent2.Bookings.Add(_booking3); } private async Task AddSecondAgentAsync() { // call generic update method in Web API service to add agent and booking _response = await _client.PostAsync("api/travelagent/update/", _agent2, new JsonMediaTypeFormatter()); if (_response.IsSuccessStatusCode) { // capture newly created travel agent from service _agent2 = await _response.Content.ReadAsAsync<TravelAgent>(); _booking3 = _agent2.Bookings.FirstOrDefault(x => x.Customer == "Loretta Lynn"); Console.WriteLine("Successfully created Travel Agent {0} and {1} Booking(s)", _agent2.Name, _agent2.Bookings.Count); } else Console.WriteLine("{0} ({1})", (int) _response.StatusCode, _response.ReasonPhrase); } private void ModifyAgent() { // modify agent 2 by changing agent name and assigning booking 1 to him from agent 1 _agent2.Name = "Perry Como, Jr."; _agent2.Bookings.Add(_booking1); } private async Task UpdateAgentAsync() { // call generic update method in Web API service to update agent 2 _response = await _client.PostAsync("api/travelagent/update/", _agent2, new JsonMediaTypeFormatter()); if (_response.IsSuccessStatusCode) { // capture newly created travel agent from service, which will include Ids _agent1 = _response.Content.ReadAsAsync<TravelAgent>().Result; Console.WriteLine("Successfully updated Travel Agent {0} and {1} Booking(s)", _agent1.Name, _agent1.Bookings.Count); } else Console.WriteLine("{0} ({1})", (int) _response.StatusCode, _response.ReasonPhrase); } private async Task FetchAgentsAsync() { // call Get method on service to fetch all Travel Agents and Bookings _response = _client.GetAsync("api/travelagent/retrieve").Result; if (_response.IsSuccessStatusCode) { // capture newly created travel agent from service, which will include Ids var agents = await _response.Content.ReadAsAsync<IEnumerable<TravelAgent>>(); foreach (var agent in agents) { Console.WriteLine("Travel Agent {0} has {1} Booking(s)", agent.Name, agent.Bookings.Count()); } } else Console.WriteLine("{0} ({1})", (int) _response.StatusCode, _response.ReasonPhrase); } }
اگر اپلیکیشن کنسول (کلاینت) را اجرا کنید با خروجی زیر مواجه خواهید شد.
(Successfully created Travel Agent John Tate and 2 Booking(s
(Successfully created Travel Agent Perry Como and 1 Booking(s
(Successfully updated Travel Agent Perry Como, Jr. and 2 Booking(s
(Travel Agent John Tate has 1 Booking(s
(Travel Agent Perry Como, Jr. has 2 Booking(s
با اجرای اپلیکیشن Web API شروع کنید. این اپلیکیشن یک کنترلر MVC Web Controller دارد که پس از اجرا شما را به صفحه خانه هدایت میکند. در این مرحله سایت در حال اجرا است و سرویسها قابل دسترسی هستند.
سپس اپلیکیشن کنسول را باز کنید، روی خط اول کد فایل program.cs یک breakpoint قرار دهید و آن را اجرا کنید. ابتدا آدرس سرویس Web API را نگاشت میکنیم و با تنظیم مقدار خاصیت Accept Header از سرویس درخواست میکنیم که اطلاعات را با فرمت JSON بازگرداند.
بعد از آن با استفاده از آبجکت HttpClient متد DeleteAsync را فراخوانی میکنیم که روی کنترلر TravelAgent تعریف شده است. این متد تمام دادههای پیشین را حذف میکند.
در قدم بعدی سه آبجکت جدید میسازیم: یک آژانس مسافرتی و دو رزرواسیون. سپس این آبجکتها را با فراخوانی متد PostAsync روی آبجکت HttpClient به سرویس ارسال میکنیم. اگر به متد Update در کنترلر TravelAgent یک breakpoint اضافه کنید، خواهید دید که این متد آبجکت آژانس مسافرتی را بعنوان یک پارامتر دریافت میکند و آن را به موجودیت TravelAgents در Context جاری اضافه مینماید. این کار آبجکت آژانس مسافرتی و تمام آبجکتهای فرزند آن را در حالت Added اضافه میکند و باعث میشود که context جاری شروع به ردیابی (tracking) آنها کند.
نکته: قابل ذکر است که اگر موجودیتهای متعددی با مقداری یکسان در خاصیت کلید اصلی (Primary-key value) دارید باید مجموعه آبجکتهای خود را Add کنید و نه Attach. در مثال جاری چند آبجکت Booking داریم که مقدار کلید اصلی آنها صفر است (Bookings with Id = 0). اگر از Attach استفاده کنید EF پیغام خطایی صادر میکند چرا که چند موجودیت با مقادیر کلید اصلی یکسان به context جاری اضافه کرده اید.
بعد از آن بر اساس مقدار خاصیت Id مشخص میکنیم که موجودیتها باید بروز رسانی شوند یا خیر. اگر مقدار این فیلد بزرگتر از صفر باشد، فرض بر این است که این موجودیت در دیتابیس وجود دارد بنابراین خاصیت EntityState را به Modified تغییر میدهیم. علاوه بر این فیلدی هم با نام newParentEntity تعریف کرده ایم که توسط آن بتوانیم کد وضعیت مناسبی به کلاینت بازگردانیم. در صورتی که مقدار فیلد Id در موجودیت TravelAgent برابر با یک باشد، مقدار خاصیت EntityState را به همان Added رها میکنیم.
سپس تمام آبجکتهای فرزند آژانس مسافرتی (رزرواسیون ها) را بررسی میکنیم و همین منطق را روی آنها اعمال میکنیم. یعنی در صورتی که مقدار فیلد Id آنها بزرگتر از 0 باشد وضعیت EntityState را به Modified تغییر میدهیم. در نهایت متد SaveChanges را فراخوانی میکنیم. در این مرحله برای موجودیتهای جدید اسکریپتهای Insert و برای موجودیتهای تغییر کرده اسکریپتهای Update تولید میشود. سپس کد وضعیت مناسب را به کلاینت بر میگردانیم. برای موجودیتهای اضافه شده کد وضعیت 201 (Created) و برای موجودیتهای بروز رسانی شده کد وضعیت 200 (OK) باز میگردد. کد 201 به کلاینت اطلاع میدهد که رکورد جدید با موفقیت ثبت شده است، و کد 200 از بروز رسانی موفقیت آمیز خبر میدهد. هنگام تولید سرویسهای REST-based بهتر است همیشه کد وضعیت مناسبی تولید کنید.
پس از این مراحل، آژانس مسافرتی و رزرواسیون جدیدی میسازیم و آنها را به سرویس ارسال میکنیم. سپس نام آژانس مسافرتی دوم را تغییر میدهیم، و یکی از رزرواسیونها را از آژانس اولی به آژانس دومی منتقل میکنیم. اینبار هنگام فراخوانی متد Update تمام موجودیتها شناسه ای بزرگتر از 1 دارند، بنابراین وضعیت EntityState آنها را به Modified تغییر میدهیم تا هنگام ثبت تغییرات دستورات بروز رسانی مناسب تولید و اجرا شوند.
در آخر کلاینت ما متد Retreive را روی سرویس فراخوانی میکند. این فراخوانی با کمک متد GetAsync انجام میشود که روی آبجکت HttpClient تعریف شده است. فراخوانی این متد تمام آژانسهای مسافرتی بهمراه رزرواسیونهای متناظرشان را دریافت میکند. در اینجا با استفاده از متد Include تمام رکوردهای فرزند را بهمراه تمام خاصیت هایشان (properties) بارگذاری میکنیم.
دقت کنید که مرتب کننده JSON تمام خواص عمومی (public properties) را باز میگرداند، حتی اگر در کد خود تعداد مشخصی از آنها را انتخاب کرده باشید.
نکته دیگر آنکه در مثال جاری از قراردادهای توکار Web API برای نگاشت درخواستهای HTTP به اکشن متدها استفاده نکرده ایم. مثلا بصورت پیش فرض درخواستهای POST به متدهایی نگاشت میشوند که نام آنها با "Post" شروع میشود. در مثال جاری قواعد مسیریابی را تغییر داده ایم و رویکرد مسیریابی RPC-based را در پیش گرفته ایم. در اپلیکیشنهای واقعی بهتر است از قواعد پیش فرض استفاده کنید چرا که هدف Web API ارائه سرویسهای REST-based است. بنابراین بعنوان یک قاعده کلی بهتر است متدهای سرویس شما به درخواستهای متناظر HTTP نگاشت شوند. و در آخر آنکه بهتر است لایه مجزایی برای میزبانی کدهای دسترسی داده ایجاد کنید و آنها را از سرویس Web API تفکیک نمایید.
var list = ctx.ProjectStatus.Select(...); foreach (var item in list) {...} foreach (var item in list) {...}
static void PassByValueSample() { int a = 1; PassByValue(a); Console.WriteLine($"after the invocation of {nameof(PassByValue)}, {nameof(a)} = {a}"); } static void PassByValue(int x) { x = 2; }
static void PassByReferenceSample() { int a = 1; PassByReference(ref a); Console.WriteLine($"after the invocation of {nameof(PassByReference)}, {nameof(a)} = {a}"); } static void PassByReference(ref int x) { x = 2; }
static void OutSample() { Out(out int a); Console.WriteLine($"after the invocation of {nameof(Out)}, {nameof(a)} = {a}"); } static void Out(out int x) { x = 2; }
if (int.TryParse("42", out var result)) { Console.WriteLine($"the result is {result}"); }
int x = 3; ref int x1 = ref x; x1 = 2; Console.WriteLine($"local variable {nameof(x)} after the change: {x}");
ref int i = sequence.Count();
ref int number1 = null; // ERROR ref int number2 = 42; // ERROR
static ref int ReturnByReference() { int[] arr = { 1 }; ref int x = ref arr[0]; return ref x; }
static ref int ReturnByReference2() { int[] arr = { 1 }; return ref arr[0]; }
static ref int ReturnByReference3(ref int x) { x = 2; return ref x; }
class Thing1 { ref string _Text1; /* Error */ ref string Text2 { get; set; } /* Error */ string _text = "Text"; ref string Text3 { get { return ref _text; } } // Properties that return a reference are allowed }
private MyBigStruct[] array = new MyBigStruct[10]; private int current; public ref MyBigStruct GetCurrentItem() { return ref array[current]; }
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; namespace EFGeneral { public class BlogComment { public int Id { set; get; } [MaxLength] public string Body { set; get; } public virtual BlogComment Reply { set; get; } public int? ReplyId { get; set; } public ICollection<BlogComment> Children { get; set; } } public class MyContext : DbContext { public DbSet<BlogComment> BlogComments { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // Self Referencing Entity modelBuilder.Entity<BlogComment>() .HasOptional(x => x.Reply) .WithMany(x => x.Children) .HasForeignKey(x => x.ReplyId) .WillCascadeOnDelete(false); base.OnModelCreating(modelBuilder); } } public class Configuration : DbMigrationsConfiguration<MyContext> { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } protected override void Seed(MyContext context) { var comment1 = new BlogComment { Body = "نظر من این است که" }; var comment12 = new BlogComment { Body = "پاسخی به نظر اول", Reply = comment1 }; var comment121 = new BlogComment { Body = "پاسخی به پاسخ به نظر اول", Reply = comment12 }; context.BlogComments.Add(comment121); base.Seed(context); } } public static class Test { public static void RunTests() { Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>()); using (var ctx = new MyContext()) { var list = ctx.BlogComments //.where ... .ToList() // fills the childs list too .Where(x => x.Reply == null) // for TreeViewHelper .ToList(); if (list.Any()) { } } } } }
public class BlogComment { // ... public ICollection<BlogComment> Children { get; set; } }
private void AppendChildren(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty) { var children = childrenProperty(parentItem); if (children == null || !children.Any()) { return; } //...
facid: 9, Name: 'Spa', membercost: 20, guestcost: 30, initialoutlay: 100000, monthlymaintenance: 800.
insert into facilities (facid, name, membercost, guestcost, initialoutlay, monthlymaintenance) values (9, 'Spa', 20, 30, 100000, 800); -- OR insert into facilities values (9, 'Spa', 20, 30, 100000, 800);
context.Facilities.Add(new Facility { Name = "Spa", MemberCost = 20, GuestCost = 30, InitialOutlay = 100000, MonthlyMaintenance = 800 }); context.SaveChanges();
facid: 9, Name: 'Spa', membercost: 20, guestcost: 30, initialoutlay: 100000, monthlymaintenance: 800. facid: 10, Name: 'Squash Court 2', membercost: 3.5, guestcost: 17.5, initialoutlay: 5000, monthlymaintenance: 80.
insert into facilities (facid, name, membercost, guestcost, initialoutlay, monthlymaintenance) values (9, 'Spa', 20, 30, 100000, 800), (10, 'Squash Court 2', 3.5, 17.5, 5000, 80);
context.Facilities.Add(new Facility { Name = "Spa", MemberCost = 20, GuestCost = 30, InitialOutlay = 100000, MonthlyMaintenance = 800 }); context.Facilities.Add(new Facility { Name = "Squash Court 2", MemberCost = 3.5M, GuestCost = 17.5M, InitialOutlay = 5000, MonthlyMaintenance = 80 }); context.SaveChanges();
Name: 'Spa', membercost: 20, guestcost: 30, initialoutlay: 100000, monthlymaintenance: 800.
namespace EFCorePgExercises.Entities { public class FacilityConfiguration : IEntityTypeConfiguration<Facility> { public void Configure(EntityTypeBuilder<Facility> builder) { builder.HasKey(facility => facility.FacId); builder.Property(facility => facility.FacId).IsRequired().UseIdentityColumn(seed: 0, increment: 1);
CREATE TABLE [dbo].[Facilities]( [FacId] [int] IDENTITY(0,1) NOT NULL, --- ... CONSTRAINT [PK_Facilities] PRIMARY KEY CLUSTERED ( [FacId] ASC );
update facilities set initialoutlay = 10000 where facid = 1;
var facility1 = context.Facilities.Find(1); facility1.InitialOutlay = 10000; context.SaveChanges();
update cd.facilities set membercost = 6, guestcost = 30 where facid in (0,1);
int[] facIds = { 0, 1 }; var tennisCourts = context.Facilities.Where(x => facIds.Contains(x.FacId)).ToList(); foreach (var tennisCourt in tennisCourts) { tennisCourt.MemberCost = 6; tennisCourt.GuestCost = 30; } context.SaveChanges();
update cd.facilities facs set membercost = (select membercost * 1.1 from cd.facilities where facid = 0), guestcost = (select guestcost * 1.1 from cd.facilities where facid = 0) where facs.facid = 1;
var fac0 = context.Facilities.Where(x => x.FacId == 0).First(); var fac1 = context.Facilities.Where(x => x.FacId == 1).First(); fac1.MemberCost = fac0.MemberCost * 1.1M; fac1.GuestCost = fac0.GuestCost * 1.1M; context.SaveChanges();
delete from bookings
context.Bookings.RemoveRange(context.Bookings.ToList()); context.SaveChanges();
delete from members where memid = 37;
var mem37 = context.Members.Where(x => x.MemId == 37).First(); context.Members.Remove(mem37); context.SaveChanges();
var entry = context.Entry(new Member { MemId = 37 }); entry.State = EntityState.Deleted; context.SaveChanges();
SET NOCOUNT ON; DELETE FROM [Members] WHERE [MemId] = @p0; SELECT @@ROWCOUNT;
delete from members where memid not in (select memid from cd.bookings);
var mems = context.Members.Where(x => !context.Bookings.Select(x => x.MemId).Contains(x.MemId)).ToList(); context.Members.RemoveRange(mems); context.SaveChanges();
ولی ما ابزاری مثل commit lint هنوز در دات نت نداریم.فعلا در صورت نیاز باید برای استفاده از آن همچنان node و npm را نصب کنیم.
نکته ای که باید اضافه کنم این است که در ورژن 0.5 به بعد باتوجه به اینکه پشتیبانی از CSharpScript اضافه شده است, برای بررسی متن کامیتها میتوان از اسکریپت زیر که که در مخزن Husky.Net هم وجود دارد استفاده کرد.
/// <summary> /// a simple regex commit linter example /// https://www.conventionalcommits.org/en/v1.0.0/ /// https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type /// </summary> using System.Text.RegularExpressions; private var pattern = @"^(?=.{1,90}$)(?:build|feat|ci|chore|docs|fix|perf|refactor|revert|style|test)(?:\(.+\))*(?::).{4,}(?:#\d+)*(?<![\.\s])$"; private var msg = File.ReadAllLines(Args[0])[0]; if (Regex.IsMatch(msg, pattern)) return 0; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Invalid commit message"); Console.ResetColor(); Console.WriteLine("e.g: 'feat(scope): subject' or 'fix: subject'"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("more info: https://www.conventionalcommits.org/en/v1.0.0/"); return 1;
class EventSource : System.Progress<int> { public async System.Threading.Tasks.Task<int> PerformExpensiveCalculation() { var sum = 0; for (var i = 0; i < 100; i++) { await System.Threading.Tasks.Task.Delay(100); sum += i; this.OnReport(sum); } return sum; } } static class Program { static void Main(string[] args) { var source = new EventSource(); System.EventHandler<int> handler = (_, progress) => System.Console.WriteLine(progress); source.ProgressChanged += handler; System.Console.WriteLine(source.PerformExpensiveCalculation().Result); source.ProgressChanged -= handler; source.ProgressChanged += ProgressChangedMethod; System.Console.WriteLine(source.PerformExpensiveCalculation().Result); source.ProgressChanged -= ProgressChangedMethod; } private static void ProgressChangedMethod( object sender, int e ) { System.Console.WriteLine(e); } }
IL_0007: ldsfld class [mscorlib]System.EventHandler`1<int32> LambdaPerformance.Program/'<>c'::'<>9__0_0' IL_000c: dup IL_000d: brtrue.s IL_0026 IL_000f: pop IL_0010: ldsfld class LambdaPerformance.Program/'<>c' LambdaPerformance.Program/'<>c'::'<>9' IL_0015: ldftn instance void LambdaPerformance.Program/'<>c'::'<Main>b__0_0'(object, int32) IL_001b: newobj instance void class [mscorlib]System.EventHandler`1<int32>::.ctor(object, native int) IL_0020: dup IL_0021: stsfld class [mscorlib]System.EventHandler`1<int32> LambdaPerformance.Program/'<>c'::'<>9__0_0' IL_0026: stloc.1 IL_0027: ldloc.0 IL_0028: ldloc.1 IL_0029: callvirt instance void class [mscorlib]System.Progress`1<int32>::add_ProgressChanged(class [mscorlib]System.EventHandler`1<!0>) IL_002e: nop IL_002f: ldloc.0 IL_0030: callvirt instance class [mscorlib]System.Threading.Tasks.Task`1<int32> LambdaPerformance.EventSource::PerformExpensiveCalculation() IL_0035: callvirt instance !0 class [mscorlib]System.Threading.Tasks.Task`1<int32>::get_Result() IL_003a: call void [mscorlib]System.Console::WriteLine(int32) IL_003f: nop IL_0040: ldloc.0 IL_0041: ldloc.1 IL_0042: callvirt instance void class [mscorlib]System.Progress`1<int32>::remove_ProgressChanged(class [mscorlib]System.EventHandler`1<!0>)
IL_004a: ldftn void LambdaPerformance.Program::ProgressChangedMethod(object, int32) IL_0050: newobj instance void class [mscorlib]System.EventHandler`1<int32>::.ctor(object, native int) IL_0055: callvirt instance void class [mscorlib]System.Progress`1<int32>::add_ProgressChanged(class [mscorlib]System.EventHandler`1<!0>) IL_005a: nop IL_005b: ldloc.0 IL_005c: callvirt instance class [mscorlib]System.Threading.Tasks.Task`1<int32> LambdaPerformance.EventSource::PerformExpensiveCalculation() IL_0061: callvirt instance !0 class [mscorlib]System.Threading.Tasks.Task`1<int32>::get_Result() IL_0066: call void [mscorlib]System.Console::WriteLine(int32) IL_006b: nop IL_006c: ldloc.0 IL_006d: ldnull IL_006e: ldftn void LambdaPerformance.Program::ProgressChangedMethod(object, int32) IL_0074: newobj instance void class [mscorlib]System.EventHandler`1<int32>::.ctor(object, native int) IL_0079: callvirt instance void class [mscorlib]System.Progress`1<int32>::remove_ProgressChanged(class [mscorlib]System.EventHandler`1<!0>)
static class Program { static void Main(string[] args) { for (var repeats = 10; repeats <= 1000000; repeats *= 10) { VersionOne(repeats); VersionTwo(repeats); } } private static void VersionOne(int repeats) { var timer = new System.Diagnostics.Stopwatch(); timer.Start(); var source = new EventSource(); for (var i = 0; i < repeats; i++) { System.EventHandler<int> handler = (_, progress) => System.Console.WriteLine(progress); source.ProgressChanged += handler; // Console.WriteLine(source.PerformExpensiveCalculation().Result); source.ProgressChanged -= handler; } timer.Stop(); System.Console.WriteLine($"Version one: {repeats} add/remove takes {timer.ElapsedMilliseconds}ms"); } private static void VersionTwo(int repeats) { var timer = new System.Diagnostics.Stopwatch(); timer.Start(); var source = new EventSource(); for (var i = 0; i < repeats; i++) { source.ProgressChanged += ProgressChangedMethod; // Console.WriteLine(source.PerformExpensiveCalculation().Result); source.ProgressChanged -= ProgressChangedMethod; } timer.Stop(); System.Console.WriteLine($"Version two: {repeats} add/remove takes {timer.ElapsedMilliseconds}ms"); } private static void ProgressChangedMethod(object sender, int e) { System.Console.WriteLine(e); } }
Version one: 10 add/remove takes 0ms Version two: 10 add/remove takes 0ms Version one: 100 add/remove takes 0ms Version two: 100 add/remove takes 0ms Version one: 1000 add/remove takes 0ms Version two: 1000 add/remove takes 0ms Version one: 10000 add/remove takes 0ms Version two: 10000 add/remove takes 1ms Version one: 100000 add/remove takes 8ms Version two: 100000 add/remove takes 13ms Version one: 1000000 add/remove takes 93ms Version two: 1000000 add/remove takes 121ms
private static void VersionOne(int repeats) { var timer = new System.Diagnostics.Stopwatch(); timer.Start(); var source = new EventSource(); for (var i = 0; i < repeats; i++) { // System.EventHandler<int> handler = (_, progress) => System.Console.WriteLine(progress); source.ProgressChanged += (_, progress) => System.Console.WriteLine(progress); // Console.WriteLine(source.PerformExpensiveCalculation().Result); source.ProgressChanged -= (_, progress) => System.Console.WriteLine(progress); } timer.Stop(); System.Console.WriteLine($"Version one: {repeats} add/remove takes {timer.ElapsedMilliseconds}ms"); }
Version one: 10 add/remove takes 0ms Version two: 10 add/remove takes 0ms Version one: 100 add/remove takes 1ms Version two: 100 add/remove takes 0ms Version one: 1000 add/remove takes 102ms Version two: 1000 add/remove takes 0ms Version one: 10000 add/remove takes 10509ms Version two: 10000 add/remove takes 1ms Version one: 100000 add/remove takes 1039014ms Version two: 100000 add/remove takes 11ms
IL_0018: nop IL_0019: ldloc.1 IL_001a: ldsfld class [mscorlib]System.EventHandler`1<int32> LambdaPerformance.Program/'<>c'::'<>9__1_0' IL_001f: dup IL_0020: brtrue.s IL_0039 IL_0022: pop IL_0023: ldsfld class LambdaPerformance.Program/'<>c' LambdaPerformance.Program/'<>c'::'<>9' IL_0028: ldftn instance void LambdaPerformance.Program/'<>c'::'<VersionOne>b__1_0'(object, int32) IL_002e: newobj instance void class [mscorlib]System.EventHandler`1<int32>::.ctor(object, native int) IL_0033: dup IL_0034: stsfld class [mscorlib]System.EventHandler`1<int32> LambdaPerformance.Program/'<>c'::'<>9__1_0' IL_0039: callvirt instance void class [mscorlib]System.Progress`1<int32>::add_ProgressChanged(class [mscorlib]System.EventHandler`1<!0>) IL_003e: nop IL_003f: ldloc.1 IL_0040: ldsfld class [mscorlib]System.EventHandler`1<int32> LambdaPerformance.Program/'<>c'::'<>9__1_1' IL_0045: dup IL_0046: brtrue.s IL_005f IL_0048: pop IL_0049: ldsfld class LambdaPerformance.Program/'<>c' LambdaPerformance.Program/'<>c'::'<>9' IL_004e: ldftn instance void LambdaPerformance.Program/'<>c'::'<VersionOne>b__1_1'(object, int32) IL_0054: newobj instance void class [mscorlib]System.EventHandler`1<int32>::.ctor(object, native int) IL_0059: dup IL_005a: stsfld class [mscorlib]System.EventHandler`1<int32> LambdaPerformance.Program/'<>c'::'<>9__1_1' IL_005f: callvirt instance void class [mscorlib]System.Progress`1<int32>::remove_ProgressChanged(class [mscorlib]System.EventHandler`1<!0>) IL_0064: nop IL_0065: nop IL_0066: ldloc.2 IL_0067: stloc.3 IL_0068: ldloc.3 IL_0069: ldc.i4.1 IL_006a: add IL_006b: stloc.2 IL_006c: ldloc.2 IL_006d: ldarg.0 IL_006e: clt IL_0070: stloc.s V_4 IL_0072: ldloc.s V_4 IL_0074: brtrue.s IL_0018