public class MyContext : DbContext, IUnitOfWork { // ... public IList<T> GetRows<T>(string sql, params object[] parameters) where T: class { return this.Database.SqlQuery<T>(sql, parameters).ToList(); } }
گاهی از اوقات نیاز میشود تا در یک لیست، آیتمهای تکراری موجود را مشخص کرد. به صورت پیش فرض متد Distinct برای حذف مقادیر تکراری در یک لیست با استفاده از LINQ موجود است که البته آنهم اما و اگرهایی دارد که در ادامه به آن پرداخته خواهد شد، اما باز هم این مورد پاسخ سؤال اصلی نیست (نمیخواهیم موارد تکراری را حذف کنیم).
برای حذف آیتمهای تکراری از یک لیست جنریک میتوان متد زیر را نوشت:
public static List<T> RemoveDuplicates<T>(List<T> items)
{
return (from s in items select s).Distinct().ToList();
}
public static void TestRemoveDuplicates()
{
List<string> sampleList =
new List<string>() { "A1", "A2", "A3", "A1", "A2", "A3" };
sampleList = RemoveDuplicates(sampleList);
foreach (var item in sampleList)
Console.WriteLine(item);
}
public class Employee
{
public int ID { get; set; }
public string FName { get; set; }
public int Age { get; set; }
}
public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};
lstEmp = RemoveDuplicates<Employee>(lstEmp);
foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
برای رفع این مشکل باید از آرگومان دوم متد distinct جهت معرفی وهلهای از کلاسی که اینترفیس IEqualityComparer را پیاده سازی میکند، کمک گرفت.
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
//آیا دقیقا یک وهله هستند؟
if (Object.ReferenceEquals(x, y)) return true;
//آیا یکی از وهلهها نال است؟
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;
return x.Age == y.Age && x.FName == y.FName && x.ID == y.ID;
}
public int GetHashCode(Employee obj)
{
if (Object.ReferenceEquals(obj, null)) return 0;
int hashTextual = obj.FName == null ? 0 : obj.FName.GetHashCode();
int hashDigital = obj.Age.GetHashCode();
return hashTextual ^ hashDigital;
}
}
public static List<T> RemoveDuplicates<T>(List<T> items, IEqualityComparer<T> comparer)
{
return (from s in items select s).Distinct(comparer).ToList();
}
public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};
lstEmp = RemoveDuplicates(lstEmp, new EmployeeComparer());
foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
سؤال: برای یافتن آیتمهای تکراری یک لیست چه باید کرد؟
احتمالا مقاله "روشهایی برای حذف رکوردهای تکراری" را به خاطر دارید. اینجا هم میتوان کوئری LINQ ایی را نوشت که رکوردها را بر اساس سن، گروه بندی کرده و سپس گروههایی را که بیش از یک رکورد دارند، انتخاب نماید.
public static void FindDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};
var query = from c in lstEmp
group c by c.Age into g
where g.Count() > 1
select new { Age = g.Key, Count = g.Count() };
foreach (var item in query)
{
Console.WriteLine("Age {0} has {1} records", item.Age, item.Count);
}
}
نیاز به درایور OleDB مخصوص بانکهای اطلاعاتی قدیمی
برای کار با بانکهای اطلاعاتی قدیمی از طریق ADO.NET، نیاز است بتوان به نحوی با آنها ارتباط برقرار کرد و اینکار از طریق استاندارد OleDB که صرفا مختص به ویندوز است، قابل انجام است. برای مثال برای کار با فاکسپرو نیز در ابتدا باید درایور OleDB آنرا نصب کرد که ... هیچکدام از لینکهای قدیمی مایکروسافت در این زمینه دیگر وجود خارجی ندارند! آخرین نگارش مرتبط را میتوانید در این آدرس و ذیل نام VFPOLEDBSetup.msi دریافت کنید (نگارش 9 را نصب میکند).
نیاز به دریافت بستهی System.Data.OleDb
در اولین قدم جهت کار با درایور OleDB نصب شده، باید یک اتصال را توسط نمونه سازی شیء OleDbConnection ایجاد کرد که ... این شیء هم شناسایی نمیشود. به همین جهت باید بستهی نیوگت مرتبط با آنرا به صورت جداگانهای دریافت و نصب کرد:
<ItemGroup> <PackageReference Include="System.Data.OleDb" Version="7.0.0"/> </ItemGroup>
برنامهی مبتنی بر درایور OleDB فاکسپرو اجرا نمیشود!
اولین سعی در برقراری ارتباط با درایور OleDB نصب شده، با خطای زیر خاتمه مییابد:
The 'VFPOLEDB' provider is not registered on the local machine.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <PlatformTarget>x86</PlatformTarget> </PropertyGroup>
روش یافتن نام پروایدر OleDB نصب شده
رشتههای اتصالی OleDB، با =Provider، شروع میشوند. اکنون این سؤال مطرح میشود که پس از نصب VFPOLEDBSetup.ms یاد شده، دقیقا چه پروایدری به سیستم اضافه شدهاست و نام آن چیست؟
برای اینکار میتوان از چندسطر زیر برای یافتن نام تمام پروایدرهای OleDB نصب شدهی بر روی سیستم استفاده کرد:
var oleDbEnumerator = new OleDbEnumerator(); using var elements = oleDbEnumerator.GetElements(); foreach (DataRow row in elements.Rows) { Console.WriteLine($"{row["SOURCES_DESCRIPTION"]}: {row["SOURCES_NAME"]}"); }
Microsoft OLE DB Provider for SQL Server: SQLOLEDB MSDataShape: MSDataShape SQL Server Native Client 11.0: SQLNCLI11 Microsoft OLE DB Provider for Visual FoxPro: VFPOLEDB OLE DB Provider for Microsoft Directory Services: ADsDSOObject Microsoft OLE DB Driver for SQL Server: MSOLEDBSQL Microsoft OLE DB Driver for SQL Server Enumerator: MSOLEDBSQL Enumerator SQL Server Native Client 11.0 Enumerator: SQLNCLI11 Enumerator Microsoft OLE DB Provider for Search: Windows Search Data Source Microsoft OLE DB Provider for ODBC Drivers: MSDASQL Microsoft OLE DB Enumerator for ODBC Drivers: MSDASQL Enumerator Microsoft OLE DB Provider for Analysis Services 14.0: MSOLAP Microsoft OLE DB Provider for Analysis Services 14.0: MSOLAP Microsoft Jet 4.0 OLE DB Provider: Microsoft.Jet.OLEDB.4.0 Microsoft OLE DB Enumerator for SQL Server: SQLOLEDB Enumerator Microsoft OLE DB Simple Provider: MSDAOSP Microsoft OLE DB Provider for Oracle: MSDAORA
Microsoft OLE DB Provider for Visual FoxPro: VFPOLEDB
روش خواندن اطلاعات یک بانک اطلاعاتی فاکس پرو
پس از این مقدمات و تنظیمات، اکنون میتوانیم از قطعه کد متداول ADO.NET زیر، جهت خواندن اطلاعات یک بانک اطلاعاتی فاکسپرو، استفاده کنیم:
var connectionString = "Provider=VFPOLEDB;Data Source=" + @"C:\path\Db.DBF;Password=;Collating Sequence=MACHINE"; using var dbConnection = new OleDbConnection(connectionString); using var dataAdapter = new OleDbDataAdapter("select family from Db.DBF", dbConnection); using var dataset = new DataSet(); dataAdapter.Fill(dataset, "table1"); var sb = new StringBuilder(); foreach (DataRow dataRow in dataset.Tables[0].Rows) { var iranSystem = dataRow[0] as string; var unicode = iranSystem.FromIranSystemToUnicode(); if (!string.IsNullOrWhiteSpace(unicode)) { sb.AppendLine($"[DataRow(\"{iranSystem}\", \"{unicode}\")]"); } }
- نام پروایدر در رشته اتصالی، به VFPOLEDB تنظیم شدهاست.
- select انجام شده بر روی نام فایل dbf انجام میشود. یعنی هر فایل dbf، یک جدول را تشکیل میدهد.
- اگر نام فیلدهای موجود را نمیدانید، بجای select family از * select استفاده کنید و سپس بر روی DataSet پر شده، یک break-point را قرار دهید تا بتوانید نام تمام ستونها را از آن استخراج کنید.
- رشتهای را که توسط درایور فاکسپرو دریافت میکنید، یک رشتهی اسکی سیستم عامل داس است که در دات نت، با encoding مساوی 1252 شناخته میشود. یعنی encoding این رشتهی دریافتی، یونیکد پیشفرض داتنت نیست و باید توسط متد Encoding.GetEncoding(1252).GetBytes پردازش شود. که در نگارشهای جدید دات نت، این کدپیجها به صورت پیشفرض مهیا نیستند و باید در ابتدا ثبت شوند تا قابل استفاده شوند:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)
هر ردیف از جدول نمایانگر یک فایل یا پوشه است، بنابراین به ستونی نیاز داریم که بتوانیم این موضوع را نشان دهیم. بر این پایه از ستون is_directory بهره میبریم که 1 بودن آن نشاندهندهی این است که این ردیف از جدول به یک پوشه ارجاع دارد.
نام فایل یا پوشه در ستونی به نام name نگهداری میشود که رشتهای از نوع (nvarchar(255 است. افزون بر این ستون، ستونهای دیگری نیز در این جدول وجود دارد که ویژگیهای یک فایل مانند پنهانبودن، فقطخواندنی و ... توسط آن توسط آن به دست میآید. ستون پسین file_stream نام دارد که برای پوشهها، محتوای آن Null است. علت آن این است که محتوای واقعی فایل در این ستون نگهداری میشود. در واقع یک (varbinary(max با ویژگیهای fileStream است که محتوای باینری آن در سیستم فایل NTFS ذخیره میشود. مدیریت پشت صحنهی این ستون برعهدهی SQL Server است.
افزون بر این 14 ستون، هر FileTable شامل سه ستون محاسباتی به شرح زیر است:
ستون parent_path_locator نتیجهی فراخوانی تابع (GetAncestor(1 در ستون path_locator است که جهت به دست آوردن پوشهی پدر یک فایل و پوشه استفاده میشود. ستون file_type که از مقدار رشتهای ستون name تجزیه شده است، پسوند فایل را برمیگرداند. و ستون cached_file_size اندازهی بایت ذخیرهشده ستون file_stream را برمیگرداند. با این ساختار ثابت در اینجا، هر FileTable هر آنچه از File System نیاز دارید در یک پوشهی اشتراکی به شما میدهد.
این یعنی نمایش بیواسطه FileTable به هر کاربر یا برنامه. به طوری که برای نمایش یا بهروزرسانی جدول میتوانید از روش استاندارد I/O مانند کشیدن و رهاکردن با Windows Explorer یا برنامهنویسی با System.IO.FileStream و APIهای ویندوز استفاده کنید. اینچنین:
- ایجاد یک فایل یا پوشه در سیستم فایل -> افزودن یک ردیف به جدول
- افزودن یک ردیف به جدول -> ایجاد یک فایل یا پوشه در سیستم فایل
با کپی فایلها در مسیر بالا، به صورت خودکار رکوردهای زیر در جدول PhotoTable در پایگاهدادهها افزوده میشود:
به طور خلاصه پیش از این برای افزودن به FileStream دو راه کار پیش رو داشتید. یکی استفاده از T-SQL و دیگر sqlFileStream اکنون SQL Server 2012 راه کار سوم را پیشنهاد میکند. استفاده از File System در این روش FileStream به طور خودکار پر میشود.
پیش از ساخت یک FileTable بیان این نکته دارای اهمیت است که با کپی فایلها و پوشهها هیچ چیز جدیدی به NTFS افزوده نمیشود بلکه محتوای فایل به FileStream افزوده میشود و SQL Server با بررسی همزمان FileStream و FileTable نمایشی از ردیفهای FileTable به صورت یک پوشهی اشتراکی نشان میدهد. این نکته پاسخی به این پرسش خواهد بود که آیا با استفاده از FileTable حجم پایگاهدادهها دو برابر خواهد شد و در نتیجه دشواریها و چالشهای نگهداری و پشتیبانی را پیش رو خواهیم داشت!؟ که پاسخ "خیر" خواهد بود.
ایجاد یک FileTable
پیش از این در همین تارنما، روش فعال کردن FileStream در SQL Server را آموزش دیده اید. اگر درست به خاطر داشته باشید، چیزی شبیه به دستورهای زیر بود:
CREATE DATABASE MyFileArchive ON PRIMARY (NAME = MyFileArchive_data, FILENAME = 'C:\Demo\MyFileArchive_data.mdf'), FILEGROUP FileStreamGroup CONTAINS FILESTREAM (NAME = PhotoFileLibrary_blobs, FILENAME = 'C:\Demo\MyFiles') LOG ON (NAME = PhotoFileLibrary_log, FILENAME = 'C:\Demo\MyFileArchive_log.ldf')
FileTable به FileStream متکی است؛ بر این پایه پیش از ایجاد یک FileTable باید FileStream را روی پایگاهدادهها فعال کنیم. این کار با یک تعریف درست توسط بند FILEGROUP…CONTAINS FILESTREAM انجام میشود.
برای ایجاد FileTable تنها کافی است که بند WITH FILESTREAM را به دستور CREATE DATABASE بیفزایید. (یا برای فعالکردن FileTable روی یک پایگاهدادهی ساخته شده بند SET FILESTREAM را در دستور ALTER DATABASE بنویسید.) در این بند، از DIRECTORY_NAME برای نامگذاری یک پوشه برای پایگاهدادهها استفاده میکنیم. این پوشه در یک پوشه ریشه به نام SQL Server instance نمایش داده خواهد شد. بخش دوم بند NON_TRANSACTED_ACCESS=FULL است که دسترسی غیرتراکنشی را فعال میکند. با این کار برای هر FileTable در پایگاه داده یک زیرپوشه درون پوشهای که به نام DIRECTORY_NAME نامگذاری شده است؛ ساخته میشود.
با توجه به آنچه گفته شد برای ایجاد یک پایگاهداده با امکان ساخت FileTable دستورهای زیر را اجرا کنید:
CREATE DATABASE MyFileArchive ON PRIMARY (NAME = MyFileArchive_data, FILENAME = 'C:\Demo\MyFileArchive_data.mdf'), FILEGROUP FileStreamGroup CONTAINS FILESTREAM (NAME = PhotoFileLibrary_blobs, FILENAME = 'C:\Demo\MyFiles') LOG ON (NAME = PhotoFileLibrary_log, FILENAME = 'C:\Demo\MyFileArchive_log.ldf') WITH FILESTREAM (DIRECTORY_NAME='FilesLibrary', NON_TRANSACTED_ACCESS=FULL)
USE MyFileArchive GO CREATE TABLE PhotoTable AS FileTable GO
دنباله دارد ...
- به صورت لیست
- به صورت لیست sort شده براساس Name
- فیلتر بر روی لیستی از فیلدهای موجود
- این لیست باید شامل تمامی دادههای موجود (چه در رم و چه در دیتابیس) باشد.
private static void GetLocalDestinationCount() { using (var context = new BreakAwayContext()) { var count = context.Destinations.Local.Count; Console.WriteLine("Destinations in memory: {0}", count); } }
استفاده از متد Load
private static void GetLocalDestinationCount() { using (var context = new BreakAwayContext()) { foreach (var destination in context.Destinations) { Console.WriteLine(destination.Name); } var count = context.Destinations.Local.Count; Console.WriteLine("Destinations in memory: {0}", count); } }
private static void GetLocalDestinationCountWithLoad() { using (var context = new BreakAwayContext()) { context.Destinations.Load(); var count = context.Destinations.Local.Count; Console.WriteLine("Destinations in memory: {0}", count); } }
private static void LoadAustralianDestinations() { using (var context = new BreakAwayContext()) { var query = from d in context.Destinations where d.Country == "Australia" select d; query.Load(); var count = context.Destinations.Local.Count; Console.WriteLine("Aussie destinations in memory: {0}", count); } }
private static void LocalLinqQueries() { using (var context = new BreakAwayContext()) { context.Destinations.Load(); var sortedDestinations = from d in context.Destinations.Local orderby d.Name select d; Console.WriteLine("All Destinations:"); foreach (var destination in sortedDestinations) { Console.WriteLine(destination.Name); } } var aussieDestinations = from d in context.Destinations.Local where d.Country == "Australia" select d; Console.WriteLine(); Console.WriteLine("Australian Destinations:"); foreach (var destination in aussieDestinations) { Console.WriteLine(destination.Name); } }
تفاوت بین Linq providerهای مختلف:
Bulk delete و Bulk update در Entity framework
اشکال در استفاده از EntityFramework.Extended
کد زیر جهت بروز رسانی جدول مرتبط براساس رکوردهای جابجا شده در گرید توسط کاربر میباشد:
public bool RecordMoveUpDown(int idPost1, int rowNumber1, int idPost2, int rowNumber2) { var result = true; try { _posts.Update(t => t.Id == idPost1, t => new TPersonalPost { Row = rowNumber2 }); _posts.Update(t => t.Id == idPost2, t => new TPersonalPost { Row = rowNumber1 }); } catch { result = false; } return result; }
تا حالا هیچ مشکلی نداشتم و دستورات مشابه کاملاً انجام میشد، ولی در حال حاضر ((تنها درحالت اجرا، اولین Update خطای زیر مشاهده شده و از ادامه کار جلوگیری میشود :
Object reference not set to an instance of an object .
حتی برای کلاس TPersonalPost یک ctor هم ایجاد کردم ولی تاثیری نداشت.
تفاوت بین شیرپوینت 2013 و 2016
INSERT INTO tableName VALUES ( '{ "name": "User1", "age": 41 }' );
INSERT INTO tableName VALUES ( JSON_ARRAY( JSON_OBJECT( "id", 1, "name", "User1", "age", 31, "skills", JSON_ARRAY("JS", "DB", "Git"), "address", JSON_OBJECT( "country", "Iran", "city", "Tehran") ), JSON_OBJECT( "id", 2, "name", "User2", "age", 31, "skills", JSON_ARRAY("C#"), "address", JSON_OBJECT( "country", "Iran", "city", "Sanandaj" ) ) ) );
UPDATE experiments.tableName SET jsonData = JSON_ARRAY_APPEND(jsonData, '$[1].skills', 'JS', '$[1].skills', 'DB', '$[1].skills', 'Kotlin' ) -- ["C#", "JS", "DB", "Kotlin"]
UPDATE experiments.tableName SET jsonData = JSON_ARRAY_INSERT(jsonData, '$[1].skills[4]', 'TS') -- ["C#", "JS", "DB", "Kotlin", "TS"]
UPDATE experiments.tableName SET jsonData = JSON_INSERT(jsonData, '$[1].address.location', JSON_OBJECT('phone', 8989898))
UPDATE experiments.tableName SET jsonData = JSON_REPLACE(jsonData, '$[1].address.location.phone', 12345656)
UPDATE experiments.tableName SET jsonData = JSON_REMOVE(jsonData, '$[1].address')
UPDATE experiments.tableName SET jsonData = JSON_SET(jsonData, '$[1].address', JSON_OBJECT('country', 'Iran', 'city', '-', 'phone', 12345 )); /* { location: { "city": "-", "phone": 12345, "country": "Iran" } } */ UPDATE experiments.tableName SET jsonData = JSON_SET(jsonData, '$[1].address.city', 'Tehran'); /* { location: { "city": "-", "phone": 12345, "country": "Iran" } } */ UPDATE experiments.tableName SET jsonData = JSON_SET(jsonData, '$[1].address.postcode', '0098'); /* { location: {"city": "Tehran", "phone": 12345, "country": "Iran", "postcode": '0098' } } */
SELECT JSON_EXTRACT(jsonData, '$[1].address.city') FROM experiments.tableName; -- "Tehran" SELECT JSON_UNQUOTE(JSON_EXTRACT(jsonData, '$[1].address.city')) FROM experiments.tableName; -- Tehran
SELECT jsonData -> '$[1].address.city' FROM experiments.tableName; -- "Tehran"
SELECT jsonData ->> '$[1].address.city' FROM experiments.tableName; -- Tehran
SELECT jsonData ->> '$[1].address.city' FROM experiments.tableName WHERE jsonData ->> '$[1].address.city' = 'Tehran';
SELECT JSON_MERGE_PRESERVE('{ "id": "1", "name": "Product One", "price": 12.45, "discount": 10, "rating": 4, "category": ["fashion", "men"], "tags": ["fashion", "men", "jacket", "full sleeve"] }', '{ "id": "2", "name": "Product Two", "price": 30, "discount": 0, "rating": 3, "category": ["fashion", "men"], "tags": ["fashion", "men", "jacket", "full sleeve"] }');
{ "id": ["1", "2"], "name": ["Product One", "Product Two"], "tags": [ "fashion", "men", "jacket", "full sleeve", "fashion", "men", "jacket", "full sleeve" ], "price": [12.45, 30], "rating": [4, 3], "category": ["fashion", "men", "fashion", "men"], "discount": [10, 0] }
SELECT JSON_MERGE_PATCH('{ "id": "1", "name": "Product One", "price": 12.45, "discount": 10, "rating": 4, "category": ["fashion", "men"], "tags": ["fashion", "men", "jacket", "full sleeve"], "sku": "asdf123" }', '{ "id": "2", "name": "Product Two", "price": 30, "discount": 0, "rating": 3, "category": ["fashion", "men"], "tags": ["fashion", "men", "jacket", "full sleeve"] }');
{ "id": "2", "sku": "asdf123", "name": "Product Two", "tags": ["fashion", "men", "jacket", "full sleeve"], "price": 30, "rating": 3, "category": ["fashion", "men"], "discount": 0 }