راهنماهای پروژه‌ها
سؤالات متداول
  • می‌خواهم برنامه‌ام را بر روی کامپیوتر مشتری بدون دردسر نصب کنم؛ با حداقل حجم و دردسر توزیع. به علاوه بدون نیاز به وصله پینه کردن اسمبلی‌های تجاری دریافت شده.

پاسخ: PdfReport از چند اسمبلی دات نتی تشکیل شده است که نیاز به نصب خاصی ندارد. همچنین حجم فشرده شده آن نیز زیر 2 مگابایت است. بنابراین از جهت توزیع مشکل خاصی نخواهید داشت. همچنین کل این مجموعه سورس باز است و مشکلات متداول همراه با گزارش سازهای تجاری را به همراه ندارد.

  • می‌خواهم فایل گزارش، به همراه برنامه و فایل exe آن و نه جدای از آن توزیع شود.

پاسخ: روش کار با PdfReport اصطلاحا code-first است. یعنی یک یا چند کلاس تهیه شده توسط شما، کار تهیه گزارش را انجام می‌دهند و تمام این‌ها کامپایل شده و به همراه فایل اجرایی یا اسمبلی‌های خاصی که برای آن درنظر می‌گیرید، کامپایل و توزیع خواهند شد. همچنین با توجه به code-first بودن آن و عدم وابستگی به فناوری خاصی، این گزارشات در برنامه‌های وب و ویندوز نیز می‌توانند بدون نیاز به تغییری در کدهای شما مورد استفاده قرار گیرند.

  • برای کار با PdfReport از کجا باید شروع کرد؟

پاسخ: لطفا برچسب PdfReport را در سایت جاری دنبال نمائید. نحوه استفاده از قابلیت‌های آن قدم به قدم توضیح داده شده‌اند.

  • می‌خواهم برای صرفه جویی در کاغذ چاپی، گزارش چند ستونه‌ای را تهیه کنم.

پاسخ: لطفا مراجعه کنید به مثال ایجاد قالب‌های سفارشی ستون‌ها.

  • می‌خواهم گزارشی را تولید کنم که حجم متن فیلدهای آن مشخص نیست. یکی ممکن است نصف صفحه باشد دیگری دو صفحه و یا بعضی تنها یک سطر.

پاسخ: در PdfReport ارتفاع هر سطر به صورت خودکار بر مبنای حجم وارد شده محاسبه و تنظیم می‌گردد. به عبارتی این تنظیم ثابت نیست و سبب حذف محتوای ارائه شده در آن‌ها یا محو شدن ردیف‌های دیگر نمی‌گردد.

  • نیاز به گزارش سازی دارم که بدون مشکل با ORMها کار کند. برای مثال در حین کار با Entity framework مستقیما بتواند با اشیاء و لیست‌های حاصل از آن کار کند.

پاسخ: یکی از انواع منابع داده تعریف شده در PdfReport جهت کار با ORMها طراحی شده است که مثالی از نحوه استفاده از آن‌را در مثال EF Code first همراه با مجموعه مثال‌های این کتابخانه می‌توانید ملاحظه نمائید.

  • آیا این کتابخانه می‌تواند از فایل سیستم (و نه صرفا بانک اطلاعاتی) هم تصاویر را دریافت و در گزارشات قرار دهد؟

پاسخ: بلی. لطفا به مثال ImageFilePath مراجعه نمائید.

  • می‌خواهم یک گزارش ساز پویا در برنامه داشته باشم. فقط کوئری غیر مشخصی را به آن بدهم و حاصل آن یک گزارش باشد.

پاسخ: امکان تهیه گزارش‌های پویا نیز در PdfReport پیش بینی شده است. توضیحات بیشتر همچنین این امکان در حین کار با ORMها نیز وجود دارد.

  • می‌خواهم بدون استفاده از بانک اطلاعاتی نیز بتوانم گزارشی را تهیه کنم. برای مثال یک لیست جنریک تشکیل شده در حافظه دارم.

پاسخ: برای این منظور تنها کافی است از منبع داده صحیحی استفاده نمائید. برای اطلاعات بیشتر به مثال IList مراجعه کنید.

  • می‌خواهم از بانک‌های اطلاعاتی دیگری بجز SQL Server استفاده کنم.

پاسخ: می‌توانید یک نمونه مثال استفاده از PdfReport را با بانک اطلاعاتی SQLite، در اینجا مشاهده کنید.

بازخوردهای پروژه‌ها
عدم اعمال style در ستون های سفارشی
با سلام
در گزارش من استایل اعمال نمیشود. تنظیمات ابتدایی:
_pdfReport.MainTablePreferences((table) => {
table.ColumnsWidthsType(TableColumnWidthType.Relative);
});
تنظیمات ستون‌ها :
_columns.AddColumn((column) => {
column.PropertyName("col2");
column.CellsHorizontalAlignment(HorizontalAlignment.Center);
column.IsVisible(true);
 column.Order(5);
column.Width(3);
column.HeaderCell("   از    |   تا   ");
column.ColumnItemsTemplate((itemsTemplate) => itemsTemplate.Html());
 column.CalculatedField((list) => {
var FromDate = list.GetSafeStringValueOf<PersonnelTraining>((x) => x.FromDate);
var ToDate = list.GetSafeStringValueOf<PersonnelTraining>((x) => x.ToDate);
return "<table style='width: 100px;'><tr><td style='border-right:1px solid #000;'>" + FromDate + "</td><td>" + ToDate + "</td></tr></table>";
 });
در جدول و ستون‌ها استایلی اعمال نمیشود. در نهایت هدف ایجاد چنین ستونی میباشد.


نظرات مطالب
EF Code First #4
ممکنه در نگارش‌های اولیه EF Code first از این نوع خطاها وجود داشته و بعدا برطرف شده. در مورد ارتقاء به EF 6 به این مطالب مراجعه کنید: «ارتقاء به Entity framework 6 و استفاده از بانک‌های اطلاعاتی غیر از SQL Server» و همچنین «بروز رسانی استفاده از SqlServer Compact در Entityframework 6.0»
مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت سوم
پس از بررسی ساختار یک پروژه‌ی افزونه پذیر و همچنین بهبود توزیع فایل‌های استاتیک آن، اکنون نوبت به کار با داده‌ها است. هدف اصلی آن نیز داشتن مدل‌های اختصاصی و مستقل Entity framework code-first به ازای هر افزونه است و سپس بارگذاری و تشخیص خودکار آن‌ها در Context مرکزی برنامه.

پیشنیازها
- آشنایی با مباحث 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; }
اکنون برنامه‌ی اصلی چگونه باید مدل‌ها و تنظیمات سایر افزونه‌ها را یافته و به صورت خودکار به این Context اضافه کند؟ با توجه به اینکه این برنامه هیچ ارجاع مستقیمی را به افزونه‌ها ندارد.


تغییرات اینترفیس 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);
                }
            }
        }
    }
}
در متد استاندارد OnModelCreating، فرصت افزودن نوع‌های پویا و همچنین تنظیمات پویای آن‌ها وجود دارد. برای این منظور می‌توان از متدهای modelBuilder.RegisterEntityType و modelBuilder.Configurations.AddFromAssembly کمک گرفت.


بهبود اینترفیس 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; }
    } 
}
ConfigurationsAssemblies مشخص کننده‌ی اسمبلی‌هایی است که حاوی تعاریف EntityTypeConfigurationهای افزونه‌ی جاری هستند.
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 ...."
                    });
                }
            };
        }
در اینجا نحوه‌ی معرفی مدل‌های جدید را توسط خاصیت DomainEntities و تنظیمات متناظر را به کمک خاصیت ConfigurationsAssemblies مشاهده می‌کنید. باید دقت داشت که هر اسمبلی فقط باید یکبار معرفی شود و مهم نیست که چه تعداد تنظیمی در آن وجود دارند. کار یافتن کلیه‌ی تنظیمات از نوع EntityTypeConfigurationها به صورت خودکار توسط EF صورت می‌گیرد.
همچنین توسط 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();
            }
        }
    }
}
در اینجا یک راه انداز سفارشی از نوع PreApplicationStartMethod تهیه شده‌است. Pre بودن آن به معنای اجرای کدهای متد Start این کلاس، پیش از آغاز به کار برنامه و پیش از فراخوانی متد Application_Start فایل Global.asax.cs است.
همانطور که ملاحظه می‌کنید، ابتدا لیست تمام افزونه‌های موجود، به کمک StructureMap دریافت می‌شوند. سپس می‌توان در متد initDatabase به متد GetEfBootstrapper هر افزونه دسترسی یافت و توسط آن تنظیمات مدل‌ها را یافته و به Context اصلی برنامه اضافه کرد. سپس با فراخوانی ForceDatabaseInitialize تمام این موارد به صورت خودکار به بانک اطلاعاتی اعمال خواهند شد.
کار متد runDatabaseSeeders، یافتن DatabaseSeeder هر افزونه، اجرای آن‌ها و سپس فراخوانی متد SaveAllChanges در آخر کار است.



کدهای کامل این سری را از اینجا می‌توانید دریافت کنید:
MvcPlugin
اشتراک‌ها
نگاهی به EF7 RC1

· EF 7.0.0 will not become the “official” version of Entity Framework.
· EF6 will continue to be the official version.
· EF6 will continue to be what’s delivered when you install entityframework via Nuget.
· Your apps that use EF6 will not automatically update to EF7!  

نگاهی به EF7 RC1
اشتراک‌ها
استفاده از Sql Server FileTable به همراه EF
FileTable is a great new feature in SQL Server 2012 which facilitates storing and working with unstructured blob data in SQL Server. Unfortunately is not yet supported in Entity Framework, so you cannot simply use FileTable in your entity model. But there is a workaround for this inconvenience, which I am going to show you in this post. 
استفاده از Sql Server FileTable به همراه EF
اشتراک‌ها
یک Business Apps ساخته شده با Asp.net Core Mvc و , TypeScript

یک CMS تجاری بزرگ با قابلیت‌های زیر

A modular, service based web application model
Code generator to produce initial services / user interface code for an SQL table
T4 based code generation on server to reference script widgets with intellisense / compile time validation
T4 based code generation to provide compile time type safety and intellisense while calling AJAX services from script side.
An attribute based form definition system (prepare UI in server side with a simple C# class)
Automatic seamless data-binding through form definitions (form <-> entity <-> service).
Caching Helpers (Local / Distributed)
Automatic cache validation
Configuration System (storage medium independent. store settings in database, file, whatever...)
Simple Logging
Reporting (reports just provide data, has no dependency on rendering, similar to MVC)
Script bundling, minification (making use of Node / UglifyJS / CleanCSS) and content versioning (no more F5 / clear browser cache)
Fluent SQL Builder (SELECT/INSERT/UPDATE/DELETE)
Micro ORM (also Dapper is integrated)
Customizable handlers for REST like services that work by reusing information in entity classes and do automatic validation.
Attribute based navigation menu
UI Localization (store localized texts in json files, embedded resource, database, in memory class, anywhere)
Data Localization (using an extension table mechanism helps to localize even data entered by users, like lookup tables)
Script widget system (inspired by jQueryUI but more suitable for C# code)
Client side and server side validation (based on jQuery validate plugin, but abstracts dependency)
Audit logging (where CDC is not available)
System for data based integration tests
Dynamic scripts
Script side templates 
یک Business Apps ساخته شده با Asp.net Core Mvc و , TypeScript
اشتراک‌ها
کتابخانه mark.js

JavaScript keyword highlighting. Mark text with options for diacritics, synonyms, custom elements, custom class names, separate word search (multiple terms), exclusion selectors, accuracy, iframes and custom filter functions. Also available as jQuery plugin.   Demo

کتابخانه mark.js