PDA

View Full Version : آموزش: ایجاد یک پروژه MVC با به کارگیری الگوی Unit of work با استفاده از کتابخانه Structuremap چند لایه



ali_autumnal
جمعه 25 بهمن 1392, 17:08 عصر
با سلام

کلیه مطالب برگرفته از مطالب ارائه شده از مهندس وحید نصیری (http://www.dotnettips.info/) می باشد البته با کمی دخل و تصرف!


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

یه خواهش: برای ابراز احساسات از گزینه تشکر استفاده نمائید خواهشا از پست ها جهت ابراز احساسات استفاده نکنید!

بدون مقدمه یه راست سر اصل مطلب:

برای ایجاد پروژه مراحل زیر را انجام می دهیم:

با فرض اینکه نام پروژه اصلی MVCProject هست

New=> Project=> ASP.Net MVC 4 Web Application


پس از OK اول کادری باز شده در آن سومین گزینه از چپ یعنی Internet appllication

A default ASP.NET MVC 4 project with an account controller that uses forms authentication.
را انتخاب کنید
View engin را هم Razor انتخاب شود.

پس از ایجاد پروژه اصلی Class Library های زیر را در ریشه پروژه اصلی ایجاد کنید
کلیک راست بروی ریشه اصلی پروژه و گزینه add سپس New Project


MVCProject.DataLayer
MVCProject.DomainClasses
MVCProject.ServiceLayer
MVCProject.Models


سپس با دستور

Install-Package EntityFramework


در Package Manager Console، EntityFramework را در پروژه های

Project
DataLayer
DomainClasses
ServiceLayer
Models


نصب کنید.

سپس در DataLayer توسط دستور زیر Migrations رو نصب کنید

Install-Package EntityFramework.Migrations -Version 0.9.0.0

در Web.config در بخش Connection نام دیتابیس خود را بنویسید. درواقع DefaultConnection رو به "your db name" تغییر نام دهید
سپس در DataLayer با نام دیتابیس تون یه کلاس اضافه کنید
از طرفی باید به References این پروژه DomainClasses رو اضافه کنید
Interface زیر رو قبل از کلاس (نام دیتابیستون) تایپ کنید

public interface IUnitOfWork
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
int SaveChanges();
}


سپس کلاس اصلی رو به شکل زیر تغییر دهید:

public class "your db name" : DbContext, IUnitOfWork
{
public "your db name"()
: base("name=DefaultConnection")
{
}

public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}

public DbSet<UserProfile> UserProfiles { set; get; }

and other table...

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
تنظیم کلیدهای خارجی
تنظیم کلیدهای چندتایی
تنظیم حذف خودکار فرزند

base.OnModelCreating(modelBuilder);
}
}

به پروژه اصلی رفته و references های زیر رو اضافه کنید:

DataLayer
DomainClasses
ServiceLayer
Models

سپس Build Solution
سپس تو DataLayer دستور ریر رو اجرا کنید

Enable-Migrations -ContextTypeName "your db name"

پس از اجرای دستور و نصب Migrations داخل پوشه Migrations کلاس Configuration رو باز کرده و کدهای زیر را به آن اضافه کنید:

public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true; // <-- THIS LINE
}


مدل های اصلی پروژه رو به DomainClasses اضافه کنید
سپس در صورت طی همه مراحل پس از اجرای دستور زیر باید دیتابیس شما ایجاد بشه

update-database

سپس تو Project دستورات زیر رو اجرا کنید تا structuremap نصب شود

Install-Package structuremap
Install-Package StructureMap.MVC4




ادامه دارد...

مهدی هادیان2
یک شنبه 27 بهمن 1392, 14:59 عصر
بسم الله الرحمن الرحیم
با سلام


Install-Package EntityFramework
با نصب این مورد اعلام میکنه که EntityFramework به تمام پروژه ها اضافه شده است ولی تنها در رفرنس پروژه اصلی دیده می شود !!!!!!
با دستور
Install-Package EntityFramework ServiceLayer می خوام به صورت دستی به بقیه ها پروژه ها هم اضافه کنم با کمال تعجب میگه که قبلا مورد مذکور رو اضافه کرده است:

'EntityFramework 6.0.2' already installed.
Successfully added 'EntityFramework 6.0.2' to ServiceLayer.
و این بار به رفرنس پروژه مورد نظر هم افزوده شده است.
موضوع چیه؟:متعجب:
با سپاس فراوان

helpsos
یک شنبه 27 بهمن 1392, 16:50 عصر
با سلام
میشه این قطعه کد رو توضیح بدین؟
public interface IUnitOfWork
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
int SaveChanges();
}

helpsos
یک شنبه 27 بهمن 1392, 23:02 عصر
بسم الله الرحمن الرحیم
با سلام

با نصب این مورد اعلام میکنه که EntityFramework به تمام پروژه ها اضافه شده است ولی تنها در رفرنس پروژه اصلی دیده می شود !!!!!!
با دستور
Install-Package EntityFramework ServiceLayer می خوام به صورت دستی به بقیه ها پروژه ها هم اضافه کنم با کمال تعجب میگه که قبلا مورد مذکور رو اضافه کرده است:

'EntityFramework 6.0.2' already installed.
Successfully added 'EntityFramework 6.0.2' to ServiceLayer.
و این بار به رفرنس پروژه مورد نظر هم افزوده شده است.
موضوع چیه؟:متعجب:
با سپاس فراوان
برای نصب در هر پروژه باید به صورت زیر عمل نمایید(با تشکر از وحید نصیری)
http://www.dotnettips.info/file/image?name=dfprj.png

ali_autumnal
سه شنبه 29 بهمن 1392, 11:47 صبح
با سلام
میشه این قطعه کد رو توضیح بدین؟
public interface IUnitOfWork
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
int SaveChanges();
}

در این تایپیک من قصد دارم درمورد نحوه ایجاد پروژه توضیحاتی بدم. در نتیجه جهت درک بهتر مطلب توصیه میکنم اصل منبع (http://www.dotnettips.info/post/842/ef-code-first-12)رو مطالعه فرمائید.

ali_autumnal
سه شنبه 29 بهمن 1392, 11:51 صبح
با نصب این مورد اعلام میکنه که EntityFramework به تمام پروژه ها اضافه شده است ولی تنها در رفرنس پروژه اصلی دیده می شود !!!!!!

به همان روشی که helpsos عزیز پاسخ دادند عمل کنید.

ali_autumnal
جمعه 02 اسفند 1392, 18:22 عصر
ادامه مطلب:

به DomainClasses رفته و کلاس های مورد نیاز پروژه خود را اضافه کنید
به لایه سرویس رفته و references زیر را اضافه کنید:

DataLayer
DomainClasses
Models


سرویس های مورد نیاز پروژه را به لایه سرویس اضافه کنید.

به لایه Models رفته و references زیر را اضافه کنید:

DomainClasses
Models

مدل های پروژه را در این لایه اضافه کنید.

تغییرات زیر را در Global.asax انجام دهید:

add using:

using StructureMap;
using Project.DataLayer;
using Project.ServiceLayer;


add:
protected void Application_Start()
{
...
initStructureMap();
}


private static void initStructureMap()
{
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWork>().HttpContextScoped().Use(() => new DbContext());
x.For<ISampleService>().Use<EfSampleService>();
type all service in use project
...
});
//Set current Controller factory as StructureMapControllerFactory
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
}

protected void Application_EndRequest(object sender, EventArgs e)
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObject s();
}

کلاس زیر را در Global.asax تایپ کنید:

and add this class:

public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return ObjectFactory.GetInstance(controllerType) as Controller;
}
}

پس از اتمام کار Build Solution کنید.

مهدی هادیان2
سه شنبه 06 اسفند 1392, 20:34 عصر
بسم الله الرحمن الرحیم
با سلام

Enable-Migrations -ContextTypeName "your db name"
با اجرای این فرمان در پروژه DataLayer خطای زیر رو میده:

enable migrations parameter cannot be found that matches parameter name
با سپاس فراوان

helpsos
پنج شنبه 08 اسفند 1392, 16:50 عصر
بسم الله الرحمن الرحیم
با سلام

با اجرای این فرمان در پروژه DataLayer خطای زیر رو میده:

enable migrations parameter cannot be found that matches parameter name
با سپاس فراوان
سلام
ببینید هر جایی که
your db name هست رو فکر کنم باید خودتون یه نام دلخواه بدون داشتن فاصله بدهید ببینید مشکل حل میشه!

مهدی هادیان2
جمعه 09 اسفند 1392, 16:41 عصر
بسم الله الرحمن الرحیم

سلام
ببینید هر جایی که
your db name هست رو فکر کنم باید خودتون یه نام دلخواه بدون داشتن فاصله بدهید ببینید مشکل حل میشه!


با سلام
به جای your db name اسم بانکم رو گذاشتم:

PM> Enable-Migrations -MVCProjectContext "MVCProject"


نمی دونم مسئله از کجاست؟
با سپاس

[/CSHARP]

ali_autumnal
شنبه 10 اسفند 1392, 18:31 عصر
بجای این


PM> Enable-Migrations -MVCProjectContext "MVCProject"


تایپ کنید

PM> Enable-Migrations -MVCProjectContext MVCProject

مهدی هادیان2
شنبه 17 اسفند 1392, 21:47 عصر
بسم الله الرحمن الرحیم

بجای این


تایپ کنید

PM> Enable-Migrations -MVCProjectContext MVCProject

با سلام
متاسفانه بازهم خطا میده:

Enable-Migrations : A parameter cannot be found that matches parameter name 'MVCProjectContext'.
At line:1 char:37
+ Enable-Migrations -MVCProjectContext <<<< MVCProject
+ CategoryInfo : InvalidArgument: (:) [Enable-Migrations], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Enable-Migrations

با سپاس

helpsos
یک شنبه 18 اسفند 1392, 10:48 صبح
بسم الله الرحمن الرحیم

با سلام
متاسفانه بازهم خطا میده:

Enable-Migrations : A parameter cannot be found that matches parameter name 'MVCProjectContext'.
At line:1 char:37
+ Enable-Migrations -MVCProjectContext <<<< MVCProject
+ CategoryInfo : InvalidArgument: (:) [Enable-Migrations], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Enable-Migrations

با سپاس
سلام
در کنسول پاورشل اول از قسمت default project پروژه Datalayer را انتخاب کن و بعد تایپ کن Enable-Migrations و enter کن

hp1361
یک شنبه 18 اسفند 1392, 11:51 صبح
با سلام

به نظرم اگه در مورد مفاهیم بحث بشه خیلی بهتره! این کدها که توی اون سایت هست. اما اگه بشه در مورد این مسائل سوال پرسید و پاسخی هم داده بشه خیلی کاربردی تر خواهد بود.

مثلا اینکه Unit of work چیه اساساً، و چرا ازش استفاده میکنیم! یا خیلی ساده تر چرا DataLayer
DomainClasses
ServiceLayer
Models رو می سازیم؟!

hakim22
یک شنبه 18 اسفند 1392, 15:28 عصر
به نظر من وقتی Ninject هست دلیلی برای استفاه از StructureMap نیست. من برای اولین بار Ninject رو در 1 ساعت پیاده کردم و هیچوقت مشکلی باهاش نداشتم.

البته مسئله ربطی به ابزاری که از استفاده می کنیم نداره ، به اینکه چرا داریم ازش استفاده می کنیم ربط داره و کلا باید با مفهوم Unit Of Work و IoC آشنا بود تا این ابزار و اینکه چه پارامترهایی برای راه اندازی و تنظیم نیاز دارند برای ما قابل درک باشه.

hp1361
سه شنبه 27 اسفند 1392, 01:44 صبح
سلام

سوالی که برای من پیش آمده اینه که در کدام لایه باید به یک وب سرویس متصل شد و چطور باید خطاهای وب سرویس رو به کاربر نمایش داد؟

ali_autumnal
پنج شنبه 07 فروردین 1393, 12:51 عصر
توضیحاتی در رابطه با لایه ها:

لایه ServiceLayer پروژه همه کارها رو واستون انجام خواهد داد.

به ازای هر مدل اصلی DomainClasses یک سرویس بنویسید

در واقع بهتره برای هر کنترلر یک Service بنویسید در این صورت می تونید از سرویس هایی مختلف در یک سرویس Main که واسه کنترلر اختصاص داده اید بهره ببرید
مثلا از 10 سرویس اصلی در یک سرویسی که برای کنترلر نوشته اید استفاده کنید

لایه DataLayer فقط برای DbContext هست

لایه Models کلا برای دیتاهایی که بین Sevice<=>Controller<=>View انتقال یا ارسال می شود کابرد دارد

فقط و فقط لایه Service پروژه با DomainClasses در ارتباط هست

در واقع Model ارسال شده از سمت View در Controller اعتبارسنجی خواهد شد در صورت اعتبار به لایه سرویس ارسال خواهد شد در لایه سرویس مدل دریافت شده به مدل اصلی(DomainClasses) تبدیل می شود و به سرویس مربوطه ارسال خواهد شد. سرویس اصلی مدل دریافت شده را در دیتابیس ذخیره خواهد کرد.

helpsos
سه شنبه 19 فروردین 1393, 23:23 عصر
با سلام
آیا نحوه کار با structuremap در MVC5 با مطالب گفته شده در بالا متفاوت است؟

آخه من StructureMap را نصب نمودم ولی چون از نسخه mvc5 استفاده نمودم چیزی برای آن نبود تا نصب کنم و بعد هم در فایل global نیز کدهای شما را وارد نمودم ولی در این خط x.For<IUnitOfWork>().HttpContextScoped().Use(() => new Sample07Context());

به HttpContextScoped و در این خط ObjectFactory.ReleaseAndDisposeAllHttpScopedObject s();

به ReleaseAndDisposeAllHttpScopedObjects گیر میده.

hp1361
دوشنبه 15 اردیبهشت 1393, 14:15 عصر
سلام

StructureMap.Web رو به پروژه اضافه کنید.

بجای دستور

ObjectFactory.ReleaseAndDisposeAllHttpScopedObject s();


بنویسید

StructureMap.Web.Pipeline.HttpContextLifecycle.Dis poseAndClearAll();


موفق باشیم

helpsos
دوشنبه 15 اردیبهشت 1393, 22:18 عصر
سلام
من کدهای زیر را در global نوشتم ولی یک خطا میده که عکس اون در زیر است

کد ها private static void InitStructureMap() {
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWork>().HttpContextScoped().Use(() => new PartakContext());
x.For<IPhoneTypeService>().Use<EFPhoneTypeService>();


});
//Set current Controller factory as StructureMapControllerFactory
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
}


protected void Application_EndRequest(object sender, EventArgs e)
{
HttpContextLifecycle.DisposeAndClearAll();
}
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return ObjectFactory.GetInstance(controllerType) as Controller;
}
}




ولی این خطا رو میده
118675

کسی می تونه کمکی بکنه؟

ali_autumnal
سه شنبه 16 اردیبهشت 1393, 22:11 عصر
سلام
من کدهای زیر را در global نوشتم ولی یک خطا میده که عکس اون در زیر است

در واقع شما از سرویسی استفاده کرده اید که در Initialize برای اون کلاس مربوطه رو تعریف نکرده اید.

helpsos
چهارشنبه 17 اردیبهشت 1393, 08:27 صبح
در واقع شما از سرویسی استفاده کرده اید که در Initialize برای اون کلاس مربوطه رو تعریف نکرده اید.
سلام
من InitStructureMap(); رو هم در Application_start نوشتم آیا باید کار دیگری انجام بدهم؟

ali_autumnal
چهارشنبه 17 اردیبهشت 1393, 14:46 عصر
شما کلا چندتا سرویس نوشته اید؟

آیا فقط سوریس به نام IPhoneTypeService نوشته اید؟

در صورت پاسخ منفی باید کلیه سرویس ها و کلاس های مورد نظر رو در زیر
x.For<IPhoneTypeService>().Use<EFPhoneTypeService> (); بنویسید.

helpsos
چهارشنبه 17 اردیبهشت 1393, 22:37 عصر
سلام
نه من همه سرویس هام رو نوشتم . ولی نمیدونم مشکلش چیه؟
البته این رو هم بگم که از MVC5 و
<package id="structuremap" version="3.0.0.108" targetFramework="net45" /> <package id="StructureMap.MVC4" version="2.6.4.3" targetFramework="net45" />
<package id="structuremap.web" version="3.0.0.108" targetFramework="net45" />
رو هم نصب کردم.
آیا میتونه به StructureMap.MVC4 ربط داشته باشه؟

ali_autumnal
پنج شنبه 18 اردیبهشت 1393, 10:50 صبح
نه ربطی به MVC5 نداره. احتمال زیاد سرویس ها در ارتباط با کنترلرها مشکل دارند. شاید مقدار دهی نشده اند و...
اگه میتونی پروژه رو بزار چک کنم.

helpsos
پنج شنبه 15 خرداد 1393, 19:20 عصر
زمانی که از این مدل لایه بندی استفاده کردیم حالا برای احراز هویت کاربران اگه از Asp.Net Identity پیش فرض vs2013 استفاده کنیم چطوری باید Context در لایه DataLayer رو بنویسیم و اینکه تکلیف لایه Service برای کار با Identity چی میشه?
من تست کردم ولی به نتیجه نرسیدم حالا کسی راه حلی نداره؟
با تشکر

ali_autumnal
جمعه 23 خرداد 1393, 16:57 عصر
احراز هویت میسپریم به دست ASP و خودش همه کارهای لازم رو تو AccountController انجام میده. (منظور از همه کارها: Login,LogOff,Register) برای UserProfile هم یه سرویس مینویسیم هر دیتایی که لازم داشتیم می خونیم آپدیت می کنیم و...

Context رو هم همون Context اصلی رو بهش معرفی کنه حله.

helpsos
جمعه 23 خرداد 1393, 17:24 عصر
سلام
خیلی ممنون ولی نگرفتم چی شد؟
میشه یکمی بیشتر توضیح بدین؟

aroshanzamir
سه شنبه 18 شهریور 1393, 22:28 عصر
سلام وقت شما بخیر

چرا زیر ObjectFactory خط سبز میکشه می نویسه منسوخ شده
چکار باید بکنم

مرسی

ali_autumnal
دوشنبه 21 مهر 1393, 19:22 عصر
واقعا شرمندم که چک نکرده بودم این چند وقت.

برای اینکه در نسخه جدید Structuremap حذف شده. Structuremap رو از رفرنس حذف کنید و این فایل رو بجای اون Add کنید

assari
جمعه 08 دی 1396, 22:35 عصر
تنظبمات Global.asax


public class MvcApplication : System.Web.HttpApplication
{


protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.F ilters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);


initStructureMap();
}




private static void initStructureMap()
{
ObjectFactory.Container.GetInstance<IUnitOfWork>().ForceDatabaseInitialize();


ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
}




protected void Application_EndRequest(object sender, EventArgs e)
{
HttpContextLifecycle.DisposeAndClearAll();
}
}




public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return ObjectFactory.Container.GetInstance(controllerType ) as Controller;
}
}


}




نمونه ApplicationDataContext

public class DataContext : DbContext, IUnitOfWork {
public DataContext():base("DefaultConnection")
{


}
public DbSet<Product> Product { get; set; }
public DbSet<ProductGroup> ProductGroup { get; set; }


public override int SaveChanges()
{
return base.SaveChanges();
}




public void RejectChanges()
{
foreach (var entry in this.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
entry.State = EntityState.Unchanged;
break;


case EntityState.Added:
entry.State = EntityState.Detached;
break;
}
}
}


public void ForceDatabaseInitialize()
{
Database.Initialize(true);
}


#region IUnitOfWork Members
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
#endregion
}

نمونه ObjectFactoty.cs
public static class ObjectFactory {
private static readonly Lazy<Container> ContainerBuilder =
new Lazy<Container>(DefaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);


public static IContainer Container
{
get { return ContainerBuilder.Value; }
}


private static Container DefaultContainer()
{
var container = new Container(x =>
{
x.For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use(() => new DataContext());
//x.For<DataContext>().HybridHttpOrThreadLocalScoped().Use(context => (DataContext)context.GetInstance<IUnitOfWork>());
//x.For<DbContext>().HybridHttpOrThreadLocalScoped().Use(context => (DataContext)context.GetInstance<IUnitOfWork>());



x.For<IProductService>().Use<ProductService>();
x.For<IProductGroupService>().Use<ProductGroupService>();


});


return container;
}




}