3

私は孤独な開発者であり、Pluralsightは、私が学んでいるリポジトリやIoCなどのことを理解するのに役立つ私の救いです。しかし、私はそのパターンを使いにくいと感じています。

だから私はUnityIoCを使用していて、次ModelContainerのクラスです。

public static class ModelContainer
{
    private static IUnityContainer instance;

    static ModelContainer()
    {
        instance = new UnityContainer();
    }

    public static IUnityContainer Instance
    {
        get
        {
            instance.RegisterType<ISCD_CallDiaryRepository, SCD_CallDiaryRepository>(new HierarchicalLifetimeManager());
            instance.RegisterType<ICompanyRepository, CompanyRepository>(new HierarchicalLifetimeManager());
            instance.RegisterType<ISubcontractorRepository, SubcontractorRepository>(new HierarchicalLifetimeManager());
            instance.RegisterType<ITradeRepository, TradeRepository>(new HierarchicalLifetimeManager());
            instance.RegisterType<IEmployeeRepository, EmployeeRepository>(new HierarchicalLifetimeManager());
            instance.RegisterType<ISubcontractorTradeRepository, SubcontractorTradeRepository>(new HierarchicalLifetimeManager());
            instance.RegisterType<ICountyRepository, CountyRepository>(new HierarchicalLifetimeManager());
            instance.RegisterType<IAddressLineRepository, AddressLineRepository>(new HierarchicalLifetimeManager());
            return instance;
        }
    }
}

コントローラのコンストラクタでリポジトリクラスをインスタンス化できます。

public SubcontractorController(
        ISubcontractorRepository subcontractorRepository,
        ISubcontractorTradeRepository subcontractorTradeRepository, 
        ICompanyRepository companyRepository, 
        ISCD_CallDiaryRepository scdCallDiaryRepository,
        ITradeRepository tradeRepository,
        IAddressLineRepository addressLineRepository)
    {
        this.SubcontractorRepository = subcontractorRepository;
        this.SubcontractorTradeRepository = subcontractorTradeRepository;
        this.CompanyRepository = companyRepository;
        this.ScdCallDiaryRepository = scdCallDiaryRepository;
        this.TradeRepository = tradeRepository;
        this.AddressLineRepository = addressLineRepository;
    }

この設定で厄介なのは、これらのリポジトリクラスでCRUD機能を使用する場合は、コントローラーで機能を実行するか、パラメーターとして作成したリポジトリインスタンスをモデルクラスメソッドに渡す必要があることです。私は後者を選択します。コントローラーは多くのことを行うことを意図していませんが、リポジトリーパラメーターを渡し続ける必要があるのは私には正しくないようです。

私は何が欠けていますか?

4

3 に答える 3

5

MVCアプリをいくつかの異なるプロジェクトに分割します。

  • AppName.Configuration:アプリの構成を処理します(つまり、web.config / app設定を取得するなど)
  • AppName.Data:これはすべてのDBアクセスが実行されるデータ層です(ビジネスロジックはありません)。DBML / EDMXはここにあり、私のリポジトリクラスもここにあります。
  • AppName.Models:これは、すべてのViewModelがMVCに対して定義されている場所であり、アプリケーション全体で必要な他のモデルオブジェクトも同様です。
  • AppName.Services:これは私のビジネスレイヤーです。データレイヤーまたはプレゼンテーションレイヤーに到達するには、すべてがここを通過する必要があります。ViewModelsはデータベースオブジェクトから構築され、データ検証はここで行われます。
  • AppName.Web:これはMVCアプリケーションになります。AppName.Data.Test:データアプリの単体テスト
  • AppName.Services.Test:サービスの単体テスト
  • AppName.Web.Test:MVCコントローラーの単体テスト
  • AppName.Web.UI.Test:Webユーザーインターフェイスの単体テスト(WATINを使用)

また、NuGetパッケージにパッケージ化された一連のクラスがあり、必要に応じて(この例では)アプリに追加できます。

  • CompanyName.Data:データレイヤーロジックの共通ライブラリ
  • CompanyName.MVC:ASP.NETMVC統合用の共通ライブラリ
  • CompanyName.Utilities:その他のユーティリティ用の共通ライブラリ

私のコントローラーは、サービスレイヤーからビューモデルを取得してビューに送信し、ビューからの投稿時にデータを受信して​​サービスレイヤーに送信し、検証してリポジトリに保存する以外は何もしません。

基本的な例を次に示します。

これは、この例で使用されるビューモデルです。

public class CreateFocusViewModel
{
    public int CareerPlanningFormID { get; set; }

    public int PerformanceYear { get; set; }

    public IList<FocusModel> Focuses { get; set; }

    public string ResultsMeasuresFocusComments { get; set; }

    public byte MaximumFocusesAllowed { get; set; }
}

public class FocusModel
{
    public int FocusID { get; set; }

    public string FocusText { get; set; }

    public bool IsPendingDeletion { get; set; }
}

GETおよびPOSTアクションメソッドを使用したサンプルコントローラー:

public class CPFController : Controller
{
    private readonly ICareerPlanningFormService careerPlanningFormService;

    public CPFController(ICareerPlanningFormService careerPlanningFormService)
    {
        this.careerPlanningFormService = careerPlanningFormService;
    }

    [HttpGet]
    public ViewResult CreateFocus(int careerPlanningFormID)
    {
        var model = this.careerPlanningFormService.BuildCreateFocusViewModel(careerPlanningFormID);
        return this.View(model);
    }

    [HttpPost]
    public ActionResult CreateFocus(int careerPlanningFormID, string button)
    {
        var model = this.careerPlanningFormService.BuildCreateFocusViewModel(careerPlanningFormID);
        this.TryUpdateModel(model);

        switch (button)
        {
            case ButtonSubmitValues.Next:
            case ButtonSubmitValues.Save:
            case ButtonSubmitValues.SaveAndClose:
                {
                    if (this.ModelState.IsValid)
                    {
                        try
                        {
                            this.careerPlanningFormService.SaveFocusData(model);
                        }
                        catch (ModelStateException<CreateFocusViewModel> mse)
                        {
                            mse.ApplyTo(this.ModelState);
                        }
                    }

                    if (!this.ModelState.IsValid)
                    {
                        this.ShowErrorMessage(Resources.ErrorMsg_WEB_ValidationSummaryTitle);
                        return this.View(model);
                    }

                    break;
                }

            default:
                throw new InvalidOperationException(string.Format(Resources.ErrorMsg_WEB_InvalidButton, button));
        }

        switch (button)
        {
            case ButtonSubmitValues.Next:
                return this.RedirectToActionFor<CPFController>(c => c.SelectCompetencies(model.CareerPlanningFormID));

            case ButtonSubmitValues.Save:
                this.ShowSuccessMessage(Resources.Msg_WEB_NotifyBarSuccessGeneral);
                return this.RedirectToActionFor<CPFController>(c => c.CreateFocus(model.CareerPlanningFormID));

            case ButtonSubmitValues.SaveAndClose:
            default:
                return this.RedirectToActionFor<UtilityController>(c => c.CloseWindow());
        }
    }
}

ViewModelが構築され、データが検証/保存されるサービスレイヤー:

public class CareerPlanningFormService : ICareerPlanningFormService
{
    private readonly IAppNameRepository repository;
    private readonly IPrincipal currentUser;

    public CareerPlanningFormService(IAppNameRepository repository, IPrincipal currentUser)
    {
        this.repository = repository;
        this.currentUser = currentUser;
    }

    public CreateFocusViewModel BuildCreateFocusViewModel(int careerPlanningFormID)
    {
        var cpf = this.repository.GetCareerPlanningFormByID(careerPlanningFormID);

        // create the model using cpf
        var model = new CreateFocusViewModel
        {
            CareerPlanningFormID = cpf.CareerPlanningFormID,
            PerformanceYear = cpf.PerformanceYearID,
            ResultsMeasuresFocusComments = cpf.ResultsMeasuresFocusComments,
            MaximumFocusesAllowed = cpf.PerformanceYear.MaximumCareerPlanningFormFocusesAllowed
            // etc., etc...
        };

        return model;
    }

    public void SaveFocusData(CreateFocusViewModel model)
    {
        // validate the model
        this.ValidateCreateFocusViewModel(model);

        // get the current state of the CPF
        var cpf = this.repository.GetCareerPlanningFormByID(model.CareerPlanningFormID);

        // bunch of code saving focus data here...

        // update the ResultsMeasuresFocusComments
        cpf.ResultsMeasuresFocusComments = string.IsNullOrWhiteSpace(model.ResultsMeasuresFocusComments) ? null : model.ResultsMeasuresFocusComments.Trim();

        // commit the changes
        this.repository.Commit();
    }

    private void ValidateCreateFocusViewModel(CreateFocusViewModel model)
    {
        var errors = new ModelStateException<CreateFocusViewModel>();

        {
            var focusesNotPendingDeletion = model.Focuses.Where(f => f.IsPendingDeletion == false);

            // verify that at least one of the focuses (not pending deletion) has a value
            {
                var validFocuses = focusesNotPendingDeletion.Where(f => !string.IsNullOrWhiteSpace(f.FocusText)).ToList();
                if (!validFocuses.Any())
                {
                    var index = model.Focuses.IndexOf(model.Focuses.Where(f => f.IsPendingDeletion == false).First());
                    errors.AddPropertyError(m => m.Focuses[index].FocusText, Resources.ErrorMsg_CPF_OneFocusRequired);
                }
            }

            // verify that each of the focuses (not pending deletion) length is <= 100
            {
                var focusesTooLong = focusesNotPendingDeletion.Where(f => f.FocusText != null && f.FocusText.Length > 100).ToList();
                if (focusesTooLong.Any())
                {
                    focusesTooLong.ToList().ForEach(f =>
                    {
                        var index = model.Focuses.IndexOf(f);
                        errors.AddPropertyError(m => m.Focuses[index].FocusText, Resources.ErrorMsg_CPF_FocusMaxLength);
                    });
                }
            }
        }

        errors.CheckAndThrow();
    }
}

リポジトリクラス:

public class AppNameRepository : QueryRepository, IAppNameRepository
{
    public AppNameRepository(IGenericRepository repository)
        : base(repository)
    {
    }

    public CareerPlanningForm GetCareerPlanningFormByID(int careerPlanningFormID)
    {
        return this.Repository.Get<CareerPlanningForm>().Where(cpf => cpf.CareerPlanningFormID == careerPlanningFormID).Single();
    }
}

リポジトリインターフェース:

public interface IAppNameRepository : IRepository
{
    CareerPlanningForm GetCareerPlanningFormByID(int careerPlanningFormID);
}

CompanyName.Data共通ライブラリのクラス:

public abstract class QueryRepository : IRepository
{
    protected readonly IGenericRepository Repository;

    protected QueryRepository(IGenericRepository repository)
    {
        this.Repository = repository;
    }

    public void Remove<T>(T item) where T : class
    {
        this.Repository.Remove(item);
    }

    public void Add<T>(T item) where T : class
    {
        this.Repository.Add(item);
    }

    public void Commit()
    {
        this.Repository.Commit();
    }

    public void Refresh(object entity)
    {
        this.Repository.Refresh(entity);
    }
}

public interface IGenericRepository : IRepository
{
    IQueryable<T> Get<T>() where T : class;
}

public interface IRepository
{
    void Remove<T>(T item) where T : class;
    void Add<T>(T item) where T : class;
    void Commit();
    void Refresh(object entity);
}

LinqToSQLとEFの両方があります。LinqToSQLのセットアップは次のとおりです。

internal sealed class LinqToSqlRepository : IGenericRepository
{
    private readonly DataContext dc;

    public LinqToSqlRepository(DataContext dc)
    {
        this.dc = dc;
    }

    public IQueryable<T> Get<T>() where T : class
    {
        return this.dc.GetTable<T>();
    }

    public void Remove<T>(T item) where T : class
    {
        this.dc.GetTable<T>().DeleteOnSubmit(item);
    }

    public void Add<T>(T item) where T : class
    {
        this.dc.GetTable<T>().InsertOnSubmit(item);
    }

    public void Commit()
    {
        this.dc.SubmitChanges();
    }

    public void Refresh(object entity)
    {
        this.dc.Refresh(RefreshMode.OverwriteCurrentValues, entity);
    }
}

これは、CompanyName.Data共通ライブラリにもあります。LinqToSQLまたはEntityFrameworkを登録するメソッドがあります

public static class UnityContainerExtensions
{
    public static IUnityContainer RegisterEntityFrameworkClasses<TDbContext>(this IUnityContainer container, string nameOrConnectionString) where TDbContext : DbContext
    {
        var constructor = typeof(TDbContext).GetConstructor(new Type[] { typeof(string) });
        container.RegisterType<DbContext>(new HierarchicalLifetimeManager(), new InjectionFactory(c => constructor.Invoke(new object[] { nameOrConnectionString })));
        container.RegisterType<IGenericRepository, EntityFrameworkRepository>();
        return container;
    }

    public static IUnityContainer RegisterLinqToSqlClasses<TDataContext>(this IUnityContainer container, string connectionString) where TDataContext : DataContext
    {
        var constructor = typeof(TDataContext).GetConstructor(new Type[] { typeof(string) });
        container.RegisterType<DataContext>(new HierarchicalLifetimeManager(), new InjectionFactory(c => constructor.Invoke(new object[] { connectionString })));
        container.RegisterType<IGenericRepository, LinqToSqlRepository>();
        return container;
    }
}

CompanyName.Utilitiesライブラリ:

public interface IUnityBootstrap
{
    IUnityContainer Configure(IUnityContainer container);
}

AppName.DataでのUnityブートストラップ

public class UnityBootstrap : IUnityBootstrap
{
    public IUnityContainer Configure(IUnityContainer container)
    {
        var config = container.Resolve<IAppNameConfiguration>();

        return container.RegisterLinqToSqlClasses<AppNameDataContext>(config.AppNameConnectionString)
                        .RegisterType<IAppNameRepository, AppNameRepository>();
    }
}

AppName.ServicesでのUnityブートストラップ

public class UnityBootstrap : IUnityBootstrap
{
    public IUnityContainer Configure(IUnityContainer container)
    {
        new CompanyName.Security.UnityBootstrap().Configure(container);
        new AppName.Data.UnityBootstrap().Configure(container);

        container.RegisterSecureServices<AuthorizationRulesEngine>(typeof(UnityBootstrap).Assembly);

        return container.RegisterType<ICareerPlanningFormService, CareerPlanningFormService>()
                        .RegisterType<IStaffService, StaffService>();
    }
}

AppName.WebでのUnityブートストラップ

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Standard MVC setup
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);

        // Application configuration 
        var container = new UnityContainer();
        new CompanyName.Mvc.UnityBootstrap().Configure(container);
        new AppName.Configuration.UnityBootstrap().Configure(container);
        new AppName.Data.UnityBootstrap().Configure(container);
        new AppName.Services.UnityBootstrap().Configure(container);

        // Default MVC model binder is pretty weak with collections
        ModelBinders.Binders.DefaultBinder = new DefaultGraphModelBinder();
    }

    protected void Application_Error()
    {
        HttpApplicationEventHandler.OnError(this.Context);
    }

    protected void Application_EndRequest()
    {
        HttpApplicationEventHandler.OnEndRequest(this.Context);
    }
}
于 2013-01-22T11:31:38.480 に答える
5

提供されたサンプルには、 6つの依存関係SubcontractorControllerを持つコントローラーがあります。これは通常、コントローラーが一度に多くのことを実行しようとし、単一責任の原則に違反する可能性があることを意味します。状況を改善するには、少なくとも3つの方法があります。

  • リポジトリコントローラーに挿入し、リポジトリインスタンスをパラメーターとしてモデルクラスメソッドに渡す代わりに、Aggregate Servicesにリファクタリングすることができます。つまり、モデルメソッドと依存関係の両方を単一のエンティティに結合できます。これにより、コンストラクターの過剰注入も解決されます。
  • リポジトリのファクトリをコントローラに注入し、後でこのファクトリを使用して依存関係を解決します
  • モデル自体を注入し、リポジトリをそれらの依存関係にする。
于 2013-01-22T09:29:41.630 に答える
1

私はキャリアの早い段階で後者も試しました。うまくいきませんでした。予期しないパフォーマンスの低下や大きなモデルオブジェクトが発生します。コントローラーは、あなたが言及するロジックに最適な場所だと思います。同じクラスのUIとビジネスの懸念が心配な場合は、ビジネスをサービスクラスに移動します。

于 2013-01-22T08:10:17.577 に答える