Uczyłem się wzorców Repozytorium i Jednostka pracy z różnych źródeł, w tym tutaj:

http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net- mvc-aplikacja

Jeśli przejdziesz do powyższego linku do Tworzenia jednostki pracy, jest to następujące:

    private GenericRepository<Department> departmentRepository;
    private GenericRepository<Course> courseRepository;

To jest w porządku, ale chciałem rozszerzyć klasę Unit of Work, która jest Generic i skonfigurować kolekcję GenericRepositories, aby móc aktualizować następnie dynamicznie w oparciu o model, przez który przechodzę.

Ostatecznie chcę wykonać następujące czynności w moim Kontrole:

public class LedgerUserController : Controller
{
    private GenericUnitOfWork unitOfWork = new GenericUnitOfWork();
    LedgerUser ledgeruser = new LedgerUser();


    public ActionResult Index()
    {
        //var ledgerusers = db.LedgerUsers.Include(l => l.Image).Include(l => l.UserType);
        var view = unitOfWork.Repository(ledgeruser).Get(l => l.LastName == "smith");
        return View(view.ToList());
    }
}

Oto moje dotychczasowe klasy i interfejsy:

irePozytorium.cs

/// <summary>
/// Generic Repository for CRUD Operations and methods
/// to locate entities within your store. This is not specific to which Data Access
/// tools your are using (Direct SQL, EF, NHibernate, etc).
/// </summary>
public interface IRepository<T> where T : class
{
    //--Search Operations
    IQueryable<T> GetAll();
    IEnumerable<T> GetAllList();
    IEnumerable<T> Get(Expression<Func<T,bool>> filter);
    T GetIt(Expression<Func<T, bool>> filter);
    T GetById(object id);


    //--CRUD Operations
    void Create(T entity);
    void Update(T entity);
    void Delete(T entity);

}

generyczne repozytorium.vs

/// /// Klasa repozytorium do szukania encji /// Operacje CRUD /// /// public class GenericRepository : IRepository gdzie TEntity : class {

    internal AccountsContext context;
    internal DbSet<TEntity> dbSet;
    internal IQueryable<TEntity> query;

    /// <summary>
    /// Default Constructor.
    /// </summary>
    /// <param name="context"></param>
    public GenericRepository(AccountsContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    #region Methods
    #region Search Functionality
    /// <summary>
    /// Obtain the whole Entity to query if needed.
    /// </summary>
    /// <returns>IQueryable object.</returns>
    public virtual IQueryable<TEntity> GetAll()
    {
        IQueryable<TEntity> query = dbSet;
        return query;

    }

    /// <summary>
    /// Obtain the whole Entity to Enumerate throught if needed.
    /// </summary>
    /// <returns>IEnumerble object.</returns>
    public virtual IEnumerable<TEntity> GetAllList()
    {
        IQueryable<TEntity> query = dbSet;
        return query.ToList();

    }

    /// <summary>
    /// Locate an Entity by its indexed id.
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual TEntity GetById(object id)
    {
        return dbSet.Find(id);
    }

    /// <summary>
    /// Gets a collection based on LINQ lambda expressions
    /// </summary>
    /// <param name="filter">Lambda Expression</param>
    /// <returns>Query</returns>
    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter)
    {
        query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        return this.query.ToList();
    }

    /// <summary>
    /// Gets one record based on a one-to-one relationship.
    /// </summary>
    /// <param name="filter">Lambda Expression.</param>
    /// <returns>One record.</returns>
    public virtual TEntity GetIt(Expression<Func<TEntity, bool>> filter)
    {
        IQueryable<TEntity> query = dbSet;
        return query.SingleOrDefault(filter);

    }


    #endregion
    #region CRUD Functionality

    /// <summary>
    /// Used to create a new entity into the database.
    /// </summary>
    /// <param name="entity">Entity to create.</param>
    public virtual void Create(TEntity entity)
    {
        dbSet.Add(entity);
    }

    /// <summary>
    /// Used to update an entity that already exists in the
    /// database.
    /// </summary>
    /// <param name="entity">Entity to update.</param>
    public virtual void Update(TEntity entity)
    {
        dbSet.Attach(entity);
        context.Entry(entity).State = EntityState.Modified;
    }

    /// <summary>
    /// Used to delete an entity from the database.
    /// </summary>
    /// <param name="entity">Entity to delete.</param>
    public virtual void Delete(TEntity entity)
    {
        if (context.Entry(entity).State == EntityState.Detached)
        {
            dbSet.Attach(entity);
        }
        dbSet.Remove(entity);
    }

    #endregion
    #endregion

}
#endregion

GenericUnitOfWork.cs:

  /// <summary>
/// Unit of work class that handles multiple Repositories
/// and shares the context.
/// </summary>
public class GenericUnitOfWork : IUnitOfWork

{
    private AccountsContext context = new AccountsContext();

    Dictionary<string, GenericRepository<IRepository<IRepositoryEntity>>> repostories = null;

    /// <summary>
    /// Generic Repository method which checks the repository is available if not,
    /// it sets it up.
    /// </summary>
    /// <param name="entity">Entity</param>
    /// <returns>Repository to use.</returns>
    public  GenericRepository<IRepository<IRepositoryEntity>> Repository (IRepositoryEntity entity)
    {

            string index = entity.GetType().ToString();

            if (!repostories.ContainsKey(index))
            {

                //Reflections to create the repoositiory if it is not needed.
                Type type1 = typeof(GenericRepository<>);
                Type[] typeArgs = {typeof(IRepositoryEntity)};

                Type constructed = type1.MakeGenericType(typeArgs);
                object o = Activator.CreateInstance(constructed);

                if(o is  GenericRepository<IRepository<IRepositoryEntity>>)
                {
                    var rep = (GenericRepository<IRepository<IRepositoryEntity>>)o;
                    rep.context = this.context;
                    repostories.Add(index, rep);  
                }

            }

            return this.repostories[index];
    }

    /// <summary>
    /// Save method.
    /// </summary>
    public void Save()
    {
        context.SaveChanges();

    }

    private bool disposed = false;

    /// <summary>
    /// Dispose the conxtext when finished.
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

}

}

Teraz wiem, że implementacja słownika repozytoriów i metoda Repozytorium są niepoprawne (typy ogólne są błędne), ponieważ w moim wyrażeniu lambda nie można rozpoznać użytkownika księgowego LastName.

var view = unitOfWork.Repository(ledgeruser).Get(l => l.LastName == "smith");

Czy przesadzam z inżynierią problemu, czy też istnieje dobry, czysty sposób na utworzenie ogólnej jednostki pracy z moją kolekcją ogólnych repozytoriów, które są ustawiane i tworzone na podstawie podanego obiektu modelu (LedgerUser na przykład powyżej).

5
garfbradaz 25 wrzesień 2012, 23:47

2 odpowiedzi

Najlepsza odpowiedź

Kilka pomysłów :

  1. Zamiast tworzyć wszystko z odbiciem w implementacji UnitOfWork, możesz użyć kontenera IoC i w ten sposób zautomatyzować tworzenie repozytoriów. Framework MVC jest w rzeczywistości stworzony dla tego rodzaju podejścia. Proste wyjaśnienie i przykład użycia StructureMap do tego celu jest dostępny na http://www.mikesdotnetting.com/Article/117/Dependency-Injection-and-Inversion-of-Control-with-ASP.NET-MVC.

  2. Jeśli nie chcesz używać pełnego kontenera IoC, nadal możesz standaryzować logikę i kod fabryki względem interfejsu. Odgrywa to tę samą rolę, ale może działać lepiej, jeśli chcesz nadal korzystać z niestandardowej implementacji jednostki pracy. W mojej odpowiedzi na C#/EF i wzorzec repozytorium: gdzie umieścić ObjectContext w rozwiązaniu z wieloma repozytoriami?, opublikowałem klasę RepositoryProvider, która umożliwia tworzenie UnitOfWork repozytoria o zakresie z dostosowywanymi fabrykami. Polecam przynajmniej przyjrzeć się temu kodowi, ponieważ jest to podobny, ale bardziej skuteczny sposób na zrobienie tego, do czego dąży twój przykład kodu. Jedną ważną rzeczą do zrozumienia jest to, że przykład w tej odpowiedzi używa ObjectContext jako UnitOfWork, więc Twoja zmiana musiałaby uwzględnić tę rozbieżność, zastępując wystąpienia ObjectContext wystąpieniem IUnitOfWork. Jeśli cokolwiek w tym podejściu jest niejasne po przejrzeniu kodu, daj mi znać, a postaram się wyjaśnić, jak dostosować konkretny przypadek użycia.

  3. Wygląda na to, że w logice twojej fabryki jest coś okrągłego. Jeśli repozytorium tworzy LedgerUsers, nie powinno wymagać LedgerUser do utworzenia fabryki. Wydaje mi się, że to, czego naprawdę chcesz, to parametr Type, taki jak unitOfWork.Repository(typeof(LedgerUser)). Możesz uczynić to bardziej płynnym, tworząc parametr typu ogólnego nad obciążeniem i wykonując unitOfWork.Repository<LedgerUser>()`. Na podstawie twojego przykładu wydaje się, że nie ma żadnego powodu, dla którego potrzebowałbyś instancji.

  4. Wygląda na to, że wolisz mocne pisanie od interfejsu w swojej metodzie Repository. Myślę, że może to, do czego dążysz, jest bardziej podobne do:


    public IRepository Repository()
        where T : IRepositoryEntity 
    { 
           //  your factory/cache-retrieval logic here
    }

Zamiast

public  GenericRepository<IRepository<IRepositoryEntity>> Repository (IRepositoryEntity entity)
{
      //  your factory/cache-retrieval logic here
}

Następnie, jeśli twoje wywołanie miało na celu Repository<LedgerUser>, twoja metoda zwróciłaby GenericRepository<LedgerUser>, nawet jeśli podpis mówi IRepository<LedgerUser>. Tak działa link do implementacji RepositoryProvider, który sugerowałem.

4
Community 23 maj 2017, 15:07

Nie rozumiem, dlaczego chcesz przekazać instancję do obiektu zamiast typu obiektu i przechowywać repozytoria według typu:

Spróbuj tego

ConcurrentDictionary<Type, object> _repositories = ...;

public GenericRepository<IRepository<TEntity>> Repository<TEntity>(IRepositoryEntity entity) where TEntity: IRepositoryEntity
{
    return (GenericRepository<IRepository<TEntity>>)_repositories.GetOrAdd(
        typeof(TEntity), 
        t => new GenericRepository<IRepository<TEntity>>(this.Context)
    );
}
2
esskar 26 wrzesień 2012, 00:06