دریافت خروجی سایت
بنده فکر کنم همان 1 درصد مرورگر safari باشم که سایت شما را با ipad مرور میکنم
از طرفی بدلیل اینکه مدیریت فایل PDF در دستگاههای مانند ipad خیلی خسته کننده است به شخصه از CHM بیشتر استفاده میکنم و کاربر پسندتر است
در هر صورت چه فایل CHM و چه فایل PDF باشد خیلی از شما متشکریم
به گفته دوستان داشتن حتی فایل txt برخی مطالب این سایت بسیار ارزشمندتر است
services.AddMvc(options => options.EnableEndpointRouting = false) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
از طریق نوار ابزار قرمز رنگ پایین میتوان نام culture و زبان صفحات را تنظیم کرد به طوری که راست به چپ بودن یا چپ به راست بودن را هم میتوان در آن دید. همینطور استفاده از Resources به صورت دوحالت عمومی (Common.resx) یا شخصی سازی شده (Views.Home.About.fa.resx) بر حسب نام Viewها در آن رعایت شده است که بر حسب نیاز میتوان هرکدام از این دو مورد را استفاده یا توسعه داد.
امیدوارم برای دوستان هم مفید باشه
WebApplicationLocalization.rar
قسمت «دریافت فایل PDF» از نکتهی «HTML5 download attribute » استفاده میکند.
<a href="computer.png" download="PC.png"> Download File </a>
- اگر این ویژگی به نامی هم مزین شود، این نام، نام فایل دریافتی خواهد بود.
لینکهای هفته اول دی
وبلاگها و سایتهای ایرانی
امنیت
ASP. Net
طراحی وب
PHP
- خبرهایی در مورد مایکروسافت و PHP
اسکیوال سرور
سی شارپ
عمومی دات نت
مسایل اجتماعی و انسانی برنامه نویسی
کتابهای رایگان جدید
متفرقه
- آهن بجای کروم! (یک برنامه نویس آلمانی قسمتهایی از مرورگر کروم را که در جهت جمع آوری اطلاعات برای گوگل بکار میرفته، حذف کرده و مرورگر دیگری به نام آهن را ارائه داده است!)
هدف: استفاده از کتابخانهی jsSHA
میخواهیم در یک برنامهی AngularJS 2.0، از کتابخانهی jsSHA استفاده کرده و هش SHA512 یک رشته را محاسبه کنیم.
تامین پیشنیازهای اولیه
میتوان فایلهای این کتابخانه را مستقیما از GitHub دریافت و به پروژه اضافه کرد. اما بهتر است اینکار را توسط npm مدیریت کنیم. به همین جهت فایل package.json آنرا گشوده و سپس مدخل متناظری را به آن اضافه کنید:
"dependencies": { // ... "jssha": "^2.1.0", // ... },
بارگذاری فایلهای کتابخانه به صورت پویا
یک روش استفاده از این کتابخانه یا هر کتابخانهی جاوا اسکریپتی، افزودن مدخل تعریف آن به صفحهی index.html است:
<script src="node_modules/jssha/src/sha512.js"></script>
// map tells the System loader where to look for things var map = { // ... 'jssha': 'node_modules/jssha/src' }; // packages tells the System loader how to load when no filename and/or no extension var packages = { // ... 'jssha': { main: 'sha512.js', defaultExtension: 'js' } };
به این ترتیب هر زمانیکه کار import این کتابخانه صورت گیرد، بارگذاری پویای آن انجام خواهد شد. به علاوه ابزارهای بسته بندی و deploy پروژه هم این فایل را پردازش کرده و به صورت خودکار، کار bundling، فشرده سازی و یکی سازی اسکریپتها را انجام میدهند.
استفاده از jsSHA به صورت untyped
پس از دریافت بستههای این کتابخانه و مشخص سازی نحوهی بارگذاری پویای آن، اکنون نوبت به استفادهی از آن است. در اینجا منظور از untyped این است که فرض کنیم برای این کتابخانه، فایلهای typings مخصوص TypeScript وجود ندارند و پس از جستجوی در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped نتوانستهایم معادلی را برای آن پیدا کنیم. بنابراین فایل جدید untyped-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
import { Component, OnInit } from '@angular/core'; var jsSHA = require("jssha"); // ==> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions //declare var jsSHA: any; // ==> this requires adding <script src="node_modules/jssha/src/sha512.js"></script> to the first page manually. @Component({ templateUrl: 'app/using-third-party-libraries/untyped-sha.component.html' }) export class UnTypedShaComponent implements OnInit { hash: String; ngOnInit(): void { let shaObj = new jsSHA("SHA-512", "TEXT"); shaObj.update("This is a test"); this.hash = shaObj.getHash("HEX"); } }
<h1>SHA-512 Hash / UnTyped</h1> <p>String: This is a test</p> <p>HEX: {{hash}}</p>
هر زمانیکه فایلهای typing یک کتابخانهی جاوا اسکریپتی در دسترس نبودند، فقط کافی است از روش declare var jsSHA: any استفاده کنید. در اینجا any به همان حالت استاندارد و بینوع جاوا اسکریپت اشاره میکند. در این حالت برنامه بدون مشکل کامپایل خواهد شد؛ اما از تمام مزایای TypeScript مانند بررسی نوعها و همچنین intellisense محروم میشویم.
در این مثال در hook ویژهای به نام OnInit، کار ساخت شیء SHA را انجام داده و سپس هش عبارت This is a test محاسبه شده و به خاصیت عمومی hash انتساب داده میشود. سپس این خاصیت عمومی، در قالب این کامپوننت از طریق روش interpolation نمایش داده شدهاست.
دو نکتهی مهم
الف) اگر از روش declare var jsSHA: any استفاده کردید، کار بارگذاری فایل sha512.js به صورت خودکار رخ نخواهد داد؛ چون ماژولی را import نمیکند. بنابراین تعاریف systemjs.config.js ندید گرفته خواهد شد. در این حالت باید از همان روش متداول افزودن تگ script این کتابخانه به فایل index.html استفاده کرد.
ب) برای بارگذاری پویای کتابخانهی jsSHA بر اساس تعاریف فایل systemjs.config.js از متد require کمک بگیرید:
var jsSHA = require("jssha");
استفاده از jsSHA به صورت typed
کتابخانهی jsSHA در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/jssha دارای فایل d.ts. مخصوص خود است. برای نصب آن از یکی از دو روش ذیل استفاده کنید:
الف) نصب دستی فایلهای typings
npm install -g typings typings install jssha --save --ambient
ب) تکمیل فایل typings.ts
{ "ambientDependencies": { // ... "jssha": "registry:dt/jssha#2.1.0+20160317120654" } }
پس از نصب فایلهای typings این پروژه، به فایل main.ts مراجعه کرده و مدخل ذیل را به ابتدای آن اضافه کنید:
/// <reference path="../typings/browser/ambient/jssha/index.d.ts" />
در ادامه فایل جدید typed-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
import { Component, OnInit } from '@angular/core'; //import { jsSHA } from "jssha"; import * as jsSHA from "jssha"; // ===> var jsSHA = require("jssha"); // ===> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions @Component({ templateUrl: 'app/using-third-party-libraries/typed-sha.component.html' }) export class TypedShaComponent implements OnInit{ hash: String; ngOnInit(): void { let shaObj = new jsSHA("SHA-512", "TEXT"); shaObj.update("This is a test"); this.hash = shaObj.getHash("HEX"); } }
در اینجا تنها نکتهی مهم و جدید نسبت به روش قبل (استفاده از jsSHA به صورت untyped)، روش import این کتابخانه است. روش "import * as jsSHA from "jssha به عبارت var jsSHA = require("jssha") ترجمه میشود که در نهایت سبب بارگذاری خودکار فایلهای jssha بر اساس تعاریف مداخل آن در فایل systemjs.config.js میگردد.
کدهای کامل این پروژه را از اینجا میتوانید دریافت کنید.
با نصب این برنامه نسبتا کم حجم (اوپن سورس) مجموعه ای از چند ابزار جانبی برروی کامپیوترتان نصب میشود. یکی از ابزارهای بسیار کاربردی آن Color Picker است که با فشردن کلیدهای Win+shift+C این برنامه فعال شده و با کیلک بر روی هر نقطه از عکس کد رنگ نقطه انتخابی را نمایش میدهد.
سایر ابزار شامل این موارد است:
Always On Top, File Explorer add-ons, File locksmith, Image Resizer, Keyboard Manager, Mouse utilities, Power rename (enables you to perform bulk renaming, searching and replacing file names), PowerToysRun (can help you search and launch your app instantly. To open, use the shortcut Alt+Space and start typing), Screen Ruler, ShortCut Guid(shows the available shortcuts for the current state of the desktop), Text Extractor, Fancy Zones, PowerToys Awake
استفاده از XQuery - قسمت دوم
کوئری گرفتن از اسناد XML دارای فضای نام، توسط XQuery
در مثال زیر، تمام المانهای سند XML، در فضای نام http://www.people.com تعریف شدهاند.
DECLARE @doc XML SET @doc =' <p:people xmlns:p="http://www.people.com"> <p:person name="Vahid" /> <p:person name="Farid" /> </p:people> ' SELECT @doc.query('/people/person')
سعی دوم احتمالا روش ذیل خواهد بود
SELECT @doc.query('/p:people/p:person')
XQuery [query()]: The name "p" does not denote a namespace.
SELECT @doc.query(' declare default element namespace "http://www.people.com"; /people/person ')
SELECT @doc.query(' declare namespace aa="http://www.people.com"; /aa:people/aa:person ')
روش دیگر تعریف فضای نام، استفاده از WITH XMLNAMESPACES، پیش از تعریف کوئری است:
WITH XMLNAMESPACES(DEFAULT 'http://www.people.com') SELECT @doc.query('/people/person')
در اینجا نیز امکان کار با چندین فضای نام وجود دارد و برای این منظور تنها کافی است از تعریف Alias استفاده شود. فضاهای نام بعدی با یک کاما از هم مجزا خواهند شد.
WITH XMLNAMESPACES('http://www.people.com' AS aa) SELECT @doc.query('/aa:people/aa:person')
عبارات XPath و FLOWR
XQuery از دو نوع عبارت XPath و FLOWR میتواند استفاده کند. XQuery همیشه از XPath برای انتخاب دادهها و نودها استفاده میکند. در اینجا هر نوع XPath سازگار با استاندارد 2 آن، یک XQuery نیز خواهد بود. برای انجام اعمالی بجز انتخاب دادهها، باید از عبارات FLOWR استفاده کرد؛ برای مثال برای ایجاد حلقه، مرتب سازی و یا ایجاد نودهای جدید.
در مثال زیر که data آن در قسمت قبل تعریف شد، دو کوئری نوشته شده یکی هستند:
SELECT @data.query(' (: FLOWE :) for $p in /people/person where $p/age > 30 return $p ') SELECT @data.query(' (: XPath :) /people/person[age>30] ')
XPath بسیار شبیه به مسیر دهیهای یونیکسی است. بسیار فشرده بوده و همچنین مناسب است برای کار با ساختارهای تو در تو و سلسله مراتبی. مثال زیر را درنظر بگیرید:
/books/book[1]/title/chapter
در XPath توسط قابلیتی به نام محور میتوان به المانهای قبلی یا بعدی دسترسی پیدا کرد. این محورهای پشتیبانی شده در SQL Server عبارتند از self (خود نود)، child (فرزند نود)، parent (والد نود)، decedent (فرزند فرزند فرزند ...)و attribute (دسترسی به ویژگیها). محورهای استانداردی مانند preceding-sibling و following-sibling در SQL Server با عملگرهایی مانند >> و << پشتیبانی میشوند.
مثالهایی از نحوهی استفاده از محورهای XPath
اینبار قصد داریم یک سند XML نسبتا پیچیده را بررسی کرده و اجزای مختلف آنرا به کمک XPath بدست بیاوریم.
DECLARE @doc XML SET @doc=' <Team name="Project 1" xmlns:a="urn:annotations"> <Employee id="544" years="6.5"> <Name>User 1</Name> <Title>Architect</Title> <Expertise>Games</Expertise> <Expertise>Puzzles</Expertise> <Employee id="101" years="7.1" a:assigned-to="C1"> <Name>User 2</Name> <Title>Dev lead</Title> <Expertise>Video Games</Expertise> <Employee id="50" years="2.3" a:assigned-to="C2"> <Name>User 3</Name> <Title>Developer</Title> <Expertise>Hardware</Expertise> <Expertise>Entertainment</Expertise> </Employee> </Employee> </Employee> </Team> '
در XPath، محور پیش فرض، child است (اگر مانند کوئری زیر مورد خاصی ذکر نشود):
SELECT @doc.query('/Team/Employee/Name')
SELECT @doc.query('/Team/Employee/child::Name')
<Name>User 1</Name>
SELECT @doc.query('//Employee/Name')
<Name>User 1</Name> <Name>User 2</Name> <Name>User 3</Name>
برای کار با ویژگیها و attributes از [] به همراه علامت @ استفاده میشود:
SELECT @doc.query(' declare namespace a = "urn:annotations"; //Employee[@a:assigned-to]/Name ')
<Name>User 2</Name> <Name>User 3</Name>
SELECT @doc.query(' declare namespace a = "urn:annotations"; //Employee[attribute::a:assigned-to]/Name ')
SELECT @doc.query(' declare namespace a = "urn:annotations"; //Employee[not(@a:assigned-to)]/Name ')
<Name>User 1</Name>
SELECT @doc.query('count(//Employee[Name="User 1"]/Employee)')
در XPath برای یافتن والد از .. استفاده میشود:
SELECT @doc.query('//Employee[../Name="User 1"]')
استفاده از .. در SQL Server به دلایل کارآیی پایین توصیه نمیشود. بهتر است از همان روش قبلی کوئری تعداد کارمندانی که به user 1 مستقیما گزارش میدهند، استفاده شود.
عبارات FLOWR
FLOWR هستهی XQuery را تشکیل داده و قابلیت توسعه XPath را دارد. FLOWR مخفف for، let، order by، where و retrun است. از for برای تشکیل حلقه، از let برای انتساب، از where و order by برای فیلتر و مرتب سازی اطلاعات و از return برای بازگشت نتایج کمک گرفته میشود. FLOWR بسیار شبیه به ساختار SQL عمل میکند.
معادل عبارت SQL
Select p.name, p.job from people as p where p.age > 30 order by p.age
for $p in /people/person where $p.age > 30 order by $p.age[1] return ($p/name, $p/job)
تنها تفاوت مهم، در اینجا است که در عبارات SQL، خروجی کار توسط select، در ابتدای کوئری ذکر میشود، اما در عبارات FLOWR در انتهای آنها.
از let برای انتساب مجموعهای از نودها استفاده میشود:
let $p := /people/person return $p
یک نکته
اگر به order by دقت کنید، به اولین سن اشاره میکند. Order by در اینجا با تک مقدارها کار میکند و امکان کار با مجموعهای از نودها را ندارد. به همین جهت باید طوری آنرا تنظیم کرد که هربار فقط به یک مقدار اشاره کند.
هر زمانیکه به خطای requires a singleton برخوردید، یعنی دستورات مورد استفاده با یک سری از نودها کار نکرده و نیاز است دقیقا مشخص کنید، کدام مقدار مدنظر است.
مثالهایی از عبارات FLOWR
دو کوئری ذیل یک خروجی 1 2 3 را تولید میکنند
DECLARE @x XML = ''; SELECT @x.query(' for $i in (1,2,3) return $i '); SELECT @x.query(' let $i := (1,2,3) return $i ');
در ادامه اگر سعی کنیم به این کوئریها یک order by را اضافه کنیم، کوئری اول با موفقیت اجرا شده،
DECLARE @x XML = ''; SELECT @x.query(' for $i in (1,2,3) order by $i descending return $i '); SELECT @x.query(' let $i := (1,2,3) order by $i descending return $i ');
XQuery [query()]: 'order by' requires a singleton (or empty sequence), found operand of type 'xs:integer +'
ساخت المانهای جدید XML توسط عبارات FLOWR
ابتدا همان سند XML قسمت قبل را درنظر بگیرید:
DECLARE @doc XML =' <people> <person> <name> <givenName>name1</givenName> <familyName>lname1</familyName> </name> <age>33</age> <height>short</height> </person> <person> <name> <givenName>name2</givenName> <familyName>lname2</familyName> </name> <age>40</age> <height>short</height> </person> <person> <name> <givenName>name3</givenName> <familyName>lname3</familyName> </name> <age>30</age> <height>medium</height> </person> </people> '
SELECT @doc.query(' for $p in /people/person return <person> {$p/name[1]/givenName[1]/text()} </person> ');
<person>name1</person> <person>name2</person> <person>name3</person>
سؤال: اگر به این خروجی بخواهیم یک root element اضافه کنیم، چه باید کرد؟ اگر المان root دلخواهی را در return قرار دهیم، به ازای هر آیتم یافت شده، یکبار تکرار میشود که مدنظر ما نیست.
SELECT @doc.query(' <root> { for $p in /people/person return <person> {$p/name[1]/givenName[1]/text()} </person> } </root> ');
<root> <person>name1</person> <person>name2</person> <person>name3</person> </root>
مفهوم quantification در FLOWR
همان سند Team name=Project 1 ابتدای بحث جاری را درنظر بگیرید.
SELECT @doc.query('some $emp in //Employee satisfies $emp/@years >5') -- true SELECT @doc.query('every $emp in //Employee satisfies $emp/@years >5') -- false
پیشنیازها
- آشنایی با مباحث Migrations در EF Code first
- آشنایی با مباحث الگوی واحد کار
- چگونه مدلهای EF را به صورت خودکار به Context اضافه کنیم؟
- چگونه تنظیمات مدلهای EF را به صورت خودکار به Context اضافه کنیم؟
کدهایی را که در این قسمت مشاهده خواهید کرد، در حقیقت همان برنامهی توسعه یافته «آشنایی با مباحث الگوی واحد کار» است و از ذکر قسمتهای تکراری آن جهت طولانی نشدن مبحث، صرفنظر خواهد شد. برای مثال Context و مدلهای محصولات و گروههای آنها به همراه کلاسهای لایه سرویس برنامهی اصلی، دقیقا همان کدهای مطلب «آشنایی با مباحث الگوی واحد کار» است.
تعریف domain classes مخصوص افزونهها
در ادامهی پروژهی افزونه پذیر فعلی، پروژهی class library جدیدی را به نام MvcPluginMasterApp.Plugin1.DomainClasses اضافه خواهیم کرد. از آن جهت تعریف کلاسهای مدل افزونهی یک استفاده میکنیم. برای مثال کلاس News را به همراه تنظیمات Fluent آن به این پروژهی جدید اضافه کنید:
using System.Data.Entity.ModelConfiguration; namespace MvcPluginMasterApp.Plugin1.DomainClasses { public class News { public int Id { set; get; } public string Title { set; get; } public string Body { set; get; } } public class NewsConfig : EntityTypeConfiguration<News> { public NewsConfig() { this.ToTable("Plugin1_News"); this.HasKey(news => news.Id); this.Property(news => news.Title).IsRequired().HasMaxLength(500); this.Property(news => news.Body).IsOptional().IsMaxLength(); } } }
PM> install-package EntityFramework
مشکل! برنامهی اصلی، همانند مطلب «آشنایی با مباحث الگوی واحد کار» دارای domain classes خاص خودش است به همراه تنظیمات Context ایی که صریحا در آن مدلهای متناظر با این پروژه در معرض دید EF قرار گرفتهاند:
public class MvcPluginMasterAppContext : DbContext, IUnitOfWork { public DbSet<Category> Categories { set; get; } public DbSet<Product> Products { set; get; }
تغییرات اینترفیس Unit of work جهت افزونه پذیری
در ادامه، اینترفیس بهبود یافتهی IUnitOfWork را جهت پذیرش DbSetهای پویا و همچنین EntityTypeConfigurationهای پویا، ملاحظه میکنید:
namespace MvcPluginMasterApp.PluginsBase { public interface IUnitOfWork : IDisposable { IDbSet<TEntity> Set<TEntity>() where TEntity : class; int SaveAllChanges(); void MarkAsChanged<TEntity>(TEntity entity) where TEntity : class; IList<T> GetRows<T>(string sql, params object[] parameters) where T : class; IEnumerable<TEntity> AddThisRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class; void SetDynamicEntities(Type[] dynamicTypes); void ForceDatabaseInitialize(); void SetConfigurationsAssemblies(Assembly[] assembly); } }
SetDynamicEntities : توسط این متد در ابتدای برنامه، نوعهای مدلهای جدید افزونهها به صورت خودکار به Context اضافه خواهند شد.
SetConfigurationsAssemblies : کار افزودن اسمبلیهای حاوی تعاریف EntityTypeConfigurationهای جدید و پویا را به عهده دارد.
ForceDatabaseInitialize: سبب خواهد شد تا مباحث migrations، پیش از شروع به کار برنامه، اعمال شوند.
در کلاس Context ذیل، نحوهی پیاده سازی این متدهای جدید را ملاحظه میکنید:
namespace MvcPluginMasterApp.DataLayer.Context { public class MvcPluginMasterAppContext : DbContext, IUnitOfWork { private readonly IList<Assembly> _configurationsAssemblies = new List<Assembly>(); private readonly IList<Type[]> _dynamicTypes = new List<Type[]>(); public void ForceDatabaseInitialize() { Database.Initialize(force: true); } public void SetConfigurationsAssemblies(Assembly[] assemblies) { if (assemblies == null) return; foreach (var assembly in assemblies) { _configurationsAssemblies.Add(assembly); } } public void SetDynamicEntities(Type[] dynamicTypes) { if (dynamicTypes == null) return; _dynamicTypes.Add(dynamicTypes); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { addConfigurationsFromAssemblies(modelBuilder); addPluginsEntitiesDynamically(modelBuilder); base.OnModelCreating(modelBuilder); } private void addConfigurationsFromAssemblies(DbModelBuilder modelBuilder) { foreach (var assembly in _configurationsAssemblies) { modelBuilder.Configurations.AddFromAssembly(assembly); } } private void addPluginsEntitiesDynamically(DbModelBuilder modelBuilder) { foreach (var types in _dynamicTypes) { foreach (var type in types) { modelBuilder.RegisterEntityType(type); } } } } }
بهبود اینترفیس IPlugin جهت پذیرش نوعهای پویای EF
در قسمت اول، با اینترفیس IPlugin آشنا شدیم. هر افزونه باید دارای کلاسی باشد که این اینترفیس را پیاده سازی میکند. از آن جهت دریافت تنظیمات و یا ثبت تنظیمات مسیریابی و امثال آن استفاده میشود.
در اینجا متد GetEfBootstrapper آن کار دریافت تنظیمات EF هر افزونه را به عهد دارد.
namespace MvcPluginMasterApp.PluginsBase { public interface IPlugin { EfBootstrapper GetEfBootstrapper(); //...به همراه سایر متدهای مورد نیاز } public class EfBootstrapper { /// <summary> /// Assemblies containing EntityTypeConfiguration classes. /// </summary> public Assembly[] ConfigurationsAssemblies { get; set; } /// <summary> /// Domain classes. /// </summary> public Type[] DomainEntities { get; set; } /// <summary> /// Custom Seed method. /// </summary> public Action<IUnitOfWork> DatabaseSeeder { get; set; } } }
DomainEntities بیانگر لیست مدلها و موجودیتهای هر افزونه است.
DatabaseSeeder کار دریافت منطق متد Seed را بر عهده دارد. برای مثال اگر افزونهای نیاز است در آغاز کار تشکیل جداول آن، دیتای پیش فرض و خاصی را در بانک اطلاعاتی ثبت کند، میتوان از این متد استفاده کرد. اگر دقت کنید این Action یک وهله از IUnitOfWork را به افزونه ارسال میکند. بنابراین در این طراحی جدید، اینترفیس IUnitOfWork به پروژهی MvcPluginMasterApp.PluginsBase منتقل میشود. به این ترتیب دیگر نیازی نیست تا تک تک افزونهها ارجاع مستقیمی را به DataLayer پروژهی اصلی پیدا کنند.
تکمیل متد GetEfBootstrapper در افزونهها
اکنون جهت معرفی مدلها و تنظیمات EF آنها، تنها کافی است متد GetEfBootstrapper هر افزونه را تکمیل کنیم:
namespace MvcPluginMasterApp.Plugin1 { public class Plugin1 : IPlugin { public EfBootstrapper GetEfBootstrapper() { return new EfBootstrapper { DomainEntities = new[] { typeof(News) }, ConfigurationsAssemblies = new[] { typeof(NewsConfig).Assembly }, DatabaseSeeder = uow => { var news = uow.Set<News>(); if (news.Any()) { return; } news.Add(new News { Title = "News 1", Body = "news 1 news 1 news 1 ...." }); news.Add(new News { Title = "News 2", Body = "news 2 news 2 news 2 ...." }); } }; }
همچنین توسط delegate ایی به نام DatabaseSeeder، نحوهی دسترسی به متد Set واحد کار و سپس استفادهی از آن، برای تعریف متد Seed سفارشی نیز تکمیل شدهاست.
تدارک یک راه انداز EF، پیش از شروع به کار برنامه
در پوشهی App_Start پروژهی اصلی یا همان MvcPluginMasterApp، کلاس جدید EFBootstrapperStart را با کدهای ذیل اضافه کنید:
[assembly: PreApplicationStartMethod(typeof(EFBootstrapperStart), "Start")] namespace MvcPluginMasterApp { public static class EFBootstrapperStart { public static void Start() { var plugins = SmObjectFactory.Container.GetAllInstances<IPlugin>().ToList(); using (var uow = SmObjectFactory.Container.GetInstance<IUnitOfWork>()) { initDatabase(uow, plugins); runDatabaseSeeders(uow, plugins); } } private static void initDatabase(IUnitOfWork uow, IEnumerable<IPlugin> plugins) { foreach (var plugin in plugins) { var efBootstrapper = plugin.GetEfBootstrapper(); if (efBootstrapper == null) continue; uow.SetDynamicEntities(efBootstrapper.DomainEntities); uow.SetConfigurationsAssemblies(efBootstrapper.ConfigurationsAssemblies); } Database.SetInitializer(new MigrateDatabaseToLatestVersion<MvcPluginMasterAppContext, Configuration>()); uow.ForceDatabaseInitialize(); } private static void runDatabaseSeeders(IUnitOfWork uow, IEnumerable<IPlugin> plugins) { foreach (var plugin in plugins) { var efBootstrapper = plugin.GetEfBootstrapper(); if (efBootstrapper == null || efBootstrapper.DatabaseSeeder == null) continue; efBootstrapper.DatabaseSeeder(uow); uow.SaveAllChanges(); } } } }
همانطور که ملاحظه میکنید، ابتدا لیست تمام افزونههای موجود، به کمک StructureMap دریافت میشوند. سپس میتوان در متد initDatabase به متد GetEfBootstrapper هر افزونه دسترسی یافت و توسط آن تنظیمات مدلها را یافته و به Context اصلی برنامه اضافه کرد. سپس با فراخوانی ForceDatabaseInitialize تمام این موارد به صورت خودکار به بانک اطلاعاتی اعمال خواهند شد.
کار متد runDatabaseSeeders، یافتن DatabaseSeeder هر افزونه، اجرای آنها و سپس فراخوانی متد SaveAllChanges در آخر کار است.
کدهای کامل این سری را از اینجا میتوانید دریافت کنید:
MvcPlugin