With the new Version 7, Entity Framework is moving away from legacy systems and duplications which have accumulated in recent years. As a slim-line new development it will be available for .NET Core, and therefore also WinRT, whilst in addition allowing access to NoSQL databases.This session will show exactly what has changed and how you can benefit from the new possibilities.
This is the first of an eight part series where I am joined by Phil Japikse to discuss design patterns. A design pattern is a best practice you can use in your code to solve a common problem. In this episode, Phil demonstrates the Command and Memento patterns.
Episodes in this series:
- Command/Memento patterns (this episode)
- Strategy pattern
- Template Method pattern (to be published 7/20)
- Observer/Publish-Subscribe patterns (to be published 7/25)
- Singleton pattern (to be published 8/8)
- Factory patterns (to be published 8/10)
- Adapter/Facade patterns (to be published 8/15)
- Decorator pattern (to be published 8/17)
اشتراکها
پشتیبانی رسمی RethinkDB از Windows
در طی سالهای قبل، نحوهی آغاز برنامههای ASP.NET Core دچار تغییرات زیادی شدهاست که حداقل سه مورد مهم آنها به صورت زیر است:
- استفاده از WebHost.CreateDefaultBuilder: این روش جهت تنظیم شروع به کار یک برنامهی ASP.NET Core 2x مورد استفاده قرار میگرفت.
- استفاده از Host.CreateDefaultBuilder: روش پیشفرض آغاز برنامههای وب NET Core 3x. و NET 5x. که با معرفی generic host، امکان تهیهی Worker services را میسر کردند.
- استفاده از WebApplication.CreateBuilder: روش جدید شروع به کار با برنامههای وب مبتنی بر NET 6.
استفاده از WebHost.CreateDefaultBuilder در ASP.NET Core 2.x
در نگارش اول ASP.NET Core، مفهومی به نام هاست پیشفرض (default host) وجود نداشت. یکی از مهمترین نکات درنظر گرفته شده در طراحی ASP.NET Core، مفهوم «هزینه کردن به ازای احتیاج» است. یعنی اگر نیاز به قابلیتی نیست، نباید وجود داشته باشد. به این ترتیب یک قالب آغازین نباید به همراه تعداد زیادی بستههای NuGet و مقدار زیادی کد برای تنظیم باشد؛ زمانیکه واقعا قرار نیست از تمام آنها استفاده شود. به همین جهت از نگارش 2، شروع به سادهکردن این قالب اولیه کردند و در این زمان، WebHost.CreateDefaultBuilder ارائه شد. این تنظیم به ظاهر ساده، کدهای پیشفرض مورد نیاز قابل توجهی را جهت ساخت سادهی IWebHostBuilder و IWebHost به همراه دارد. در قالب به همراه آن، بین مفاهیم تنظیمات application و host تفاوت قائل شدهاند؛ یعنی دو فایل Program.cs را برای تنظیمات application و فایل Startup.cs را برای ارائهی تنظیمات هاست، تدارک دیدند.
در این طراحی، بین میدان دید Program و Startup تفاوت وجود دارد. هدف از Program، ارائهی تنظیمات زیرساختی برنامه، مانند تنظیمات HTTP Server، یکپارچگی با IIS و امثال آن شد و عموما در طول عمر برنامه ثابت است. اما برای تغییر رفتار برنامه، بیشتر تغییرات و تنظیمات، به Startup محول شدند؛ مانند تنظیمات تزریق وابستگیها، تنظیمات میانافزارها، مسیریابیها و غیره.
نمونهای از فایل Program.cs برنامههای ASP.NET Core 2x را در اینجا ملاحظه میکنید این تنظیم ساده، کار خواندن فایل appsettings.json و برپایی تنظیمات برنامه، تنظیمات logging و همچنین تنظیمات یکپارچگی با Kestrel/IIS را در پشت صحنه انجام میدهد. همچنین ارجاعی را نیز به کلاس Startup برنامه دارد. متد UseStartup به صورت خودکار به دنبال متدهای ویژهی ConfigureServices و Configure در کلاس آغازین برنامه گشته و آنها را فراخوانی میکند تا تنظیمات تزریق وابستگیها، مسیریابیها و میانافزارها صورت گیرند.
معرفی Generic Host در ASP.NET Core 3.x/5
ASP.NET Core 3.x به همراه تغییرات بزرگی در کدهای آغازین برنامههای ASP.NET Core بود. تا پیش از آن، امکانات پایهی ASP.NET Core تنها برای مقاصد وب قابل استفاده بود. اما در نگارشهای 3x، با ارائهی یک هاست عمومی، امکان پشتیبانی از سایر برنامهها، مانند worker services را نیز میسر کرد؛ برای مثال پیشتیبانی از کارهای طولانی پس زمینه، هاست سرویسهای gRPC، هاست سرویسهای ویندوز و غیره. هدف اصلی از این تغییرات، به اشتراک گذاری فریمورک پایهی ASP.NET Core که تنها برای برنامههای وب ساخته شده بود و به همراه امکاناتی مانند تنظیمات، ثبت وقایع، تزریق وابستگیها و غیره بود، با سایر انواع برنامههای یاد شده است. برای رسیدن به این هدف، Web Host نگارش 2x، به Generic Host نگارش 3x تغییر کرد و سپس ASP.NET Core برفراز آن بنا شد. اینبار بجای IWebHostBuilder، نمونهی جدید IHostBuilder را داریم:
در اینجا نمونهای از فایل Program.cs برنامههای ASP.NET Core 3x را ملاحظه میکنید. با توجه به عمومی بودن این روش، قسمت ConfigureWebHostDefaults آن، همان کار WebHost.CreateDefaultBuilder نگارش 2 ASP.NET Core را انجام میدهد.
ASP.NET Core 5 به همراه تغییرات مهمی در این زمینه نبود و تنها با تغییر target framework در فایل csproj و به روز رسانی بستههای نیوگت مرتبط، کار ارتقاء به نگارش جدید در آن صورت میگرفت.
معرفی WebApplicationBuilder در ASP.NET Core 6x
در دات نت 6، روش آغاز برنامههای وب بطور کامل تغییر کردهاست. در تمام نگارشهای پیشین ASP.NET Core، همواره شاهد دو فایل Program.cs و Startup.cs بودیم؛ اما در اینجا فقط یک فایل Program.cs بیشتر وجود ندارد. هر چند همانطور که در مطلب «ارتقاء فایلهای آغازین برنامههای ASP.NET Core 5x به 6x» نیز عنوان شد، میتوان همان سبک و سیاق پیشین را نیز برگرداند و از این لحاظ محدودیتی وجود ندارد.
در اینجا نمونهای از فایل Program.cs برنامههای دات نت 6 را مشاهده میکنید که تغییرات قابل ملاحظهای داشتهاست:
- استفاده از top level statements
- استفاده از usingهای سراسری و سایر قابلیتهای C# 10.0
- حذف کامل کلاس Startup؛ اینبار همه چیز فقط یک فایل است.
دیدگاهی را که در اینجا بکار گرفتهاند شامل این موارد است و بیشتر تازهواردان را مدنظر دارند:
- برای شروع به کار، using statements اضافی هستند؛ پس حذف شدهاند!
- برای شروع به کار، namespaces اضافی هستند؛ پس حذف شدهاند!
- برای شروع به کار، Program.Main ... هم اضافی است!
- تنظیمات بین دو فایل Program.cs و Startup.cs تقیسم نشدهاند و همهچیز یکجا است و این هم نیازی به توضیح اضافی به تازهواردان، ندارد.
- همچنین همانطور که عنوان شد، « ... متد UseStartup به صورت خودکار به دنبال متدهای ویژهی ConfigureServices و Configure در کلاس آغازین برنامه گشته و آنها را فراخوانی میکند تا تنظیمات تزریق وابستگیها، مسیریابیها و میانافزارها صورت گیرند ...»
نکتهی مهم این توضیح، این است که کلاس Startup، هیچ اینترفیسی را پیاده سازی نمیکند. یعنی نام این متدها، دقیقا باید به همین صورت باشند (بدون اینکه قرار دادی توسط یک اینترفیس برای آنها وضع شده باشد) و ... چرا واقعا باید به این صورت باشد؟! به همین جهت اینها هم حذف شدهاند!
- در اینجا WebApplication هم مشاهده میشود؛ اما آیا واقعا نیازی به آن است؟
پاسخ: خیر! WebApplication.CreateBuilder ای که در اینجا ملاحظه میکنید در حقیقت ساده شدهی قطعه کد زیر از کدهای ASP.NET Core 3x/5x است:
که ... WebApplication.CreateBuilder بدون شک سادهتر از قطعه کد فوق است. همچنین پس از یک سطر زیر:
با استفاده از خاصیت builder.Configuration، میتوان به تنظیمات برنامه دسترسی یافت و یا با استفاده از builder.Services میتوان سرویسهای جدیدی را ثبت کرد:
و یا با استفاده از خاصیت builder.Logging میتوان تنظیمات ثبت وقایع برنامه را تغییر داد:
همچنین در اینجا زمانیکه کار تنظیمات به پایان رسید، نمونهای از WebApplication را به صورت زیر تولید کرده:
و اکنون میتوان تنظیمات میانافزارها و یا مسیریابیها را انجام داد:
که در مقایسه با نگارشهای قبلی، بسیار سادهتر شده، هرچند مرز بین اینها و ترتیب آنها اندکی کمرنگتر شدهاست.
- استفاده از WebHost.CreateDefaultBuilder: این روش جهت تنظیم شروع به کار یک برنامهی ASP.NET Core 2x مورد استفاده قرار میگرفت.
- استفاده از Host.CreateDefaultBuilder: روش پیشفرض آغاز برنامههای وب NET Core 3x. و NET 5x. که با معرفی generic host، امکان تهیهی Worker services را میسر کردند.
- استفاده از WebApplication.CreateBuilder: روش جدید شروع به کار با برنامههای وب مبتنی بر NET 6.
استفاده از WebHost.CreateDefaultBuilder در ASP.NET Core 2.x
در نگارش اول ASP.NET Core، مفهومی به نام هاست پیشفرض (default host) وجود نداشت. یکی از مهمترین نکات درنظر گرفته شده در طراحی ASP.NET Core، مفهوم «هزینه کردن به ازای احتیاج» است. یعنی اگر نیاز به قابلیتی نیست، نباید وجود داشته باشد. به این ترتیب یک قالب آغازین نباید به همراه تعداد زیادی بستههای NuGet و مقدار زیادی کد برای تنظیم باشد؛ زمانیکه واقعا قرار نیست از تمام آنها استفاده شود. به همین جهت از نگارش 2، شروع به سادهکردن این قالب اولیه کردند و در این زمان، WebHost.CreateDefaultBuilder ارائه شد. این تنظیم به ظاهر ساده، کدهای پیشفرض مورد نیاز قابل توجهی را جهت ساخت سادهی IWebHostBuilder و IWebHost به همراه دارد. در قالب به همراه آن، بین مفاهیم تنظیمات application و host تفاوت قائل شدهاند؛ یعنی دو فایل Program.cs را برای تنظیمات application و فایل Startup.cs را برای ارائهی تنظیمات هاست، تدارک دیدند.
در این طراحی، بین میدان دید Program و Startup تفاوت وجود دارد. هدف از Program، ارائهی تنظیمات زیرساختی برنامه، مانند تنظیمات HTTP Server، یکپارچگی با IIS و امثال آن شد و عموما در طول عمر برنامه ثابت است. اما برای تغییر رفتار برنامه، بیشتر تغییرات و تنظیمات، به Startup محول شدند؛ مانند تنظیمات تزریق وابستگیها، تنظیمات میانافزارها، مسیریابیها و غیره.
public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build(); }
معرفی Generic Host در ASP.NET Core 3.x/5
ASP.NET Core 3.x به همراه تغییرات بزرگی در کدهای آغازین برنامههای ASP.NET Core بود. تا پیش از آن، امکانات پایهی ASP.NET Core تنها برای مقاصد وب قابل استفاده بود. اما در نگارشهای 3x، با ارائهی یک هاست عمومی، امکان پشتیبانی از سایر برنامهها، مانند worker services را نیز میسر کرد؛ برای مثال پیشتیبانی از کارهای طولانی پس زمینه، هاست سرویسهای gRPC، هاست سرویسهای ویندوز و غیره. هدف اصلی از این تغییرات، به اشتراک گذاری فریمورک پایهی ASP.NET Core که تنها برای برنامههای وب ساخته شده بود و به همراه امکاناتی مانند تنظیمات، ثبت وقایع، تزریق وابستگیها و غیره بود، با سایر انواع برنامههای یاد شده است. برای رسیدن به این هدف، Web Host نگارش 2x، به Generic Host نگارش 3x تغییر کرد و سپس ASP.NET Core برفراز آن بنا شد. اینبار بجای IWebHostBuilder، نمونهی جدید IHostBuilder را داریم:
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }; } }
ASP.NET Core 5 به همراه تغییرات مهمی در این زمینه نبود و تنها با تغییر target framework در فایل csproj و به روز رسانی بستههای نیوگت مرتبط، کار ارتقاء به نگارش جدید در آن صورت میگرفت.
معرفی WebApplicationBuilder در ASP.NET Core 6x
در دات نت 6، روش آغاز برنامههای وب بطور کامل تغییر کردهاست. در تمام نگارشهای پیشین ASP.NET Core، همواره شاهد دو فایل Program.cs و Startup.cs بودیم؛ اما در اینجا فقط یک فایل Program.cs بیشتر وجود ندارد. هر چند همانطور که در مطلب «ارتقاء فایلهای آغازین برنامههای ASP.NET Core 5x به 6x» نیز عنوان شد، میتوان همان سبک و سیاق پیشین را نیز برگرداند و از این لحاظ محدودیتی وجود ندارد.
var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.MapGet("/", () => "Hello World!"); app.MapRazorPages(); app.Run();
- استفاده از top level statements
- استفاده از usingهای سراسری و سایر قابلیتهای C# 10.0
- حذف کامل کلاس Startup؛ اینبار همه چیز فقط یک فایل است.
دیدگاهی را که در اینجا بکار گرفتهاند شامل این موارد است و بیشتر تازهواردان را مدنظر دارند:
- برای شروع به کار، using statements اضافی هستند؛ پس حذف شدهاند!
- برای شروع به کار، namespaces اضافی هستند؛ پس حذف شدهاند!
- برای شروع به کار، Program.Main ... هم اضافی است!
- تنظیمات بین دو فایل Program.cs و Startup.cs تقیسم نشدهاند و همهچیز یکجا است و این هم نیازی به توضیح اضافی به تازهواردان، ندارد.
- همچنین همانطور که عنوان شد، « ... متد UseStartup به صورت خودکار به دنبال متدهای ویژهی ConfigureServices و Configure در کلاس آغازین برنامه گشته و آنها را فراخوانی میکند تا تنظیمات تزریق وابستگیها، مسیریابیها و میانافزارها صورت گیرند ...»
نکتهی مهم این توضیح، این است که کلاس Startup، هیچ اینترفیسی را پیاده سازی نمیکند. یعنی نام این متدها، دقیقا باید به همین صورت باشند (بدون اینکه قرار دادی توسط یک اینترفیس برای آنها وضع شده باشد) و ... چرا واقعا باید به این صورت باشد؟! به همین جهت اینها هم حذف شدهاند!
- در اینجا WebApplication هم مشاهده میشود؛ اما آیا واقعا نیازی به آن است؟
پاسخ: خیر! WebApplication.CreateBuilder ای که در اینجا ملاحظه میکنید در حقیقت ساده شدهی قطعه کد زیر از کدهای ASP.NET Core 3x/5x است:
var hostBuilder = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddRazorPages(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.Configure((ctx, app) => { if (ctx.HostingEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", () => "Hello World!"); endpoints.MapRazorPages(); }); }); }); hostBuilder.Build().Run();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(); builder.Services.AddSingleton<MyThingy>();
builder.Logging.AddFile();
var app = builder.Build();
app.UseStaticFiles(); app.MapRazorPages();
مطالب
EF Code First #15
EF Code first و بانکهای اطلاعاتی متفاوت
در آخرین قسمت از سری EF Code first بد نیست نحوه استفاده از بانکهای اطلاعاتی دیگری را بجز SQL Server نیز بررسی کنیم. در اینجا کلاسهای مدل و کدهای مورد استفاده نیز همانند قسمت 14 است و تنها به ذکر تفاوتها و نکات مرتبط اکتفاء خواهد شد.
حالت کلی پشتیبانی از بانکهای اطلاعاتی مختلف توسط EF Code first
EF Code first با کلیه پروایدرهای تهیه شده برای ADO.NET 3.5 که پشتیبانی از EF را لحاظ کرده باشند، به خوبی کار میکند. پروایدرهای مخصوص ADO.NET 4.0، تنها سه گزینه DeleteDatabase/CreateDatabase/DatabaseExists را نسبت به نگارش قبلی بیشتر دارند و EF Code first ویژگیهای بیشتری را طلب نمیکند.
بنابراین اگر حین استفاده از پروایدر ADO.NET مخصوص بانک اطلاعاتی خاصی با پیغام «CreateDatabase is not supported by the provider» مواجه شدید، به این معنا است که این پروایدر برای دات نت 4 به روز نشده است. اما به این معنا نیست که با EF Code first کار نمیکند. فقط باید یک دیتابیس خالی از پیش تهیه شده را به برنامه معرفی کنید تا مباحث Database Migrations به خوبی کار کنند؛ یا اینکه کلا میتوانید Database Migrations را خاموش کرده (متد Database.SetInitializer را با پارامتر نال فراخوانی کنید) و فیلدها و جداول را دستی ایجاد کنید.
استفاده از EF Code first با SQLite
برای استفاده از SQLite در دات نت ابتدا نیاز به پروایدر ADO.NET آن است: «مکان دریافت درایورهای جدید SQLite مخصوص دات نت»
ضمن اینکه به نکته «استفاده از اسمبلیهای دات نت 2 در یک پروژه دات نت 4» نیز باید دقت داشت.
و یکی از بهترین management studio هایی که برای آن تهیه شده: «SQLite Manager»
پس از دریافت پروایدر آن، ارجاعی را به اسمبلی System.Data.SQLite.dll به برنامه اضافه کنید.
سپس فایل کانفیگ برنامه را به نحو زیر تغییر دهید:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Data Source=CodeFirst.db"
providerName="System.Data.SQLite"/>
</connectionStrings>
</configuration>
همانطور که ملاحظه میکنید، تفاوت آن با قبل، تغییر connectionString و providerName است.
اکنون اگر همان برنامه قسمت قبل را اجرا کنیم به خطای زیر برخواهیم خورد:
«The given key was not present in the dictionary»
در این مورد هم توضیح داده شد. سه گزینه DeleteDatabase/CreateDatabase/DatabaseExists در پروایدر جاری SQLite برای دات نت وجود ندارد. به همین جهت نیاز است فایل «CodeFirst.db» ذکر شده در کانکشن استرینگ را ابتدا دستی درست کرد.
برای مثال از افزونه SQLite Manager استفاده کنید. ابتدا یک بانک اطلاعاتی خالی را درست کرده و سپس دستورات زیر را بر روی بانک اطلاعاتی اجرا کنید تا دو جدول خالی را ایجاد کند (در برگه Execute sql افزونه SQLite Manager):
CREATE TABLE [Payees](
[Id] [integer] PRIMARY KEY AUTOINCREMENT NOT NULL,
[Name] [text] NULL,
[CreatedOn] [datetime] NOT NULL,
[CreatedBy] [text] NULL,
[ModifiedOn] [datetime] NOT NULL,
[ModifiedBy] [text] NULL
);
CREATE TABLE [Bills](
[Id] [integer] PRIMARY KEY AUTOINCREMENT NOT NULL,
[Amount] [float](18, 2) NOT NULL,
[Description] [text] NULL,
[CreatedOn] [datetime] NOT NULL,
[CreatedBy] [text] NULL,
[ModifiedOn] [datetime] NOT NULL,
[ModifiedBy] [text] NULL,
[Payee_Id] [integer] NULL
);
سپس سطر زیر را نیز به ابتدای برنامه اضافه کنید:
Database.SetInitializer<Sample09Context>(null);
به این ترتیب database migrations خاموش میشود و اکنون برنامه بدون مشکل کار خواهد کرد.
فقط باید به یک سری نکات مانند نوع دادهها در بانکهای اطلاعاتی مختلف دقت داشت. برای مثال integer در اینجا از نوع Int64 است؛ بنابراین در برنامه نیز باید به همین ترتیب تعریف شود تا نگاشتها به درستی انجام شوند.
در کل تنها مشکل پروایدر فعلی SQLite عدم پشتیبانی از مباحث database migrations است. این مورد را خاموش کرده و تغییرات ساختار بانک اطلاعاتی را به صورت دستی به بانک اطلاعاتی اعمال کنید. بدون مشکل کار خواهد کرد.
البته اگر به دنبال پروایدری تجاری با پشتیبانی از آخرین نگارش EF Code first هستید، گزینه زیر نیز مهیا است:
http://devart.com/dotconnect/sqlite/
برای مثال اگر علاقمند به استفاده از حالت تشکیل بانک اطلاعاتی SQLite در حافظه هستید (با رشته اتصالی ویژه Data Source=:memory:;Version=3;New=True;)، فعلا تنها گزینه مهیا استفاده از پروایدر تجاری فوق است؛ زیرا مبحث Database Migrations را به خوبی پشتیبانی میکند.
استفاده از EF Code first با SQL Server CE
قبلا در مورد «استفاده از SQL-CE به کمک NHibernate» مطلبی را در این سایت مطالعه کردهاید. سه مورد اول آن با EF Code first یکی است و تفاوتی نمیکند (یک سری بحث عمومی مشترک است). البته با یک تفاوت؛ در اینجا EF Code first قادر است یک بانک اطلاعاتی خالی SQL Server CE را به صورت خودکار ایجاد کند و نیازی نیست تا آنرا دستی ایجاد کرد. مباحث database migrations و به روز رسانی خودکار ساختار بانک اطلاعاتی نیز در اینجا پشتیبانی میشود.
برای استفاده از آن ابتدا ارجاعی را به اسمبلی System.Data.SqlServerCe.dll قرار گرفته در مسیر Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop اضافه کنید.
سپس رشته اتصالی به بانک اطلاعاتی و providerName را به نحو زیر تغییر دهید:
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Data Source=mydb.sdf;Password=1234;Encrypt Database=True"
providerName="System.Data.SqlServerCE.4.0"/>
</connectionStrings>
بدون نیاز به هیچگونه تغییری در کدهای برنامه، همین مقدار تغییر در تنظیمات ابتدایی برنامه برای کار با SQL Server CE کافی است.
ضمنا مشکلی هم با فیلد Identity در آخرین نگارش EF Code first وجود ندارد؛ برخلاف حالت database first آن که پیشتر این اجازه را نمیداد و خطای «Server-generated keys and server-generated values are not supported by SQL Server Compact» را ظاهر میکرد.
استفاده از EF Code first با MySQL
برای استفاده از EF Code first با MySQL (نگارش 5 به بعد البته) ابتدا نیاز است پروایدر مخصوص ADO.NET آنرا دریافت کرد: (^)
که از EF نیز پشتیبانی میکند. پس از نصب آن، ارجاعی را به اسمبلی MySql.Data.dll قرار گرفته در مسیر Program Files\MySQL\MySQL Connector Net 6.5.4\Assemblies\v4.0 به پروژه اضافه نمائید.
سپس رشته اتصالی و providerName را به نحو زیر تغییر دهید:
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Datasource=localhost; Database=testdb2; Uid=root; Pwd=123;"
providerName="MySql.Data.MySqlClient"/>
</connectionStrings>
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient"/>
<add name="MySQL Data Provider"
invariant="MySql.Data.MySqlClient"
description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
همانطور که مشاهده میکنید در اینجا شماره نگارش دقیق پروایدر مورد استفاده نیز ذکر شده است. برای مثال اگر چندین پروایدر روی سیستم نصب است، با مقدار دهی DbProviderFactories میتوان از نگارش مخصوصی استفاده کرد.
با این تغییرات پس از اجرای برنامه قسمت قبل، به خطای زیر برخواهیم خورد:
The given key was not present in the dictionary
توضیحات این مورد با قسمت SQLite یکی است؛ به عبارتی نیاز است بانک اطلاعاتی testdb را دستی درست کرد. همچنین جداول و فیلدها را نیز باید دستی ایجاد کرد و database migrations را نیز باید خاموش کرد (پارامتر Database.SetInitializer را به نال مقدار دهی کنید).
برای این منظور یک دیتابیس خالی را ایجاد کرده و سپس دو جدول زیر را به آن اضافه کنید:
CREATE TABLE IF NOT EXISTS `bills` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Amount` float DEFAULT NULL,
`Description` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`CreatedOn` datetime NOT NULL,
`CreatedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`ModifiedOn` datetime NOT NULL,
`ModifiedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`Payee_Id` int(11) NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `payees` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`CreatedOn` datetime NOT NULL,
`CreatedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
`ModifiedOn` datetime NOT NULL,
`ModifiedBy` varchar(400) CHARACTER SET utf8 COLLATE utf8_persian_ci NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci AUTO_INCREMENT=1 ;
پس از این تغییرات، برنامه بدون مشکل اجرا خواهد شد (ایجاد بانک اطلاعاتی خالی به همراه ایجاد ساختار جداول و خاموش کردن database migrations که توسط این پروایدر پشتیبانی نمیشود).
به علاوه پروایدر تجاری دیگری هم در سایت devart.com برای MySQL و EF Code first مهیا است که مباحث database migrations را به خوبی مدیریت میکند.
مشکل!
اگر به همین نحو برنامه را اجرا کنیم، فیلدهای یونیکد فارسی ثبت شده در MySQL با «??????? ?? ????» مقدار دهی خواهند شد و تنظیم CHARACTER SET utf8 COLLATE utf8_persian_ci نیز کافی نبوده است (این مورد با SQLite یا نگارشهای مختلف SQL Server بدون مشکل کار میکند و نیاز به تنظیم اضافهتری ندارد):
ALTER TABLE `bills` DEFAULT CHARACTER SET utf8 COLLATE utf8_persian_ci
برای رفع این مشکل توصیه شده است که CharSet=UTF8 را به رشته اتصالی به بانک اطلاعاتی اضافه کنیم. اما در این حالت خطای زیر ظاهر میشود:
The provider did not return a ProviderManifestToken string
این مورد فقط به اشتباه بودن تعاریف رشته اتصالی بر میگردد؛ یا عدم پشتیبانی از تنظیم اضافهای که در رشته اتصالی ذکر شده است.
مقدار صحیح آن دقیقا مساوی CHARSET=utf8 است (با همین نگارش و رعایت کوچکی و بزرگی حروف؛ مهم!):
<connectionStrings>
<clear/>
<add name="Sample09Context"
connectionString="Datasource=localhost; Database=testdb; Uid=root; Pwd=123;CHARSET=utf8"
providerName="MySql.Data.MySqlClient"/>
</connectionStrings>
به این ترتیب، مشکل ثبت عبارات یونیکد فارسی برطرف میشود (البته جدول هم بهتر است به DEFAULT CHARACTER SET utf8 COLLATE utf8_persian_ci تغییر پیدا کند؛ مطابق دستور Alter ایی که در بالا ذکر شد).
در زمان CRUD مشکل Update وجود دارد یعنی در زمان فراخوانی
در کنترلر MVC خطایی با این عنوان اتفاق میافتد
_unitofwork.MarkAsChanged(entity);
An unhandled exception has occurred while executing the request System.InvalidOperationException: The instance of entity type 'Country' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.
اشتراکها
Visual Studio 2017 15.5.7 منتشر شد
Team Explorer support for TLSv1.2
- We have updated the Git and the Git Credential Manager components that ship in Visual Studio.
- The optional Git for Windows component has also been updated.
- This update allows Git to connect to services that have deprecated support for TLSv1 and TLSv1.1 in favor of TLSv1.2.
Issues Fixed in this Release
These are the customer-reported issues addressed in this release:
- Projects targeting .NET Core 2.1 or newer are not supported by Visual Studio 2017 version 15.5.
- Fixed issue where installation of the SDK for .NET Core 2.1 or newer would cause the option to create ASP.NET Core 2.0 Web applications to disappear.
اشتراکها