update products set Name = "Test" Where Id = 1
update products with (nolock,updlock) set Name = "Test" where Id = 1
public class UpdateRowLockHintDbCommandInterceptor : IDbCommandInterceptor { public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<Int32> interceptionContext) { if (command.CommandType != CommandType.Text) return; // (1) if (!(command is SqlCommand)) return; // (2) SqlCommand sqlCommand = (SqlCommand)command; String commandText = sqlCommand.CommandText; String updateCommandRegularExpression = "(update) "; Boolean isUpdateCommand = Regex.IsMatch(commandText, updateCommandRegularExpression, RegexOptions.IgnoreCase | RegexOptions.Multiline); // You may use better regular expression pattern here. if (isUpdateCommand) { Boolean isSnapshotIsolationTransaction = sqlCommand.Transaction != null && sqlCommand.Transaction.IsolationLevel == IsolationLevel.Snapshot; String tableHintToAdd = isSnapshotIsolationTransaction ? " with (rowlock , updlock) set " : " with (rowlock) set "; commandText = Regex.Replace(commandText, "^(set) ", (match) => { return tableHintToAdd; }, RegexOptions.IgnoreCase | RegexOptions.Multiline); command.CommandText = commandText; } }
مرتب سازی پراکسیها در سرویسهای WCF
فرض کنید یک پراکسی دینامیک (dynamic proxy) از یک کوئری دریافت کرده اید. حال میخواهید این پراکسی را در قالب یک آبجکت CLR سریال کنید. هنگامی که آبجکتهای موجودیت را بصورت POCO-based پیاده سازی میکنید، EF بصورت خودکار یک آبجکت دینامیک مشتق شده را در زمان اجرا تولید میکند که dynamix proxy نام دارد. این آبجکت برای هر موجودیت POCO تولید میشود. این آبجکت پراکسی بسیاری از خواص مجازی (virtual) را بازنویسی میکند تا عملیاتی مانند ردیابی تغییرات و بارگذاری تنبل را انجام دهد.
فرض کنید مدلی مانند تصویر زیر دارید.
ما از کلاس ProxyDataContractResolver برای deserialize کردن یک آبجکت پراکسی در سمت سرور و تبدیل آن به یک آبجکت POCO روی کلاینت WCF استفاده میکنیم. مراحل زیر را دنبال کنید.
- در ویژوال استودیو پروژه جدیدی از نوع WCF Service Application بسازید. یک Entity Data Model به پروژه اضافه کنید و جدول Clients را مطابق مدل مذکور ایجاد کنید.
- کلاس POCO تولید شده توسط EF را باز کنید و کلمه کلیدی virtual را به تمام خواص اضافه کنید. این کار باعث میشود EF کلاسهای پراکسی دینامیک تولید کند. کد کامل این کلاس در لیست زیر قابل مشاهده است.
public class Client { public virtual int ClientId { get; set; } public virtual string Name { get; set; } public virtual string Email { get; set; } }
نکته: بیاد داشته باشید که هرگاه مدل EDMX را تغییر میدهید، EF بصورت خودکار کلاسهای موجودیتها را مجددا ساخته و تغییرات مرحله قبلی را بازنویسی میکند. بنابراین یا باید این مراحل را هر بار تکرار کنید، یا قالب T4 مربوطه را ویرایش کنید. اگر هم از مدل Code-First استفاده میکنید که نیازی به این کارها نخواهد بود.
- ما نیاز داریم که DataContractSerializer از یک کلاس ProxyDataContractResolver استفاده کند تا پراکسی کلاینت را به موجودیت کلاینت برای کلاینت سرویس WCF تبدیل کند. یعنی client proxy به client entity تبدیل شود، که نهایتا در اپلیکیشن کلاینت سرویس استفاده خواهد شد. بدین منظور یک ویژگی operation behavior میسازیم و آن را به متد ()GetClient در سرویس الحاق میکنیم. برای ساختن ویژگی جدید از کدی که در لیست زیر آمده استفاده کنید. بیاد داشته باشید که کلاس ProxyDataContractResolver در فضای نام Entity Framework قرار دارد.
using System.ServiceModel.Description; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using System.Data.Objects; namespace Recipe8 { public class ApplyProxyDataContractResolverAttribute : Attribute, IOperationBehavior { public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) { } public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy) { DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors .Find<DataContractSerializerOperationBehavior>(); dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver(); } public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) { DataContractSerializerOperationBehavior dataContractSerializerOperationBehavior = description.Behaviors .Find<DataContractSerializerOperationBehavior>(); dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver(); } public void Validate(OperationDescription description) { } } }
- فایل IService1.cs را باز کنید و کد آن را مطابق لیست زیر تکمیل نمایید.
[ServiceContract] public interface IService1 { [OperationContract] void InsertTestRecord(); [OperationContract] Client GetClient(); [OperationContract] void Update(Client client); }
- فایل IService1.svc.cs را باز کنید و پیاده سازی سرویس را مطابق لیست زیر تکمیل کنید.
public class Client { [ApplyProxyDataContractResolver] public Client GetClient() { using (var context = new EFRecipesEntities()) { context.Cofiguration.LazyLoadingEnabled = false; return context.Clients.Single(); } } public void Update(Client client) { using (var context = new EFRecipesEntities()) { context.Entry(client).State = EntityState.Modified; context.SaveChanges(); } } public void InsertTestRecord() { using (var context = new EFRecipesEntities()) { // delete previous test data context.ExecuteSqlCommand("delete from [clients]"); // insert new test data context.ExecuteStoreCommand(@"insert into [clients](Name, Email) values ('Armin Zia','armin.zia@gmail.com')"); } } }
- حال پروژه جدیدی از نوع Console Application به راه حل جاری اضافه کنید. این اپلیکیشن، کلاینت تست ما خواهد بود. پروژه سرویس را ارجاع کنید و کد کلاینت را مطابق لیست زیر تکمیل نمایید.
using Recipe8Client.ServiceReference1; namespace Recipe8Client { class Program { static void Main(string[] args) { using (var serviceClient = new Service1Client()) { serviceClient.InsertTestRecord(); var client = serviceClient.GetClient(); Console.WriteLine("Client is: {0} at {1}", client.Name, client.Email); client.Name = "Armin Zia"; client.Email = "arminzia@live.com"; serviceClient.Update(client); client = serviceClient.GetClient(); Console.WriteLine("Client changed to: {0} at {1}", client.Name, client.Email); } } } }
Client changed to: Armin Zia at arminzia@live.com
شرح مثال جاری
مایکروسافت استفاده از آبجکتهای POCO با WCF را توصیه میکند چرا که مرتب سازی (serialization) آبجکت موجودیت را سادهتر میکند. اما در صورتی که از آبجکتهای POCO ای استفاده میکنید که changed-based notification دارند (یعنی خواص موجودیت را با virtual علامت گذاری کرده اید و کلکسیونهای خواص پیمایشی از نوع ICollection هستند)، آنگاه EF برای موجودیتهای بازگشتی کوئریها پراکسیهای دینامیک تولید خواهد کرد.
استفاده از پراکسیهای دینامیک با WCF دو مشکل دارد. مشکل اول مربوط به سریال کردن پراکسی است. کلاس DataContractSerializer تنها قادر به serialize/deserialize کردن انواع شناخته شده (known types) است. در مثال جاری این یعنی موجودیت Client. اما از آنجا که EF برای موجودیتها پراکسی میسازد، حالا باید آبجکت پراکسی را سریال کنیم، نه خود کلاس Client را. اینجا است که از DataContractResolver استفاده میکنیم. این کلاس میتواند حین سریال کردن آبجکت ها، نوعی را به نوع دیگر تبدیل کند. برای استفاده از این کلاس ما یک ویژگی سفارشی ساختیم، که پراکسیها را به کلاسهای POCO تبدیل میکند. سپس این ویژگی را به متد ()GetClient اضافه کردیم. این کار باعث میشود که پراکسی دینامیکی که توسط متد ()GetClient برای موجودیت Client تولید میشود، به درستی سریال شود.
مشکل دوم استفاده از پراکسیها با WCF مربوط به بارگذاری تبل یا Lazy Loading میشود. هنگامی که DataContractSerializer موجودیتها را سریال میکند، تمام خواص موجودیت را دستیابی خواهد کرد که منجر به اجرای lazy-loading روی خواص پیمایشی میشود. مسلما این رفتار را نمیخواهیم. برای رفع این مشکل، مشخصا قابلیت بارگذاری تنبل را خاموش یا غیرفعال کرده ایم.
EF Code First #1
- برای حل مشکل Login failed for user ALIPC\ali ،دقیقا باید به «همین کاربر» در تنظیمات امنیتی SQL Server، دسترسیهای لازم را بدهید:
management studio -> select server -> expand Security -> right click Logins -> select "New Login..."
Right click on db-> properties -> permission -> View Server permission
نیاز به درایور 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)
public class AccountType : ObjectGraphType<Account> { public AccountType() { Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the account object."); Field(x => x.Description).Description("Description property from the account object."); Field(x => x.OwnerId, type: typeof(IdGraphType)).Description("OwnerId property from the account object."); } }
public interface IAccountRepository { IEnumerable<Account> GetAllAccountsPerOwner(Guid ownerId); } public class AccountRepository : IAccountRepository { private readonly ApplicationContext _context; public AccountRepository(ApplicationContext context) { _context = context; } public IEnumerable<Account> GetAllAccountsPerOwner(Guid ownerId) => _context.Accounts .Where(a => a.OwnerId.Equals(ownerId)) .ToList(); }
public class OwnerType : ObjectGraphType<Owner> { public OwnerType(IAccountRepository repository) { Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the owner object."); Field(x => x.Name).Description("Name property from the owner object."); Field(x => x.Address).Description("Address property from the owner object."); Field<ListGraphType<AccountType>>( "accounts", resolve: context => repository.GetAllAccountsPerOwner(context.Source.Id) ); } }
https://localhost:5001/ui/playground
{ owners{ id, name, address, accounts{ id, description, ownerId } } }
public class AccountTypeEnumType : EnumerationGraphType<TypeOfAccount> { public AccountTypeEnumType() { Name = "Type"; Description = "Enumeration for the account type object."; } }
public class AccountType : ObjectGraphType<Account> { public AccountType() { ... Field<AccountTypeEnumType>("Type", "Enumeration for the account type object."); } }
{ owners{ id, name, address, accounts{ id, description, type, ownerId } } }
public interface IAccountRepository { ... Task<ILookup<Guid, Account>> GetAccountsByOwnerIds(IEnumerable<Guid> ownerIds); }
public class AccountRepository : IAccountRepository { ... public async Task<ILookup<Guid, Account>> GetAccountsByOwnerIds(IEnumerable<Guid> ownerIds) { var accounts = await _context.Accounts.Where(a => ownerIds.Contains(a.OwnerId)).ToListAsync(); return accounts.ToLookup(x => x.OwnerId); } }
public class OwnerType : ObjectGraphType<Owner> { public OwnerType(IAccountRepository repository, IDataLoaderContextAccessor dataLoader) { ... Field<ListGraphType<AccountType>>( "accounts", resolve: context => { var loader = dataLoader.Context.GetOrAddCollectionBatchLoader<Guid, Account>("GetAccountsByOwnerIds", repository.GetAccountsByOwnerIds); return loader.LoadAsync(context.Source.Id); }); } }
services.AddGraphQL(o => { o.ExposeExceptions = false; }) .AddGraphTypes(ServiceLifetime.Scoped) .AddDataLoader();
public interface IOwnerRepository { ... Owner GetById(Guid id); } public class OwnerRepository : IOwnerRepository { ... Owner GetById(Guid id) => _context.Owners.SingleOrDefault(o => o.Id.Equals(id)); }
public class AppQuery : ObjectGraphType { public AppQuery(IOwnerRepository repository) { ... Field<OwnerType>( "owner", arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }), resolve: context => { var id = context.GetArgument<Guid>("ownerId"); return repository.GetById(id); } ); } }
Field<OwnerType>( "owner", arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "ownerId" }), resolve: context => { Guid id; if (!Guid.TryParse(context.GetArgument<string>("ownerId"), out id)) { context.Errors.Add(new ExecutionError("Wrong value for guid")); return null; } return repository.GetById(id); } );
{ owner(ownerId:"6f513773-be46-4001-8adc-2e7f17d52d83"){ id, name, address, accounts{ id, description, type, ownerId } }
string name = context.GetArgument<string>("name");
{ owner(ownerId:"53270061-3ba1-4aa6-b937-1f6bc57d04d2", name:"ANDY") { ... } }
{ first:owners{ ownerId:id, ownerName:name, ownerAddress:address, ownerAccounts:accounts { accountId:id, accountDescription:description, accountType:type } }, second:owners{ ownerId:id, ownerName:name, ownerAddress:address, ownerAccounts:accounts { accountId:id, accountDescription:description, accountType:type } } }
fragment SampleName on Type{ ... }
{ first:owners{ ...ownerFields }, second:owners{ ...ownerFields }, ... } fragment ownerFields on OwnerType{ ownerId:id, ownerName:name, ownerAddress:address, ownerAccounts:accounts { accountId:id, accountDescription:description, accountType:type } }
query OwnerQuery($ownerId:ID!) { owner(ownerId:$ownerId){ id, name, address, accounts{ id, description, type } } }
{ "ownerId":"6f513773-be46-4001-8adc-2e7f17d52d83" }
*در دنیای NoSql پیاده سازیهای متفاوتی از دیتابیسها انجام شده است و هر دیتابیس، ویژگیها و مزایا و معایب خاص خودش را دارد. باید قبول کرد که همیشه و همه جا نمیتوان از یک پایگاه داده NoSql مشخص استفاده نماییم (دلایلی نظیر محدودیتهای License، هزینه پیاده سازی و...). در نتیجه بررسی یک دیتابیس دیگر با شرایط و توانمندیهای خاص آن خالی از سود نیست.
» این دیتاییس در گروه Graph databasesها قرار دارد و از زبان SPARQL (بخوانید Sparkle) برای کوئری گرفتن و کار با دادهها بهره میبرد؛
» متن باز و رایگان است
» پشتیبانی از دات نت چهار به بعد؛
» قابل استفاده در Mobile Application - Windows phone 7 , 8؛
» بدون شما (Schema Less) و با قابیلت ذخیره سازی triple و به فرمت RDF
» پشتیبانی از Linq و OData؛
» پشتیبانی از تراکنشها ؛
» پیاده سازی مدل برنامه به صورت Code First؛
» سرعت بالا جهت ذخیره سازی و لود اطلاعات؛
» نیاز به پیکربندیهای خاص جهت پیاده سازی ندارد؛
» ارائه افزونه رایگان Polaris جهت کوئری گفتن و نمایش Visual داده ها.
و غیره که در ادامه با آنها آشنا خواهید شد.
در B*Db دو روش برای ذخیره سازی اطلاعات وجود دارد:
» Append Only : در این روش تمامی تغییرات (عملیات نوشتن) در انتهای فایل index اضافه خواهد شد. این روش مزایای زیر را به دنبال خواهد داشت:
- عملیات نوشتن هیچگاه عملیات خواندن اطلاعات را block نمیکند. در
نتیجه هر تعداد عملیات خواندن فایل (منظور اجرای کوئریهای SPQRL است) میتواند به صورت موازی بر روی Storeها اجرا شود.
- به دلیل اینکه عمل ویرایش واقعی هیچ گاه انجام نمیشود (دادهها فقط اضافه خواهند شد) همیشه میتوانید وضعیت Store را به حالتهای قبلی بازگردانید.
- عملیات نوشتن اطلاعات بسیار سریع خواهد بود.
نکته ای که باید به آن دقت داشت این است که فقط در هنگام ساخت Storeها میتوانید نوع ذخیره سازی آن را تعیین نمایید، بعد از ساخت Store امکان سوئیچ بین حالات امکان پذیر نیست.
همان طور که پیشتر گفته شد B*Db برای ذخیره سازی اطلاعات از سند RDF بهره میبرد. البته با RDF Syntaxهای متفاوت :
هم چنین در B*Db چهار روش برای دست یابی با دادهها (پیاده سازی عملیات CRUD) وجود دارد از قبیل:
» B*Db EntityFramewok
» Data Object Layer
» RDF Client Api
» Dynamic API
که هر کدام در طی پستهای متفاوت بررسی خواهد شد.
بررسی یک مثال با روش B*Db EntityFramework
برای شروع ابتدا یک پروژه جدید از نوع Console Application ایجاد کنید. سپس از طریق Nuget اقدام به نصب Package زیر نمایید:
pm> install-Package BirghtStarDb
PM> Install-Package BirghtStarDbLibs
بعد از نصب پکیجهای بالا یک فایل Text Template با نام MyEntityContext.tt نیز به پروژه افزوده خواهد شد. این فایل جهت تولید خودکار مدلهای برنامه استفاده میشود. اما برای این کار لازم است به ازای هر مدل ابتدا یک اینترفیس ایجاد نمایید. برای مثال:
[Entity] public interface IBook { public int Code { get; set; } public string Title { get; set; } }
» نیاز به ایجاد یک خاصیت به عنوان Id وجود ندارد. به صورت پیش فرض خاصیت Id با نوع string برای هر مدل پیاده سازی میشود. اما اگر قصد دارید این نام خاصیت را تغییر دهید میتوانید به صورت زیر عمل کنید:
[Entity] public interface IBook { [Identifier] public string MyId { get; } public int Code { get; set; } public string Title { get; set; } }
استفاده از اینترفیس برای ساخت مدل انعطاف پذیری بالایی را در اختیار ما قرار میدهد که بعدا مفصل بحث خواهد شد. برای عملیات درج داده میتوان به صورت زیر عمل کنید:
MyEntityContext context = new MyEntityContext("type=embedded;storesdirectory=c:\brightstar;storename=test"); var book = context.Books.Create(); book.Code = 1; book.Title = "Test"; context.Books.Add(book); context.SaveChanges();
»embedded : در این حالت از طریق آدرس فیزیکی فایل مورد نظر میتوان یک Connection ایجاد کرد.
»rest : یا استفاده از HTTP یا HTTPS میتوان به سرویس B*Db دسترسی داشت.
»dotNetRdf : برای ارتباط با یک Store دیگر با استفاده از اتصال دهندههای DotNetRDf.
»sparql : اتصال به منبع داده ای دیگر با استفاده از پروتکل SPARQL
در هنگام ایجاد اتصال باید نوع مورد نظر را از حتما تعیین نمایید. با استفاده از storedirctory مکان فیزیکی فایل تعیین خواهد شد.
تبدیلگر تاریخ شمسی برای AutoMapper
public class User { public int Id { set; get; } public string Name { set; get; } public DateTime RegistrationDate { set; get; } }
public class UserViewModel { public int Id { set; get; } public string Name { set; get; } public string RegistrationDate { set; get; } }
تبدیلگر سفارشی تاریخ میلادی به شمسی مخصوص AutoMapper
در ذیل یک تبدیلگر سفارشی مخصوص AutoMapper را با پیاده سازی اینترفیس ITypeConverter آن ملاحظه میکنید:
public class DateTimeToPersianDateTimeConverter : ITypeConverter<DateTime, string> { private readonly string _separator; private readonly bool _includeHourMinute; public DateTimeToPersianDateTimeConverter(string separator = "/", bool includeHourMinute = true) { _separator = separator; _includeHourMinute = includeHourMinute; } public string Convert(ResolutionContext context) { var objDateTime = context.SourceValue; return objDateTime == null ? string.Empty : toShamsiDateTime((DateTime)context.SourceValue); } private string toShamsiDateTime(DateTime info) { var year = info.Year; var month = info.Month; var day = info.Day; var persianCalendar = new PersianCalendar(); var pYear = persianCalendar.GetYear(new DateTime(year, month, day, new GregorianCalendar())); var pMonth = persianCalendar.GetMonth(new DateTime(year, month, day, new GregorianCalendar())); var pDay = persianCalendar.GetDayOfMonth(new DateTime(year, month, day, new GregorianCalendar())); return _includeHourMinute ? string.Format("{0}{1}{2}{1}{3} {4}:{5}", pYear, _separator, pMonth.ToString("00", CultureInfo.InvariantCulture), pDay.ToString("00", CultureInfo.InvariantCulture), info.Hour.ToString("00"), info.Minute.ToString("00")) : string.Format("{0}{1}{2}{1}{3}", pYear, _separator, pMonth.ToString("00", CultureInfo.InvariantCulture), pDay.ToString("00", CultureInfo.InvariantCulture)); } }
ثبت و معرفی تبدیلگرهای سفارشی AutoMapper
پس از تعریف یک تبدیلگر سفارشی AutoMapper، اکنون نیاز است آنرا به AutoMapper معرفی کنیم:
public class TestProfile1 : Profile { protected override void Configure() { // این تنظیم سراسری هست و به تمام خواص زمانی اعمال میشود this.CreateMap<DateTime, string>().ConvertUsing(new DateTimeToPersianDateTimeConverter()); this.CreateMap<User, UserViewModel>(); } public override string ProfileName { get { return this.GetType().Name; } } }
همانطور که مشاهده میکنید در اینجا دو نگاشت تعریف شدهاند. یکی برای تبدیل User به UserViewModel و دیگری، معرفی نحوهی نگاشت DateTime به string، توسط تبدیلگر سفارشی DateTimeToPersianDateTimeConverter است که به کمک متد الحاقی ConvertUsing صورت گرفتهاست.
باید دقت داشت که تنظیمات تبدیلگرهای سفارشی سراسری هستند و در کل برنامه و به تمام پروفایلها اعمال میشوند.
بررسی خروجی تبدیلگر سفارشی تاریخ
اکنون کار استفاده از تنظیمات AutoMapper با ثبت پروفایل تعریف شده آغاز میشود:
Mapper.Initialize(cfg => // In Application_Start() { cfg.AddProfile<TestProfile1>(); });
var dbUser1 = new User { Id = 1, Name = "Test", RegistrationDate = DateTime.Now.AddDays(-10) }; var uiUser = new UserViewModel(); Mapper.Map(source: dbUser1, destination: uiUser);
نوشتن تبدیلگرهای غیر سراسری
همانطور که عنوان شد، معرفی تبدیلگرها به AutoMapper سراسری است و در کل برنامه اعمال میشود. اگر نیاز است فقط برای یک مدل خاص و یک خاصیت خاص آن تبدیلگر نوشته شود، باید نگاشت مورد نظر را به صورت ذیل تعریف کرد:
this.CreateMap<User, UserViewModel>() .ForMember(userViewModel => userViewModel.RegistrationDate, opt => opt.ResolveUsing(src => { var dt = src.RegistrationDate; return dt.ToShortDateString(); }));
خصوصی سازی تبدیلگرها با تدارک موتورهای نگاشت اختصاصی
اگر میخواهید تنظیمات TestProfile1 به کل برنامه اعمال نشود، نیاز است یک MappingEngine جدید و مجزای از MappingEngine سراسری AutoMapper را ایجاد کرد:
var configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers); configurationStore.AddProfile<TestProfile1>(); var mapper = new MappingEngine(configurationStore); mapper.Map(source: dbUser1, destination: uiUser);
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
AM_Sample02.zip