【问题标题】:Entity Framework with Repository Pattern insert many-to-many具有存储库模式的实体框架插入多对多
【发布时间】:2015-06-24 13:39:47
【问题描述】:

我继承了一个使用带有存储库模式的实体框架的解决方案。 在这个解决方案中,以前的开发人员忘记了实现多对多关系,所以现在我必须这样做。

我对 EF 或模式都不太熟悉,所以我无法得到我想要完成的实际工作,即插入多对多关系。 我可以让 EF 在数据库中创建关系表,但不知何故我无法插入。

我见过other questions similar to this one,但没有一个完全符合这里的模式实现方式,然后我对一切都不熟悉,无法理解它。

有人可以看看代码,看看我缺少什么吗? (代码已经简化了一点,这不是实际的学生/课程项目,但我使用的名称与之前示例中使用的名称相同)

这是我的代码。非常简化,没有数千个接口。 这运行得很好,没有例外。 当我在“Main”类中调试和快速查看studentUow 时,Students.Courses 集合确实包含一个值,但它从未保存到数据库中。此外,它只包含一个值,即使它应该包含几门课程。

实体类

public class Student { // This class already existed
    public int StudentId { get; protected set; }
    public virtual ICollection<Course> Courses { get; set; } // I added this property
}

public class Course { // This class already existed
    public int CourseId { get; protected set; }
    public virtual ICollection<Student> Students { get; set; } // I added this property
}

存储库

public class StudentRepository {
    protected DbContext DbContext { get; private set; }
    protected DbSet<Student> DbSet { get; private set; }
    public StudentRepository(DbContext dbContext) {
        DbContext = dbContext;
        DbSet = dbContext.Set<Student>();
    }

    public virtual void AddOrUpdate(Student entity) {
        if (Exists(entity)) {
            Update(entity);
        } else {
            Add(entity);
        }
    }

    public virtual void Update(Student entity) {
        var dbEntityEntry = DbContext.Entry(entity);
        if (dbEntityEntry.State == EntityState.Detached) {
            DbSet.Attach(entity);
        }
        dbEntityEntry.State = EntityState.Modified;
    }

    public virtual Student Add(Student entity) {
        var dbEntityEntry = DbContext.Entry(entity);
        if (dbEntityEntry.State != EntityState.Detached) {
            dbEntityEntry.State = EntityState.Added;
        } else {
            return DbSet.Add(entity);
        }
        return null;
    }

    public IQueryable<Student> Queryable() {
        return DbSet;
    }

    public bool Exists(Student entity) {
        var objContext = ((IObjectContextAdapter)DbContext).ObjectContext;
        object existingEntity;
        var exists = objContext.TryGetObjectByKey(GetEntityKey(entity), out existingEntity);
        if (exists) objContext.Detach(existingEntity);

        return exists;
    }

    private EntityKey GetEntityKey(Student entity) {
        var objContext = ((IObjectContextAdapter)DbContext).ObjectContext;
        var objSet = objContext.CreateObjectSet<T>();
        var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);
        return entityKey;
    }
}

工作单元

public class StudentUow : UnitOfWork<MyDbContext> {
    public StudentRepository Students { get { return CreateRepository<StudentRepository>(); } }
    public CourseRepository Courses { get { return CreateRepository<CourseRepository>(); } }
}

public class UnitOfWork<TContext> where TContext : System.Data.Entity.DbContext {
    private readonly TContext _dbContext;
    private IRepositoryProvider _repositoryProvider;

    protected UnitOfWork(IRepositoryProvider provider) {
        _repositoryProvider = provider;
    }

    protected TRepository CreateRepository<TRepository>() {
        return _repositoryProvider.Create<TRepository>(_dbContext, "default");
    }

    public void Commit() {
        _dbContext.SaveChanges();
    }
}

[Export(typeof(IUnitOfWorkProvider))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class UnitOfWorkProvider {
    [Import] private IRepositoryProvider _repositoryProvider;

    public StudentUow GetStudentUow() {
        return new StudentUow(_repositoryProvider);
    }
}

[Export(typeof(IRepositoryProvider))]
public class MyRepositoryProvider {
    public MyRepositoryProvider() {
        Register<RepositoryFactory<IProductRepository, StudentRepository>>();
    }

    public TRepository Create<TRepository>(DbContext dbContext, string conntextKey)
    {
        var type = typeof (TRepository);
        if (!_factories.ContainsKey(type))
            throw new UnknownFactoryException(type);

        return (TRepository)_factories[type].Create(dbContext);
    }

    public void Register<TRepositoryFactory>() where TRepositoryFactory : IRepositoryFactory, new()
    {
        var factory = new TRepositoryFactory();
        if (_factories.ContainsKey(factory.Type)) throw new FactoryTypeAlreadyRegisteredException(factory.Type);
        _factories[factory.Type] = factory;
    }
}

“主”类

public class MyClass {
    public AddCourse(int studentId, List<int> courses) {
        using (var studentUow = new StudentUow()) {
            foreach (int courseId in courses) {
                Student s = studentUow.Student.Queryable().First(x => x.StudentId == studentId);
                Course c = studentUow.Course.Queryable().First(x => x.CourseId == courseId);
                s.Courses.Add(c);

                studentUow.Student.AddOrUpdate(s);
            }
            studentUow.Commit();
        }
    }
}

如果您缺少某些功能,请发表评论,我会添加它,或者让您知道它在哪里。

【问题讨论】:

  • 实际项目充满了大量的 UOW、存储库、接口,并使用一些 MEF 来实例化各种东西。我什至不知道从哪里开始创建一个混乱的 SSCCE。
  • 你能发布 CreateRepository 的代码吗?我最初的想法是每个存储库都在创建一个新的 DbContext,因此两者之间没有关系。因此,当您在这两个上使用 Queryable 方法时,它们仍然是代理对象,因此创建多对多将不起作用。确保 DbContext 引用相同的对象,或者在两个 Queryable 上尝试 ToList,这样它们就不是代理对象。
  • @ManOVision 代码已更新。如您所见,其中一些东西是由 MEF 实例化的。
  • MyDbContext 的生命周期是什么。每次都实例化一个新的,还是单例?无论哪种方式都可能在代码的不同区域带来问题。我建议打破存储库模式,让您的“主”类直接与 MyDbContext 交互。这样您就可以消除对 EF 或其他地方的问题的疑虑。如果您的多对多对象添加得很好,您可能需要挖掘大量代码。如果没有添加,可能是简单的 MyDbContext 代码更改或数据库更改。

标签: c# .net entity-framework repository-pattern


【解决方案1】:

默认情况下,EF 不包括查询中的相关实体。为此,您需要在需要时手动添加课程。还有一个问题是,在每次迭代中,您都会一次又一次地获取学生,因此课程集合会丢失。这应该有效:

using System.Data.Entity;
...
using (var studentUow = new StudentUow()) {
    Student s = studentUow.Student.Queryable().Include(x => x.Courses).First(x => x.StudentId == studentId);
    foreach (int courseId in courses) {
        Course c = studentUow.Course.Queryable().First(x => x.CourseId == courseId);
        s.Courses.Add(c);
        c.Students.Add(s);

        studentUow.Course.Update(c);
    }
    studentUow.Student.Update(s);
    studentUow.Commit();
}

如果可能,我强烈建议重构您的代码。

【讨论】:

  • 我试过这个,但仍然没有任何东西插入到关系表中。也许重构一切是要走的路。关于使用最好是模块化的良好模式的任何提示?
  • 首先尝试直接使用DbContext,看看是否有效。然后我会通过 DI 将 DbContext 实例传递给存储库。 UoW 也将具有 DbContext 实例,因此 Commit 调用 DbContext.SaveChanges()。您还需要在 IoC 容器中指定每个请求的 DbContext 生命周期。
猜你喜欢
  • 2012-11-27
  • 2018-03-05
  • 2011-07-30
  • 1970-01-01
  • 2012-09-02
  • 1970-01-01
  • 2011-06-02
  • 1970-01-01
  • 2011-06-10
相关资源
最近更新 更多