BloggerToCHM 1.3
با تشکر. برای آخر هفته درستش میکنم.
Contact me
البته تمام اینها در حالتی است که سرمایه گذار همان ناشر باشد. اگر خودتون سرمایه گذاری کنید و خودتون هم پخش کنید بدیهی است این درصدها فرق میکنه ولی توصیه میکنم اصلا وارد بازی پخش نشوید که برای پخش کتابها واقعا اسیر خواهید شد و خلاصه بگم نه وقتش رو خواهید داشت و نه رابطهاش را. ناشر شما کتابها رو همون هفته اول پس از چاپ، پخش میکنه (بر اساس روابطی که داره) ولی خودتون شاید تا 6 ماه درگیر شوید.
NSA چگونه SSL را شنود میکند؟
شرایطی را در نظر بگیرید که نیاز است از تغییرات یک Entity در سیستم آگاه شویم. برای مثلا در زمان ثبت سفارش جدید در فروشگاه، ایمیلی به مدیر فروشگاه ارسال شود، یک Business Rule نیز چک شود و همچنین بنابر نیاز مشتری، تعداد آنها روز به روز ممکن است افزایش یابد و چه بسا در اعمال این Ruleها، موجودیتهای مختلفی درگیر باشند. در این صورت است که خواسته یا ناخواسته اتصال بین کلاسها خیلی افزایش خواهد یافت. یکی از راه حلهای رهایی از این پیچیدگی و اتصال بالا، استفاده از Event میباشد.
هدف طراحی و پیاده سازی زیرساختی برای استفاده از DomainEventها میباشد. کدهای کامل این مطلب را میتوانید از اینجا دریافت کنید.
Domain Event چیست؟
چیزی که در یک Domain خاصی رخ داده است و هدف از آن آگاه کردن سایر بخشهای آن Domain میباشد تا بتوانند واکنش مناسبی را نشان دهند. با بهره گیری از این نوع رویدادها، میتوان Separation Of Concerns خوبی را بین کلاسهای موجود در آن Domain اعمال کرد و به طراحی ای با Coupling پایین رسید. این رویدادها عموما داخل پروسه Raise میشوند.
namespace DomainEventsSample.Framework.Eventing.DomainEvents { public interface IDomainEvent : ITransientDependency { } }
namespace DomainEventsSample.Framework.Eventing.DomainEvents { public interface IDomainEventHandler<in T> : ITransientDependency where T : IDomainEvent { bool IsAdvisable { get; } void Handle(T domainEvent); } }
- متد Raise مربوط به Engine برای رویداد خاصی فراخوانی میشود.
- با استفاده از یک IOC Container، تمام هندلرهای مربوط به رویداد جمع آوری میشود.
- متد Handle مربوط به تک تک هندلرها، فراخوانی خواهد شد.
namespace DomainEventsSample.Framework.Eventing.DomainEvents { public interface IDomainEventEngine : ISingletonDependency { void Raise<T>(T domainEvent) where T : IDomainEvent; } } namespace DomainEventsSample.Framework.Eventing.DomainEvents { public class DomainEventEngine : IDomainEventEngine { private readonly IContainer _container; public DomainEventEngine(IContainer container) { _container = container; } public void Raise<T>(T domainEvent) where T : IDomainEvent { foreach (var handler in _container.GetAllInstances<IDomainEventHandler<T>>()) try { handler.Handle(domainEvent); } catch (Exception) { if (domainEvent.IsAdvisable && handler.IsAdvisable) throw; } } } }
namespace DomainEventsSample.Framework.Domain.Events { public abstract class EntityDomainEvent<TEntity> : IDomainEvent where TEntity : Entity { protected EntityDomainEvent(TEntity entity) { Entity = entity; } public TEntity Entity { get; } } }
کلاس بالا به عنوان کلاس پایه یکسری رویداد مشترک مابین Entityهای سیستم در نظر گرفته شده است.
namespace DomainEventsSample.Framework.Domain.Events { public class EntityCreatingEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntityCreatingEvent(TEntity entity) : base(entity) { } } } namespace DomainEventsSample.Framework.Domain.Events { public class EntityCreatedEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntityCreatedEvent(TEntity entity) : base(entity) { } } }
این رویدادها مربوط به زمان قبل و بعد از ایجاد یک Entity میباشند.
namespace DomainEventsSample.Framework.Domain.Events { public class EntityEditingEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntityEditingEvent(TEntity entity) : base(entity) { } } } namespace DomainEventsSample.Framework.Domain.Events { public class EntityEditedEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntityEditedEvent(TEntity entity) : base(entity) { } } }
این رویدادها مربوط به زمان قبل و بعد از ویرایش یک Entity میباشند.
namespace DomainEventsSample.Framework.Domain.Events { public class EntityDeletingEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntityDeletingEvent(TEntity entity) : base(entity) { } } } namespace DomainEventsSample.Framework.Domain.Events { public class EntityDeletedEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntityDeletedEvent(TEntity entity) : base(entity) { } } }
این رویدادها مربوط به زمان قبل و بعد از حذف یک Entity میباشند.
namespace DomainEventsSample.Framework.Domain.Events { public class EntitySavingEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntitySavingEvent(TEntity entity) : base(entity) { } } } namespace DomainEventsSample.Framework.Domain.Events { public class EntitySavedEvent<TEntity> : EntityDomainEvent<TEntity> where TEntity : Entity { public EntitySavedEvent(TEntity entity) : base(entity) { } } }
این رویدادها مربوط به زمان قبل و بعد از ذخیره (ایجاد و ویرایش) یک Entity میباشند.
نکته: برای اسکن کردن تمام هندلرها لازم است کد زیر را به تنظیمات StructureMap اضافه کنید:
Scan(scan => { scan.ConnectImplementationsToTypesClosing(typeof(IDomainEventHandler<>)); });
public class ProductCreatedEventHandler : IDomainEventHandler<EntityCreatedEvent<Product>> { public bool IsAdvisable => false; public void Handle(EntityCreatedEvent<Product> domainEvent) { //todo: notify users } }
در متد Create مربوط به ProductApplicationService و بعد از عملیات ذخیره سازی به شکل زیر میبایست عمل کرد:
public class ProductApplicationService : IProductApplicationService { private readonly IDomainEventEngine _eventEngine; private readonly IUnitOfWork _unitOfWork; private readonly IMapper _mapper; public ProductApplicationService(IDomainEventEngine eventEngine,IMapper mapper,IUnitOfWork unitOfWork) { _eventEngine = eventEngine; _mapper=mapper; _unitOfWork=unitOfWork; } [Transactional] public void Create(ProductCreateViewModel model) { var entity=_mapper.Map<Product>(model); _unitOfWork.Set<Product>().Add(entity); _unitOfWork.SaveChanges(); _eventEngine.Raise(new EntityCreatedEvent<Product>(entity)); } }
البته بهتر است برای Raise کردن این نوع رویدادها از مکانیزم Hook استفاده کرد و در زمان ذخیره سازی و فراخوانی متد SaveChange، این عملیات به صورت خودکار صورت گیرند.
در مقاله بعدی با استفاده از Hookها این عملیات را انجام خواهیم داد.
کدهای این قسمت را میتوانید از اینجا دریافت کنید.
طراحی پایگاه داده چند زبانه
نمایش دو جدول در یک صفحه
هنگامیکه برای اولین بار، جستجو بر مبنای کلمات کلیدی (keyword search) بر روی مجموعهای از متون، به دنیای بازیابی اطلاعات معرفی شد شاید فقط یک ذهنیت مطرح میشد و آن یافتن لغت در متن بود. به بیان دیگر در آن زمان تنها بدنبال متونی میگشتیم که دقیقا شامل کلمه کلیدی مورد جستجوی کاربر باشند. روال کار نیز بدین صورت بود که از دل پرس و جوی کاربر، کلماتی بعنوان کلمات کلیدی استخراج میشد. سپس الگوریتم جستجو در میان متون موجود بدنبال متونی میگشت که دقیقا یک یا تمامی کلمات کلیدی در آن آمده باشند. اگر متنی شامل این کلمات بود به مجموعه جوابها اضافه میگردید و در غیر این صورت حذف میگشت. در پایان جستجو با استفاده از الگوریتمی، نتایج حاصل رتبه بندی میگشت و به ترتیب رتبه با کاربر نمایش داده میشد.
نکته مهمی که در این روش دیده میشود اینست که متون به تنهایی و بدون در نظر گرفتن کل مجموعه پردازش میشدند و اگر تصمیمی مبنی بر جواب بودن یک متن گرفته میشد، آن تصمیم کاملا متکی به همان متن و مستقل از متون دیگر گرفته میشد. در آن سالها هیچ توجهی به وابستگی موجود بین متون مختلف و ارتباط بین آنها نمیشد که این مسئله یکی از عوامل پایین بودن دقت جستجوها بشمار میرفت.
بر اساس دیدگاه LSI اسناد مشابه با هم، اسنادی هستند که لغات مشابه یا مشترک بیشتری داشته باشند. توجه داشته باشید تنها نمیگوییم لغات مشترک بیشتری بلکه از واژه لغات مشابه نیز استفاده میکنیم. چرا که بر اساس LSI دو سند ممکن است هیچ لغت مشترکی نداشته باشند (یعنی لغات یکسان نداشته باشند) اما لغاتی در آنها وجود داشته باشد که به لحاظی معنایی و مفهومی هم معنا و یا مرتبط به هم باشند. بعنوان مثال لغات شش و ریه دو لغت متفاوت اما مرتبط با یکدیگر هستند و اگر دو لغات در دوسند آورده شوند میتوان حدس زد که ارتباط و شباهتی معنایی بین آنها وجود دارد. به روش هایی که بر اساس این دیدگاه ارائه میشوند روشهای جستجوی معنایی نیز گفته میشود. این دیدگاه مشابه دیدگاه انسانی در مواجهه با متون نیز است. انسان هنگامی که دو متن را با یکدیگر مقایسه میکند تنها بدنبال لغات یکسان در آنها نمیگردد بلکه شباهتهای معنایی بین لغات را نیز در نظر میگیرد این اصل و نگرش پایه و اساس الگوریتم LSI و همچنین حوزه ای از علم بازیابی اطلاعات بنام مدل سازی موضوعی (Topic Modeling) میباشد.
برای آنکه با دیدگاه LSI بیشتر آشنا شوید در اینجا مثالی از نحوه عملکرد آن میزنیم. فرض کنید میخواهیم بر روی مجموعه ای از اسناد در حوزه زیست شناسی اندیس گذاری کنیم. بر مبنای روش LSI چنانچه لغاتی مانند کروموزم، ژن و DNA در اسناد زیادی در کنار یکدیگر آورده شوند (یا بعبارتی اسناد مشترک باهم زیادی داشته باشند)، الگوریتم جستجو چنین برداشت میکند که به احتمال زیاد نوعی رابطه معنایی بین آنها وجود دارد. به همین دلیل اگر شما پرس و جویی را با کلمه کلیدی "کروموزوم" اجرا نمایید، الگوریتم علاوه بر مقالاتی که مستقیما واژه کروموزوم در آنها وجود دارد، اسنادی که شامل لغات "DNA" و "ژن" نیز باشند را بعنوان نتیجه به شما باز خواهد گرداند. در واقع میتوان گفت الگوریتم جستجو به پرس و جوی شما این دو واژه را نیز اضافه میکند که همان بسط دادن پرس و جوی شما است. دقت داشته باشید که الگوریتم جستجو هیچ اطلاع و دانشی از معنای لغات مذکور ندارد و تنها بر اساس تحلیلهای ریاضی به این نتیجه میرسد که در بخشهای بعدی چگونگی آن را برای شما بازگو خواهیم نمود. یکی از برتریهای مهم LSI نسبت به روشهای مبتنی بر کلمات کلیدی (keyword based) این است که در LSI، ما به recall بالاتری دست پیدا میکنیم، بدین معنی که از کل جوابهای موجود برای پرس و جوی شما، جوابهای بیشتری به کاربر نمایش داده خواهند شد.