Starting in .NET 5.0, Roslyn analyzers are included with the .NET SDK. Roslyn analyzers are enabled, by default, for projects that target .NET 5.0 or later. You can enable them on projects that target earlier .NET versions by setting the EnableNETAnalyzers property to true.
هرچند قرار است Visual Basic در NET 5x. حضور داشته باشد، اما این زبان دیگر به روز رسانی نخواهد شد
"Going forward, we do not plan to evolve Visual Basic as a language," the .NET team said. "This supports language stability and maintains compatibility between the .NET Core and .NET Framework versions of Visual Basic. Future features of .NET Core that require language changes may not be supported in Visual Basic. Due to differences in the platform, there will be some differences between Visual Basic on .NET Framework and .NET Core."
خبرنامه هفتگی C# Digest
آموزش AngularJS به صورت تعاملی
EF Code First #2
در قسمت قبل با تنظیمات و قراردادهای ابتدایی EF Code first آشنا شدیم، هرچند این تنظیمات حجم کدنویسی ابتدایی راه اندازی سیستم را به شدت کاهش میدهند، اما کافی نیستند. در این قسمت نگاهی سطحی و مقدماتی خواهیم داشت بر امکانات مهیا جهت تنظیم ویژگیهای مدلهای برنامه در EF Code first.
تنظیمات EF Code first توسط اعمال متادیتای خواص
اغلب متادیتای مورد نیاز جهت اعمال تنظیمات EF Code first در اسمبلی System.ComponentModel.DataAnnotations.dll قرار دارند. بنابراین اگر مدلهای خود را در اسمبلی و پروژه class library جداگانهای تعریف و نگهداری میکنید (مثلا به نام DomainClasses)، نیاز است ابتدا ارجاعی را به این اسمبلی به پروژه جاری اضافه نمائیم. همچنین تعدادی دیگر از متادیتای قابل استفاده در خود اسمبلی EntityFramework.dll قرار دارند. بنابراین در صورت نیاز باید ارجاعی را به این اسمبلی نیز اضافه نمود.
همان مثال قبل را در اینجا ادامه میدهیم. دو کلاس Blog و Post در آن تعریف شده (به این نوع کلاسها POCO – the Plain Old CLR Objects نیز گفته میشود)، به همراه کلاس Context که از کلاس DbContext مشتق شده است. ابتدا دیتابیس قبلی را دستی drop کنید. سپس در کلاس Blog، خاصیت public int Id را مثلا به public int MyTableKey تغییر دهید و پروژه را اجرا کنید. برنامه بلافاصله با خطای زیر متوقف میشود:
One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'Blog' has no key defined.
زیرا EF Code first در این کلاس خاصیتی به نام Id یا BlogId را نیافتهاست و امکان تشکیل Primary key جدول را ندارد. برای رفع این مشکل تنها کافی است ویژگی Key را به این خاصیت اعمال کنیم:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EF_Sample01.Models
{
public class Blog
{
[Key]
public int MyTableKey { set; get; }
همچنین تعدادی ویژگی دیگر مانند MaxLength و Required را نیز میتوان بر روی خواص کلاس اعمال کرد:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EF_Sample01.Models
{
public class Blog
{
[Key]
public int MyTableKey { set; get; }
[MaxLength(100)]
public string Title { set; get; }
[Required]
public string AuthorName { set; get; }
public IList<Post> Posts { set; get; }
}
}
این ویژگیها دو مقصود مهم را برآورده میسازند:
الف) بر روی ساختار بانک اطلاعاتی تشکیل شده تاثیر دارند:
CREATE TABLE [dbo].[Blogs](
[MyTableKey] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](100) NULL,
[AuthorName] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY CLUSTERED
(
[MyTableKey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
همانطور که ملاحظه میکنید در اینجا طول فیلد Title به 100 تنظیم شده است و همچنین فیلد AuthorName اینبار NOT NULL است. به علاوه primary key نیز بر اساس ویژگی Key اعمالی تعیین شده است.
البته برای اجرای کدهای تغییر کرده مدل، فعلا بانک اطلاعاتی قبلی را دستی میتوان حذف کرد تا بتوان به ساختار جدید رسید. در مورد جزئیات مبحث DB Migration در قسمتهای بعدی مفصلا بحث خواهد شد.
ب) اعتبار سنجی اطلاعات پیش از ارسال کوئری به بانک اطلاعاتی
برای مثال اگر در حین تعریف وهلهای از کلاس Blog، خاصیت AuthorName مقدار دهی نگردد، پیش از اینکه رفت و برگشتی به بانک اطلاعاتی صورت گیرد، یک validation error را دریافت خواهیم کرد. یا برای مثال اگر طول اطلاعات خاصیت Title بیش از 100 حرف باشد نیز مجددا در حین ثبت اطلاعات، یک استثنای اعتبار سنجی را مشاهده خواهیم کرد. البته امکان تعریف پیغامهای خطای سفارشی نیز وجود دارد. برای این حالت تنها کافی است پارامتر ErrorMessage این ویژگیها را مقدار دهی کرد. برای مثال:
[Required(ErrorMessage = "لطفا نام نویسنده را مشخص نمائید")]
public string AuthorName { set; get; }
نکتهی مهمی که در اینجا وجود دارد، وجود یک اکوسیستم هماهنگ و سازگار است. این نوع اعتبار سنجی هم با EF Code first هماهنگ است و هم برای مثال در ASP.NET MVC به صورت خودکار جهت اعتبار سنجی سمت سرور و کلاینت یک مدل میتواند مورد استفاده قرار گیرد و مفاهیم و روشهای مورد استفاده در آن نیز یکی است.
تنظیمات EF Code first به کمک Fluent API
اگر علاقمند به استفاده از متادیتا، جهت تعریف قیود و ویژگیهای خواص کلاسهای مدل خود نیستید، روش دیگری نیز در EF Code first به نام Fluent API تدارک دیده شده است. در اینجا امکان تعریف همان ویژگیها توسط کدنویسی نیز وجود دارد، به علاوه اعمال قیود دیگری که توسط متادیتای مهیا قابل تعریف نیستند.
محل تعریف این قیود، کلاس Context که از کلاس DbContext مشتق شده است، میباشد و در اینجا، کار با تحریف متد OnModelCreating شروع میشود:
using System.Data.Entity;
using EF_Sample01.Models;
namespace EF_Sample01
{
public class Context : DbContext
{
public DbSet<Blog> Blogs { set; get; }
public DbSet<Post> Posts { set; get; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().HasKey(x => x.MyTableKey);
modelBuilder.Entity<Blog>().Property(x => x.Title).HasMaxLength(100);
modelBuilder.Entity<Blog>().Property(x => x.AuthorName).IsRequired();
base.OnModelCreating(modelBuilder);
}
}
}
به کمک پارامتر modelBuilder، امکان دسترسی به متدهای تنظیم کننده ویژگیهای خواص یک مدل یا موجودیت وجود دارد. در اینجا چون میتوان متدها را به صورت یک زنجیره به هم متصل کرد و همچنین حاصل نهایی شبیه به جمله بندی انگلیسی است، به آن Fluent API یا API روان نیز گفته میشود.
البته در این حالت امکان تعریف ErrorMessage وجود ندارد و برای این منظور باید از همان data annotations استفاده کرد.
نحوه مدیریت صحیح تعاریف نگاشتها به کمک Fluent API
OnModelCreating محل مناسبی جهت تعریف حجم انبوهی از تنظیمات کلاسهای مختلف مدلهای برنامه نیست. در حد سه چهار سطر مشکلی ندارد اما اگر بیشتر شد بهتر است از روش زیر استفاده شود:
using System.Data.Entity;
using EF_Sample01.Models;
using System.Data.Entity.ModelConfiguration;
namespace EF_Sample01
{
public class BlogConfig : EntityTypeConfiguration<Blog>
{
public BlogConfig()
{
this.Property(x => x.Id).HasColumnName("MyTableKey");
this.Property(x => x.RowVersion).HasColumnType("Timestamp");
}
}
با ارث بری از کلاس EntityTypeConfiguration، میتوان به ازای هر کلاس مدل، تنظیمات را جداگانه انجام داد. به این ترتیب اصل SRP یا Single responsibility principle نقض نخواهد شد. سپس برای استفاده از این کلاسهای Config تک مسئولیتی به نحو زیر میتوان اقدام کرد:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BlogConfig());
نحوه تنظیمات ابتدایی نگاشت کلاسها به بانک اطلاعاتی در EF Code first
الزامی ندارد که EF Code first حتما با یک بانک اطلاعاتی از نو تهیه شده بر اساس پیش فرضهای آن کار کند. در اینجا میتوان از بانکهای اطلاعاتی موجود نیز استفاده کرد. اما در این حالت نیاز خواهد بود تا مثلا نام جدولی خاص با کلاسی مفروض در برنامه، یا نام فیلدی خاص که مطابق استانداردهای نامگذاری خواص در سی شارپ تعریف نشده، با خاصیتی در یک کلاس تطابق داده شوند. برای مثال اینبار تعاریف کلاس Blog را به نحو زیر تغییر دهید:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EF_Sample01.Models
{
[Table("tblBlogs")]
public class Blog
{
[Column("MyTableKey")]
public int Id { set; get; }
[MaxLength(100)]
public string Title { set; get; }
[Required(ErrorMessage = "لطفا نام نویسنده را مشخص نمائید")]
public string AuthorName { set; get; }
public IList<Post> Posts { set; get; }
[Timestamp]
public byte[] RowVersion { set; get; }
}
}
در اینجا فرض بر این است که نام جدول متناظر با کلاس Blog در بانک اطلاعاتی مثلا tblBlogs است و نام خاصیت Id در بانک اطلاعاتی مساوی فیلدی است به نام MyTableKey. چون نام خاصیت را مجددا به Id تغییر دادهایم، دیگر ضرورتی به ذکر ویژگی Key وجود نداشته است. برای تعریف این دو از ویژگیهای Table و Column جهت سفارشی سازی نامهای خواص و کلاس استفاده شده است.
یا اگر در کلاس خود خاصیتی محاسبه شده بر اساس سایر خواص، تعریف شده است و قصد نداریم آنرا به فیلدی در بانک اطلاعاتی نگاشت کنیم، میتوان از ویژگی NotMapped برای مزین سازی و تعریف آن کمک گرفت.
به علاوه اگر از نام پیش فرض کلید خارجی تشکیل شده خرسند نیستید میتوان به کمک ویژگی ForeignKey، نسبت به تعریف مقداری جدید مطابق تعاریف یک بانک اطلاعاتی موجود، اقدام کرد.
همچنین خاصیت دیگری به نام RowVersion در اینجا اضافه شده که با ویژگی TimeStamp مزین گردیده است. از این خاصیت ویژه برای بررسی مسایل همزمانی ثبت اطلاعات در EF استفاده میشود. به علاوه بانک اطلاعاتی میتواند به صورت خودکار آنرا در حین ثبت مقدار دهی کند.
تمام این تغییرات را به کمک Fluent API نیز میتوان انجام داد:
modelBuilder.Entity<Blog>().ToTable("tblBlogs");
modelBuilder.Entity<Blog>().Property(x => x.Id).HasColumnName("MyTableKey");
modelBuilder.Entity<Blog>().Property(x => x.RowVersion).HasColumnType("Timestamp");
تبدیل پروژههای قدیمی EF به کلاسهای EF Code first به صورت خودکار
روش متداول کار با EF از روز اول آن، مهندسی معکوس خودکار اطلاعات یک بانک اطلاعاتی و تبدیل آن به یک فایل EDMX بوده است. هنوز هم میتوان از این روش در اینجا نیز بهره جست. برای مثال اگر قصد دارید یک پروژه قدیمی را تبدیل به نمونه جدید Code first کنید، یا یک بانک اطلاعاتی موجود را مهندسی معکوس کنید، بر روی پروژه در Solution explorer کلیک راست کرده و گزینه Add|New Item را انتخاب کنید. سپس از صفحه ظاهر شده، ADO.NET Entity data model را انتخاب کرده و در ادامه گزینه «Generate from database» را انتخاب کنید. این روال مرسوم کار با EF Database first است.
پس از اتمام کار به entity data model designer مراجعه کرده و بر روی صفحه کلیک راست نمائید. از منوی ظاهر شده گزینه «Add code generation item» را انتخاب کنید. سپس در صفحه باز شده از لیست قالبهای موجود، گزینه «ADO.NET DbContext Generator» را انتخاب نمائید. این گزینه به صورت خودکار اطلاعات فایل EDMX قدیمی یا موجود شما را تبدیل به کلاسهای مدل Code first معادل به همراه کلاس DbContext معرف آنها خواهد کرد.
روش دیگری نیز برای انجام اینکار وجود دارد. نیاز است افزونهی به نام Entity Framework Power Tools را دریافت کنید. پس از نصب، از منوی Entity Framework آن گزینهی «Reverse Engineer Code First» را انتخاب نمائید. در اینجا میتوان مشخصات اتصال به بانک اطلاعاتی را تعریف و سپس نسبت به تولید خودکار کدهای مدلها و DbContext مرتبط اقدام کرد.
استراتژیهای مقدماتی تشکیل بانک اطلاعاتی در EF Code first
اگر مثال این سری را دنبال کرده باشید، مشاهده کردهاید که با اولین بار اجرای برنامه، یک بانک اطلاعاتی پیش فرض نیز تولید خواهد شد. یا اگر تعاریف ویژگیهای یک فیلد را تغییر دادیم، نیاز است تا بانک اطلاعاتی را دستی drop کرده و اجازه دهیم تا بانک اطلاعاتی جدیدی بر اساس تعاریف جدید مدلها تشکیل شود که ... هیچکدام از اینها بهینه نیستند.
در اینجا دو استراتژی مقدماتی را در حین آغاز یک برنامه میتوان تعریف کرد:
System.Data.Entity.Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context>());
// or
System.Data.Entity.Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
میتوان بانک اطلاعاتی را در صورت تغییر اطلاعات یک مدل به صورت خودکار drop کرده و نسبت به ایجاد نمونهای جدید اقدام کرد (DropCreateDatabaseIfModelChanges)؛ یا در حین آزمایش برنامه همیشه (DropCreateDatabaseAlways) با شروع برنامه، ابتدا باید بانک اطلاعاتی drop شده و سپس نمونه جدیدی تولید گردد.
محل فراخوانی این دستور هم باید در نقطه آغازین برنامه، پیش از وهله سازی اولین DbContext باشد. مثلا در برنامههای وب در متد Application_Start فایل global.asax.cs یا در برنامههای WPF در متد سازنده کلاس App میتوان بانک اطلاعاتی را آغاز نمود.
البته الزامی به استفاده از کلاسهای DropCreateDatabaseIfModelChanges یا DropCreateDatabaseAlways وجود ندارد. میتوان با پیاده سازی اینترفیس IDatabaseInitializer از نوع کلاس Context تعریف شده در برنامه، همان عملیات را شبیه سازی کرد یا سفارشی نمود:
public class MyInitializer : IDatabaseInitializer<Context>
{
public void InitializeDatabase(Context context)
{
if (context.Database.Exists() ||
context.Database.CompatibleWithModel(throwIfNoMetadata: false))
context.Database.Delete();
context.Database.Create();
}
}
سپس برای استفاده از این کلاس در ابتدای برنامه، خواهیم داشت:
System.Data.Entity.Database.SetInitializer(new MyInitializer());
نکته:
اگر از یک بانک اطلاعاتی موجود استفاده میکنید (محیط کاری) و نیازی به پیش فرضهای EF Code first ندارید و همچنین این بانک اطلاعاتی نیز نباید drop شود یا تغییر کند، میتوانید تمام این پیش فرضها را با دستور زیر غیرفعال کنید:
Database.SetInitializer<Context>(null);
بدیهی است این دستور نیز باید پیش از ایجاد اولین وهله از شیء DbContext فراخوانی شود.
همچنین باید درنظر داشت که در آخرین نگارشهای پایدار EF Code first، این موارد بهبود یافتهاند و مبحثی تحت عنوان DB Migration ایجاد شده است تا نیازی نباشد هربار بانک اطلاعاتی drop شود و تمام اطلاعات از دست برود. میتوان صرفا تغییرات کلاسها را به بانک اطلاعاتی اعمال کرد که به صورت جداگانه، در قسمتی مجزا بررسی خواهد شد. به این ترتیب دیگر نیازی به drop بانک اطلاعاتی نخواهد بود. به صورت پیش فرض در صورت از دست رفتن اطلاعات یک استثناء را سبب خواهد شد (که توسط برنامه نویس قابل تنظیم است) و در حالت خودکار یا دستی با تنظیمات ویژه قابل اعمال است.
تنظیم استراتژیهای آغاز بانک اطلاعاتی در فایل کانفیگ برنامه
الزامی ندارد که حتما متد Database.SetInitializer را دستی فراخوانی کنیم. با اندکی تنظیم فایلهای app.config و یا web.config نیز میتوان نوع استراتژی مورد استفاده را تعیین کرد:
<appSettings>
<add key="DatabaseInitializerForType MyNamespace.MyDbContextClass, MyAssembly"
value="MyNamespace.MyInitializerClass, MyAssembly" />
</appSettings>
<appSettings>
<add key="DatabaseInitializerForType MyNamespace.MyDbContextClass, MyAssembly"
value="Disabled" />
</appSettings>
یکی از دو حالت فوق باید در قسمت appSettings فایل کانفیگ برنامه تنظیم شود. حالت دوم برای غیرفعال کردن پروسه آغاز بانک اطلاعاتی و اعمال تغییرات به آن، بکار میرود.
برای نمونه در مثال جاری، جهت استفاده از کلاس MyInitializer فوق، میتوان از تنظیم زیر نیز استفاده کرد:
<appSettings>
<add key="DatabaseInitializerForType EF_Sample01.Context, EF_Sample01"
value="EF_Sample01.MyInitializer, EF_Sample01" />
</appSettings>
اجرای کدهای ویژه در حین تشکیل یک بانک اطلاعاتی جدید
امکان سفارشی سازی این آغاز کنندههای پیش فرض نیز وجود دارد. برای مثال:
public class MyCustomInitializer : DropCreateDatabaseIfModelChanges<Context>
{
protected override void Seed(Context context)
{
context.Blogs.Add(new Blog { AuthorName = "Vahid", Title = ".NET Tips" });
context.Database.ExecuteSqlCommand("CREATE INDEX IX_title ON tblBlogs (title)");
base.Seed(context);
}
}
در اینجا با ارث بری از کلاس DropCreateDatabaseIfModelChanges یک آغاز کننده سفارشی را تعریف کردهایم. سپس با تحریف متد Seed آن میتوان در حین آغاز یک بانک اطلاعاتی، تعدادی رکورد پیش فرض را به آن افزود. کار ذخیره سازی نهایی در متد base.Seed انجام میشود.
برای استفاده از آن اینبار در حین فراخوانی متد System.Data.Entity.Database.SetInitializer، از کلاس MyCustomInitializer استفاده خواهیم کرد.
و یا توسط متد context.Database.ExecuteSqlCommand میتوان دستورات SQL را مستقیما در اینجا اجرا کرد. عموما دستوراتی در اینجا مدنظر هستند که توسط ORMها پشتیبانی نمیشوند. برای مثال تغییر collation یک ستون یا افزودن یک ایندکس و مواردی از این دست.
سطح دسترسی مورد نیاز جهت فراخوانی متد Database.SetInitializer
استفاده از متدهای آغاز کننده بانک اطلاعاتی نیاز به سطح دسترسی بر روی بانک اطلاعاتی master را در SQL Server دارند (زیرا با انجام کوئری بر روی این بانک اطلاعاتی مشخص میشود، آیا بانک اطلاعاتی مورد نظر پیشتر تعریف شده است یا خیر). البته این مورد حین کار با SQL Server CE شاید اهمیتی نداشته باشد. بنابراین اگر کاربری که با آن به بانک اطلاعاتی متصل میشویم سطح دسترسی پایینی دارد نیاز است Persist Security Info=True را به رشته اتصالی اضافه کرد. البته این مورد را پس از انجام تغییرات بر روی بانک اطلاعاتی جهت امنیت بیشتر حذف کنید (یا به عبارتی در محیط کاری Persist Security Info=False باید باشد).
Server=(local);Database=yourDatabase;User ID=yourDBUser;Password=yourDBPassword;Trusted_Connection=False;Persist Security Info=True
تعیین Schema و کاربر فراخوان دستورات SQL
در EF Code first به صورت پیش فرض همه چیز بر مبنای کاربری با دسترسی مدیریتی یا dbo schema در اس کیوال سرور تنظیم شده است. اما اگر کاربر خاصی برای کار با دیتابیس تعریف گردد که در هاستهای اشتراکی بسیار مرسوم است، دیگر از دسترسی مدیریتی dbo خبری نخواهد بود. اینبار نام جداول ما بجای dbo.tableName مثلا someUser.tableName میباشند و عدم دقت به این نکته، اجرای برنامه را غیرممکن میسازد.
برای تغییر و تعیین صریح کاربر متصل شده به بانک اطلاعاتی اگر از متادیتا استفاده میکنید، روش زیر باید بکارگرفته شود:
[Table("tblBlogs", Schema="someUser")]
public class Blog
و یا در حالت بکارگیری Fluent API به نحو زیر قابل تنظیم است:
modelBuilder.Entity<Blog>().ToTable("tblBlogs", schemaName:"someUser");
.NET Core + Angular Dashboard
Topics Covered:
- Building a dashboard application in Angular
- Building a Web API in .NET Core 2.0
- Using Chart.js to build stunning charts of different types
- Making HTTP requests using Angular to query a Web API
- Using Postman to send requests
- Working with Observables
- Using Input and Output decorators in Angular
- Using PostgreSQL and pgAdmin
- Automatically seeding a database with large amounts of sample data
- Styling an application using custom CSS and Bootstrap 4
- Using Map, Filter, and Reduce in Javascript
- Creating Routes in Angular
- Get, Put, Post, Patch Web API Controller Action request types
- Configuring your API for CORS
Learn .NET MAUI while Creating a Contacts App in .NET 7 - YouTube
00:00:00 Introduction
00:02:54 What is .NET MAUI
00:06:13 Prepare Development Environment & Create first project.
00:11:50 Project Structure of .Net Maui
00:19:49 Three elements of stateful .Net Maui app
00:23:12 Page, Layout & View, Namespaces
00:32:23 URL based navigation - .Net Maui
00:50:31 Basics of ListView and Data Binding in .Net Maui
01:05:19 Events Handling of ListView in .Net Maui
01:16:15 Parameters in URL based Navigation & Static Repository in .Net Maui
01:34:56 Stacklayout for Edit Contact page in .Net Maui
01:52:08 View Contact Details & Update Contact (.Net Maui)
02:06:01 Observable Collection
02:14:19 Field Validation with .Net Maui CommunityToolkit
02:26:18 Reusable Control in .Net Maui
02:39:42 Grid Layout and Use reusable control in .Net Maui
02:52:44 ContextActions & MenuItems in ListView for .Net Maui
03:03:05 SearchBar in .NetMaui