使用存储库模式

时间:2016-06-06 10:23:02

标签: asp.net-mvc entity-framework ninject repository-pattern unit-of-work

我现在有点失落......我从来没有见过关于问题解决方案的这些不同信息。但是,让我们从头开始。

由于Ninject,我正在使用ASP.NET MVC和Repositories注入到控制器中。我有2个简单的实体:Admin,其中包含已创建的博客条目列表和包含一个虚拟Admin字段的条目。

管理

public class Admin
{
    [Key, ScaffoldColumn(false)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Zły login.")]
    [StringLength(20), MinLength(3)]
    [RegularExpression(@"^[a-zA-Z0-9]*$", ErrorMessage = "Special characters are not allowed.")]
    public string Login { get; set; }

    [Required(ErrorMessage = "Złe hasło.")]
    [StringLength(20, MinimumLength = 3)]
    [DataType(DataType.Password)]
    [Display(Name = "Hasło")]
    public string Password { get; set; }

    public virtual List<Entry> CreatedEntries { get; set; } // napisane aktualności przez danego admina
}

项:

public class Entry
{
    [Key, ScaffoldColumn(false)]
    public int Id { get; set; }

    [StringLength(200, MinimumLength = 2)]
    [DataType(DataType.Text)]
    [Display(Name = "Tytuł")]
    public string Title { get; set; }

    [Required, StringLength(2000), MinLength(3)]
    [Display(Name = "Treść")]
    [UIHint("tinymce_jquery_full"), AllowHtml]
    public string Text { get; set; }

    public virtual Admin Admin { get; set; }
}

你可能知道它的发展方向,因为这个问题在stackoverflow上是“经典的”。

在Controller中我想将一个对象绑定到另一个对象:

entry.Admin = repAdmins.GetAdmin(User.Identity.Name);

repEntries.AddEntry(entry);

在存储库中:

public void AddEntry(Entry entry)
    {
        db.Entries.Add(entry);
        db.SaveChanges();
    }

当然我不能这样做,因为着名的“实体对象不能被IEntityChangeTracker的多个实例引用”,这是每个存储库中有单独的数据库上下文的结果。

当我在寻找解决方案时,我已经知道解决它的最佳方法可能是使用一个常见的上下文。然后我发现了工作单元模式。但是现在是真正的问题开始的时候。

  1. 在许多网站上,解决方案有点不同。
  2. 存储库必须具有通用的通用接口(我不想使用它,因为我不需要在每个实体上进行每个CRUD操作,有时我需要有额外的方法,如“IfExists”等。 )
  3. 在一些网站上我读过这个抽象是不需要的,因为抽象已经提供了实体框架,而UoW是用DbContext实现的(无论这意味着什么)
  4. 工作单元模式(至少从互联网上的例子来看)似乎对我来说真的很痛苦......
  5. 我需要一些指导......我只学习了一年的ASP.NET MVC。对我来说,这似乎是“形式超越内容的胜利”。因为... 我只需要将一个对象绑定到另一个对象。我开始认为当我在Controller中只有一个上下文对象并且我不需要时它会更好建造艾菲尔铁塔以实现上述目标:\但我喜欢存储库的想法......

2 个答案:

答案 0 :(得分:4)

我会直接回答这个问题而打开。简单地说,您的存储库应该将上下文作为依赖项(它应该有一个接受类型DbContext的参数的构造函数)。您的上下文应由Ninject管理,然后注入您的存储库和/或控制器。这样,一切都使用相同的上下文。你应该在&#34;请求&#34;范围,以便上下文特定于当前请求。

那就是说,我想点击你的其他一些观点。首先,存储库只是一种访问方法。它真的不应该依赖于实体。您可以使用不打算在特定实体上使用的方法:只是不要使用它们。但是,如果您确实要强制执行此操作,则始终可以使用通用约束和接口。例如,我们假设您不希望在特定实体上提供更新。您可以使用以下接口:

public interface ICreateable
{
}

public interface IUpdateable : ICreateable
{
}

然后,不应更新的实体将仅实现ICreateable,而其他实体(允许更新)将实现IUpdateable(通过接口继承,也实现ICreateable)。最后,您将在存储库方法上添加约束:

public void Create<TEntity>(TEntity entity)
    where TEntity : class, ICreateable

public void Update<TEntity>(TEntity entity>)
    where TEntity : class, IUpdateable

由于有问题的实体仅实施ICreatable,因此无法将其用作Update的类型参数,因此无法使用该方法。

接下来,不使用存储库/ UoW模式与实体框架的建议确实是因为实体框架已经实现了这些模式。存储库模式作为一种在一个地方包含所有数据库查询逻辑(构造SQL语句等)的方式而存在。这就是&#34;抽象&#34;我们在这里谈论。换句话说,不是在应用程序代码中直接构造SQL语句,而是将代码抽象带入存储库。但是,这正是实体框架所做的,这就是为什么您不需要再次执行此操作。工作单元模式作为一种方法来协调多个存储库的工作,允许诸如事务之类的事情。然而,实体框架再次做到了这一切。

添加任何进一步抽象的唯一原因是,如果要抽象实际的提供者,即实体框架本身。例如,您可以使用IRepository之类的界面,然后创建EntityFrameworkRepositoryNHibernateRepositoryWebApiRepository等实现。您的应用程序将仅依赖于{{1}然后,您可以根据需要在不同的实现中进行子操作。如果您不打算这样做,或者您将始终使用实体框架,那么您也可以直接使用您的上下文。任何进一步的抽象只是维护其他东西而对你的应用程序没有任何好处。

最后,是的,工作单元模式对每个人来说都是一种真正的痛苦,而不仅仅是你。这就是为什么我完全放弃它。我使用我称之为&#34;真正通用的存储库&#34;,它利用通用方法和接口来处理我想要抛出的任何实体。这意味着它不仅可以作为存储库,还可以作为工作单元。每个上下文只需要一个实例,并且它与提供程序无关。有关详细信息,请查看我的网站上的article I wrote on the subject

答案 1 :(得分:2)

以下示例显示如何在多个存储库中使用相同的上下文。为了简化它,我没有使用接口,也没有使用容器来注入依赖项。

控制器类:

public class HomeController : Controller
{
    Context context;
    AdminRepository adminRepository;
    EntryRepository entryRepository;

    public HomeController()
    {
        context = new Context();
        adminRepository = new AdminRepository(context);
        entryRepository = new EntryRepository(context);
    }
    // GET: Home
    public ActionResult Index()
    {
        string login = "MyLogin";
        Admin admin = adminRepository.GetAdmin(login);
        Entry entry = new Entry() { Admin = admin};
        entryRepository.AddEntry(entry);
        return View(entry);
    }
}

存储库:

public class AdminRepository
{
    Context context;
    public AdminRepository(Context context)
    {
        this.context = context;

        // This seeds the database
        Admin admin = new Admin() { Login = "MyLogin" };
        this.context.Admins.Add(admin);
        this.context.SaveChanges();
    }

    public Admin GetAdmin(string login)
    {
        return context.Admins.Where(a => a.Login == login).FirstOrDefault();
    }
}

public class EntryRepository
{
    Context context;
    public EntryRepository(Context context)
    {
        this.context = context;
    }

    public void AddEntry(Entry entry){
        context.Entrys.Add(entry);
        context.SaveChanges();
    }
}

上下文类:

public class Context : DbContext
{
    public Context()
    {
        Database.SetInitializer<Context>(new DropCreateDatabaseAlways<Context>());
        Database.Initialize(true);
    }

    public DbSet<Admin> Admins { get; set; }
    public DbSet<Entry> Entrys { get; set; }
}

修改后的模型:

public class Admin
{
    public int Id { get; set; }
    public string Login { get; set; }

}

public class Entry
{
    public int Id { get; set; }
    public virtual Admin Admin { get; set; }
}