مفاهیم و اصول OOP
روش معمول انجام اینکار، به روز رسانی دستی تمام فایلهای AssemblyInfo.cs یک Solution است و همچنین اطمینان حاصل کردن از همگام بودن آنها. در ادامه قصد داریم با استفاده از فایلهای T4، یک فایل SharedAssemblyInfo.tt را جهت تولید اطلاعات مشترک Build بین اسمبلیهای مختلف یک پروژه، تولید کنیم.
ایجاد پروژهی SharedMetaData
برای نگهداری فایل مشترک SharedAssemblyInfo.cs نهایی و همچنین اطمینان از تولید مجدد آن به ازای هر Build، یک پروژهی class library جدید را به نام SharedMetaData به Solution جاری اضافه کنید.
سپس نیاز است یک فایل text template جدید را به نام SharedAssemblyInfo.tt، به این پروژه اضافه کنید.
به خواص فایل SharedAssemblyInfo.tt مراجعه کرده و Transform on build آنرا true کنید. به این ترتیب مطمئن خواهیم شد این فایل به ازای هر build جدید، مجددا تولید میگردد.
اکنون محتوای این فایل را به نحو ذیل تغییر دهید:
<#@ template debug="false" hostspecific="false" language="C#" #> // // This code was generated by a tool. Any changes made manually will be lost // the next time this code is regenerated. // using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyCompany("some name")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguageAttribute("en")] [assembly: AssemblyProduct("product name")] [assembly: AssemblyCopyright("Copyright VahidN 2014")] [assembly: AssemblyTrademark("some name")] #if DEBUG [assembly: AssemblyConfiguration("Debug")] #else [assembly: AssemblyConfiguration("Release")] #endif // Assembly Versions are incremented manually when branching the code for a release. [assembly: AssemblyVersion("<#= this.MajorVersion #>.<#= this.MinorVersion #>.<#= this.BuildNumber #>.<#= this.RevisionNumber #>")] // Assembly File Version should be incremented automatically as part of the build process. [assembly: AssemblyFileVersion("<#= this.MajorVersion #>.<#= this.MinorVersion #>.<#= this.BuildNumber #>.<#= this.RevisionNumber #>")] <#+ // Manually incremented for major releases, such as adding many new features to the solution or introducing breaking changes. int MajorVersion = 1; // Manually incremented for minor releases, such as introducing small changes to existing features or adding new features. int MinorVersion = 0; // Typically incremented automatically as part of every build performed on the Build Server. int BuildNumber = (int)(DateTime.UtcNow - new DateTime(2013,1,1)).TotalDays; // Incremented for QFEs (a.k.a. “hotfixes” or patches) to builds released into the Production environment. // This is set to zero for the initial release of any major/minor version of the solution. int RevisionNumber = 0; #>
MajorVersion با افزودن تعداد زیادی قابلیت به برنامه، به صورت دستی تغییر میکند. همچنین اگر یک breaking change در برنامه یا کتابخانه وجود داشته باشد نیز این شماره باید تغییر نماید.
MinorVersion با افزودن ویژگیهای کوچکی به نگارش فعلی برنامه تغییر میکند.
BuildNumber به صورت خودکار بر اساس هر Build انجام شده باید تغییر یابد. در اینجا این عدد به صورت خودکار به ازای هر روز، یک واحد افزایش پیدا میکند. ابتدای مبداء آن در این مثال، 2013 قرار گرفتهاست.
RevisionNumber با ارائه یک وصله جدید برای نگارش فعلی برنامه، به صورت دستی باید تغییر کند. اگر اعداد شماره نگارش major یا minor تغییر کنند، این عدد باید به صفر تنظیم شود.
اکنون اگر این محتوای جدید را ذخیره کنید، فایل SharedAssemblyInfo.cs به صورت خودکار تولید خواهد شد.
افزودن فایل SharedAssemblyInfo.cs به صورت لینک به تمام پروژهها
نحوهی افزودن فایل جدید SharedAssemblyInfo.cs به پروژههای موجود، اندکی متفاوت است با روش معمول افزودن فایلهای cs هر پروژه. ابتدا از منوی پروژه گزینهی add existing item را انتخاب کنید. سپس فایل SharedAssemblyInfo.cs را یافته و به صورت add as link، به تمام پروژههای موجود اضافه کنید.
اینکار باید در مورد تمام پروژهها صورت گیرد. به این ترتیب چون فایل SharedAssemblyInfo.cs به این پروژهها صرفا لینک شدهاست، اگر محتوای آن در پروژهی metadata تغییر کند، به صورت خودکار و یک دست، در تمام پروژههای دیگر نیز منعکس خواهد شد.
در ادامه اگر بخواهید Solution را Build کنید، پیام تکراری بودن یک سری از ویژگیها را یافت خواهید کرد. این مورد از این جهت رخ میدهد که هنوز فایلهای AssemblyInfo.cs اصلی، در پروژههای برنامه موجود هستند.
این فایلها را یافته و صرفا چند سطر همیشه ثابت ذیل را در آنها باقی بگذارید:
using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("title")] [assembly: AssemblyDescription("")] [assembly: ComVisible(false)] [assembly: Guid("9cde6054-dd73-42d5-a859-7d4b6dc9b596")]
اضافه کردن build dependency به پروژه MetaData
در پایان کار نیاز است اطمینان حاصل کنیم، فایل SharedAssemblyInfo.cs به صورت خودکار پیش از Build هر پروژه، تولید میشود. برای این منظور، از منوی Project، گزینهی Project dependencies را انتخاب کنید. سپس در برگهی dependencies آن، به ازای تمام پروژههای موجود، گزینهی SharedMetadata را انتخاب نمائید.
این مساله سبب اجرای خودکار فایل SharedAssemblyInfo.tt پیش از هر Build میشود و به این ترتیب میتوان از تازه بودن اطلاعات SharedAssemblyInfo.cs اطمینان حاصل کرد.
اکنون اگر پروژه را Build کنید، تمام اجزای آن شماره نگارش یکسانی را خواهند داشت:
و در دفعات آتی، تنها نیاز است تک فایل SharedAssemblyInfo.tt را برای تغییر شماره نگارشهای اصلی، ویرایش کرد.
کلمه کلیدی volatile
پس از انتشار جزوهی SVN در حدود دو سال قبل، ایمیل در این مورد زیاد داشتم. یکی از سؤالات هم این بود که: "چگونه از SVN جهت مدیریت نگارشهای مختلف یک بانک اطلاعاتی اس کیوال سرور در یک تیم استفاده کنیم؟ (منظور مدیریت schema است)" و من هم پاسخ مناسبی برای این مورد نداشتم چون کلاینتهای SVN حداقل با Management studio یکپارچه نمیشود (بر خلاف ابزارهای موجود برای VS.NET مانند VisualSVN ، AnkhSVN و غیره). صد البته میشود از آن همانند اعمال نگارش به یک فایل Text معمولی مانند فایلهای SQL استفاده کرد، اما خوب ...
و خبر خوب اینکه شرکت معظم RedGate چند روز قبل یک کتاب رایگان را در این مورد منتشر کرده است:
سرفصلهای این کتاب
Chapter 1: Writing Readable SQL
Chapter 2: Documenting your Database
Chapter 3: Change Management and Source Control
Chapter 4: Managing Deployments
Chapter 5: Testing Databases
Chapter 6: Reusing T-SQL Code
Chapter 7: Maintaining a Code Library
Chapter 8: Exploring your Database Schema
Chapter 9: Searching DDL and Build Scripts
Chapter 10: Automating CRUD
Chapter 11: SQL Refactoring
دریافت
Dynamic Semantics
Objectها علاوه بر داده و رفتار به عنوان توصیفات ثابت، در زمان اجرا دارای یک Local State (a snapshot) از مقادیر داینامیک مربوط به اعضای دادهای خود، میباشند. مجموعه تمام حالاتی که وهلههای یک کلاس میتوانند بین آنها گذر (transition) داشته باشد، dynamic semantics مربوط به کلاس نامیده میشود و به وهلههای کلاس این امکان را میدهند تا به یک پیغام مشابه رسیده و در زمانهای مختلف از چرخه زندگی خود، به اشکال مختلف پاسخ دهند.
Method junk for the class X if (local state #1) then do something else if (local state #2) then do something different End Method
بخش اصلی هر طراحی شیء گرا، dynamic semantics وهلهها میباشد. dynamic semantics هر کلاسی باید در قالب یک دیاگرام state-transition مستند شود. شکل زیر dynamic semantics پروسههای موجود در یک سیستم عامل را در قابل یک دیاگرام حالت نمایش میدهد. این پروسهها توانایی این را دارند که در هر کدام از حالات: runnable، current process، blocked، sleeping و یا در حالت exited، قرار داشته باشند. همچنین به عنوان مثال، یک پروسه زمانی میتواند در حالت current process قرار گیرد که حتما قبلا در حالت runnable قرار داشته باشد. این اطلاعات برای ایجاد تست برای کلاسها و وهلههای آنها میتواند مفید واقع شود.
شکل 2.8 State-transition diagram notation
برخی از طراحان به طور تصادفی، dynamic semantics یک کلاس را به عنوان static semantics آن کلاس مدل میکنند. به عنوان مثال اگر color یکی از اعضای داده ای (data member) کلاس توپ باشد و بعد از وهله سازی از کلاس توپ، color آن بازهم قابل تغییر باشد، منظور اینکه توپ آبی به عنوان یک وهله از کلاس توپ در زمان حیات خود تغییر رنگ دهد، اصطلاحا میگویند: color جزء dynamic semantics کلاس توپ میباشد. با توجه به توضحیاتی که داده شد، حال اگر طراحی برای هر رنگ توپ یک کلاس جدا در نظر گرفته باشد، dynamic semantics را به عنوان static semantics مدل کرده و به احتمال زیاد ما را به سمت ایجاد مشکل Class Proliferation (ازدیاد کلاس ها) سوق خواهد داد.
Abstract Classes
به سوالات زیر توجه کنید:
- آیا هرگز میوه خوردهاید؟
- آیا هرگز پیش غذا خوردهاید؟
- آیا هرگز دسر خوردهاید؟
حال با توجه به سوالات «مزه غذا چطور بود؟ دسری که خوردید، چه تعداد کالری داشت؟ هزینه پیش غذایی که خوردید چقدر بود» پاسخ چه خواهد بود؟
من (نویسنده) ادعا میکنم که هیچ کسی تا به حال میوه نخورده است. بیشتر مردم، سیب، موز و پرتقال خوردهاند؛ میوهی قرمز رنگی به ارزش 3 پوند را نخوردهاند.
شبیه به این مسئله برای زمانی است که گارسون رستوران از شما سوال میکند: «برای شام چه چیزی میل دارید» و شما جواب میدهید: «یک پیش غذا، یک غذای اصلی و یک دسر». در این حالت چون شما دقیقا مشخص نکردهاید چه نوعی میخواهید، گارسون، مات و مبهوت خواهد ماند. همه میدانیم که چیزی تحت عنوان میوه، پیش غذا و یا وهله دسر در واقعیت وجود ندارد؛ بله این عبارات اطلاعات مفیدی را تسخیر میکنند. اگر من در دستم یک ساعت زنگی گرفته و از شما میپرسیدم: «نظرتان در مورد میوه من چیست؟»؛ بدون شک فکر میکردید من دیوانه شدهام. حال اگر در دستم سیبی گرفته و سوال قبلی را میپرسیدم، این بار از نظر شما من یک شخص عاقل بودم.
با وجود اینکه نمیتوان از میوه وهله سازی کرد، اما اطلاعات مفیدی را تسخیر میکند. در واقع میوه، یک کلاسی (concept) است که دانشی از نحوه وهله سازی وهله هایش به وسیله Type پیاده ساز خود، ندارد.
کلاسی که دانشی از نحوه وهله سازی وهلههای خود ندارد، abstract class (کلاس مجرد یا انتزاعی) نامیده میشود.
کلاسی که دانش نحوه وهله سازی وهلههای خود دارد، concrete class نامیده میشود.
در پارادایم شیء گرا، مهمترین استفاده از کلاسهای انتزاعی در مباحث ارث بری مطرح میشود.
Roles Versus Classes
مطمئن باشید انتزاع هایی را که مدل میکنید کلاس بوده و نه نقشهایی که وهلههای آنها بازی میکنند. (Be sure the abstractions that you model are classes and not simply the roles objects play)آیا مادر و پدر به عنوان یک کلاس هستند یا نقشهایی هستند که وهلههای کلاس شخص، بازی میکند؟ پاسخ این سوال وابسته به دامینی (domain) است که طراح در حال مدل سازی آن میباشد. اگر در دامین مورد نظر، مادر و پدر رفتارهای مختلفی دارند، احتمالا باید به عنوان کلاسهای جدا مدل شوند. اگر رفتارهای یکسانی دارند، در نتیجه نقشهای مختلفی هستند که وهلههای کلاس شخص بازی میکنند. به عنوان مثال، میتوان کلاس خانواده را متشکل از وهلهای از کلاس پدر، وهلهای از کلاس مادر و مجموعهای از وهلههای کلاس فرزند در نظر گرفت. در مقابل ممکن است کلاس خانواده را متشکل از وهلهای از کلاس شخص به عنوان پدر، وهلهای از کلاس شخص به عنوان مادر و آرایهای از وهلههای شخص به عنوان فرزندان، مدل کنید. قرار گرفتن در وضیعتی که هر نقش، بخشی از رفتاریهای شخص را مورد استفاده قرار میدهد، کافی نیست و باید مطمئن شوید که رفتارها واقعا متفاوت میباشند. همچنین باید به یاد داشته باشید که زمانیکه وهلهای از بخشی از رفتارهای کلاس خود استفاده میکند، نیز مشکلی وجود ندارد و لازم نیست کلاسهای دیگری را به خاطر این موضوع در طراحی خود در نظر بگیرید.
شکل 2.9 Two views of a family
برخی از طراحان به این شکل تست میکنند که اگر عضوی از واسط عمومی را نمیتوان برای نقش مورد نظر مورد استفاده قرار داد، این موضوع نشان از این دارد که باید برای نقش مورد نظر در طراحی خود کلاس جداگانهای را در نظر داشته باشند. اگر هم عضو مذکور قابل استفاده نباشد، کلاس یکسانی برای نقشهای مختلف استفاده خواهد شد. به عنوان مثال، اگر عملیات ()go_into_labor جزء عملیاتی میباشد که مادر انجام میدهد، در حالیکه پدر چنین عملیاتی را نمیتواند انجام دهد، در این حالت نیز لازم است مادر به عنوان کلاس جداگانهای در نظر گرفته شود. اگر در دامین دیگری، عوض کردن پوشاک را تنها مادر انجام میدهد، در این حالت مادر نقشی از کلاس شخص میباشد، چرا که پدر هم توانایی انجام این عملیات را دارد.
قواعد شهودی فصل دوم
همه دادهها باید در داخل کلاس خود پنهان شده باشند. (All data should be hidden within its class)
استفاده کنندگان از کلاس باید به واسط عمومی آن وابسته باشند، اما یک کلاس نباید به استفاده کنندگان خود، وابسته باشد. (Users of a class must be dependent on its public interface, but a class should not be dependent on its users)
تعداد پیغامهای موجود در قرارداد یک کلاس را کمینه سازید. (Minimize the number of messages in the protocol of a class)
پیاده سازی یک واسط عمومی یکسان کمینه برای همه کلاسها (Implement a minimal public interface that all classes understand [e.g., operations such as copy (deep versus shallow), equality testing, pretty printing, parsing from an ASCII description, etc.].)
جزئیات پیاده سازی، مانند توابع خصوصی common-code ( توابعی که کد مشترک سایر متدهای کلاس را در بدنه خود دارند) را در واسط عمومی یک کلاس قرار ندهید. (Do not put implementation details such as common-code private functions into the public interface of a class)
واسط عمومی کلاس را با اقلامی که یا استفاده کنندگان از کلاس توانایی استفاده از آن را نداشته و یا تمایلی به استفاده از آنها ندارند، آمیخته نکنید. (Do not clutter the public interface of a class with items that users of that class are not able to use or are not interested in using )
اتصال و پیوستگی مابین کلاسها باید از نوع Nil یا Export باشد؛ به این معنی که یک کلاس فقط از واسط عمومی کلاس دیگر استفاده کند یا کاری با آن نداشته باشد. (Classes should only exhibit nil or export coupling with other classes, that is, a class should only use operations in the public interface of another class or have nothing to do with that class.)
یک کلاس باید یک و تنها یک Key Abstraction را تسخیر نماید. (A class should capture one and only one key abstraction)
داده و رفتار مرتبط را در یک جا (کلاس) نگه دارید. (Keep related data and behavior in one place)
اطلاعات نامرتبط به هم را در کلاسهای جدا از هم قرار دهید. ((Spin off nonrelated information into another class (i.e., noncommunicating behavior)
مطمئن باشید انتزاع هایی را که مدل میکنید کلاس بوده و نه نقشهایی که وهلههای آنها بازی میکنند. (Be sure the abstractions that you model are classes and not simply the roles objects play)
A GitHub App that keeps your forks up-to-date with upstream via automated pull requests.
اگه بخواین یه مخزنی که توی گیتهاب Fork کردین رو با سورس اصلی (upstream) بروز رسانی کنین کمی دردسر داره و این کار باید هر بار به صورت دستی انجام بشه
ولی این Github App رایگان اینکار رو بدون هیچ دردسری و به صورت خودکار و مداوم انجام میده براتون.
فعالسازی تولید خودکار بستههای نیوگت در پروژههای NET Core.
پس از تهیهی یک کتابخانهی مبتنی بر NET Core.، تنها کاری که در جهت تولید خودکار بستههای نیوگت باید انجام شود، افزودن مدخل postcompile ذیل به فایل project.json است:
"scripts": { "postcompile": [ "dotnet pack --no-build --configuration %compile:Configuration%" ] }
در اینحالت اگر فایل nupkg تولیدی را توسط برنامههای zip باز کنید، مشاهده خواهید کرد که فایل nuspec خودکاری نیز در آن درج شدهاست؛ اما ... مشخصات ثبت شدهی در آن ناقص هستند و شامل مواردی مانند نام پروژه، نام نویسنده، مجوز استفادهی از پروژه، آدرس پروژه و امثال آنها نیستند. در نگارشهای دیگر دات نت، این مشخصات از فایل nuspec تهیه شدهی توسط ما جمع آوری و درج میشود. اما در اینجا خیر.
تکمیل فایل project.json برای درج مشخصات پروژه و تکمیل اطلاعات فایل nuspec
هرچند به ظاهر دیگر فایل nuspec دستی تهیه شده در اینجا پردازش نمیشود، اما تمام اطلاعات آنرا در فایل project.json نیز میتوان درج کرد:
{ "version": "1.1.1.0", "authors": [ "Vahid Nasiri" ], "packOptions": { "owners": [ "Vahid Nasiri" ], "tags": [ "PdfReport", "Excel", "Export", "iTextSharp", "PDF", "Report", "Reporting", "Persian", ".NET Core" ], "licenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html", "projectUrl": "https://github.com/VahidN/iTextSharp.LGPLv2.Core" }, "description": " iTextSharp.LGPLv2.Core is an unofficial port of the last LGPL version of the iTextSharp (V4.1.6) to .NET Core.", "scripts": { "postcompile": [ "dotnet pack --no-build --configuration %compile:Configuration%" ] } }
تکمیل تنظیمات Build پروژه
بهتر است کتابخانههای خود را در حالت release و همچنین بهینه سازی شده، توزیع کنید. به همین منظور نیاز است مدخل ذیل را نیز به فایل project.json اضافه کرد:
"configurations": { "Release": { "buildOptions": { "optimize": true, "platform": "anycpu" } } },
افزودن مستندات XML ایی کتابخانه
به احتمال زیاد XML-Docهای هر متد (کامنتهای مخصوص دات نتی هر متد یا خاصیت عمومی) را نیز به کدهای خود افزودهاید. برای اینکه فایل XML نهایی آن به صورت خودکار تولید شده و همچنین در بستهی نیوگت نهایی درج شود، نیاز است مدخل xmlDoc را به buildOptions اضافه کنید:
"buildOptions": { "xmlDoc": true },
"buildOptions": { "xmlDoc": true, "nowarn": [ "1591" ] // 1591: missing xml comment for publicly visible type or member },
برای مطالعهی بیشتر
project.json reference
بررسی حالتهای مختلف نصب RavenDB
الف) استفاده از RavenDB در حالت مدفون شده یا Embedded
حالت Embedded به این معنا است که RavenDB درون پروسه برنامه شما اجرا خواهد شد و نه به صورت پروسهای مجزا. این حالت برای ارائه ساده برنامههای دسکتاپ بسیار مناسب است؛ یا حتی توزیع برنامههای سبک ASP.NET بدون نیاز به نصب بانک اطلاعاتی خاصی بر روی وب سرور.
برای کار با RavenDB در حالت Embedded ابتدا فایلهای مورد نیاز آنرا از طریق نیوگت دریافت کنید:
PM> Install-Package RavenDB.Embedded -Pre
var documentStore = new EmbeddableDocumentStore { DataDirectory = @"~/app_data/ravendb" }; documentStore.Initialize();
امکان تعریف DataDirectory در فایل کانفیگ برنامه نیز وجود دارد. فقط در این حالت باید دقت داشت که نام مسیر، با DataDir شروع میشود و نه DataDirectory :
<connectionStrings> <add name="Local" connectionString="DataDir = ~\Data"/>
چند نکته جالب در مورد حالت Embedded
- امکان اجرای درون حافظهای RavenDB نیز وجود دارد:
var documentStore = new EmbeddableDocumentStore{RunInMemory = true}.Initialize()
- اجرای حالت Embedded به صورت Embedded HTTP:
در حالت Embedded دیگر دسترسی به برنامه سیلورلایت Raven studio وجود ندارد. اگر علاقمند به کار با آن بودید، خاصیت UseEmbeddedHttpServer شیء EmbeddableDocumentStore را به true تنظیم کنید. سپس فایل Raven.Studio.xap را در ریشه وب سایت خود قرار دهید. اکنون مانند قبل آدرس localhost:8080/raven/studio.html برقرار خواهد بود.
همچنین سرور Http این بانک اطلاعاتی را نیز میتوان دستی راه اندازی کرد. متد NonAdminHttp.EnsureCanListenToWhenInNonAdminContext بررسی میکند که آیا برنامه مجوز راه اندازی یک سرور را بر روی پورت مثلا 8080 دارد یا خیر.
NonAdminHttp.EnsureCanListenToWhenInNonAdminContext(8080); // Start the HTTP server manually var server = new RavenDbHttpServer(documentStore.Configuration, documentStore.DocumentDatabase); server.Start();
ب) نصب RavenDB به صورت سرویس ویندوز NT
اگر مایل باشیم تا RavenDB را نیز مانند SQL Server به صورت یک سرویس ویندوز NT نصب کنیم تا همواره در پس زمینه سرور در حال اجرا باشد، کنسول پاورشل ویندوز را گشوده و سپس فرمان ذیل را صادر کنید:
d:\ravendb\server> .\raven.server.exe /install
و اگر خواستید این سرویس را عزل کنید، دستور ذیل را در پاورشل ویندوز صادر کنید:
d:\ravendb\server> .\raven.server.exe /uninstall
ج) نصب RavenDB به صورت یک پروسه IIS (یا اجرا شده توسط IIS)
فایلهای مورد نیاز حالت اجرای RavenDB را به صورت یک پروسه مجزای IIS از نیوگت دریافت کنید:
PM> Install-Package RavenDB.AspNetHost -Pre
یک نکته
تمام بستههای مورد نیاز را یکجا از آدرس http://ravendb.net/download نیز میتوان دریافت کرد. در نگارشهای جدید، بسته نصاب نیز برای این بانک اطلاعاتی تهیه شده است که برای نمونه توزیع آنرا جهت حالت نصب در IIS سادهتر میکند.