niedziela, 8 sierpnia 2010

Interfejs dostepu do bazy danych ciąg dalszy...

Jak już wcześniej wspominałem następnym krokiem w implementacji interfejsu związanego z baza danych będzie podstawowe repozytorium. Będzie ono posiadała cześć wspólna wszystkich repozytoriów. Czyli np, operacje CRUD(wstawianie, dodawanie,usuwanie,modyfikowanie), pobieranie po id oraz zwracanie elementów z danego przedziału(stronicowanie elementów). Oto implementacja tej klasy:
public class BaseRepository<TEntity> : IRepository<TEntity>, IDisposable where TEntity : class, IEntity
{
    private DatabaseContext _databaseContext;

    public BaseRepository(IDatabaseFactory databaseFactory)
    {
        Check.Argument.IsNotNull(databaseFactory, "databaseFactory");

        DatabaseFactory = databaseFactory;
    }

    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    protected DatabaseContext DatabaseContext
    {
        get
        {
            return _databaseContext ?? (_databaseContext = DatabaseFactory.Get());
        }
    }

    public void Dispose()
    {
        if (DatabaseContext != null)
            GC.SuppressFinalize(DatabaseContext);

        if (DatabaseFactory != null)
            GC.SuppressFinalize(DatabaseFactory);
    }

    public IEnumerable<TEntity> GetBySpec(ISpecification<TEntity> specification)
    {
        Check.Argument.IsNotNull(specification, "specification");

        IEnumerable<TEntity> result = DatabaseContext.CreateQuery<TEntity>().Where(specification.SatisfiedBy());

        return result;
    }

    public virtual void Add(TEntity entity)
    {
        Check.Argument.IsNotNull(entity, "entity");

        DatabaseContext.Save<TEntity>(entity);
    }

    public virtual void Delete(Guid id)
    {
        Check.Argument.IsNotNull(id, "id");

        TEntity entity = GetById(id);

        if (entity != null)
            DatabaseContext.Delete<TEntity>(entity);
    }

    public virtual void Delete(TEntity entity)
    {
        Check.Argument.IsNotNull(entity, "entity");

        DatabaseContext.Delete<TEntity>(entity);
    }

    public virtual TEntity GetById(Guid id)
    {
        return DatabaseContext.CreateQuery<TEntity>().SingleOrDefault<TEntity>(x => x.Id == id);
    }

    public virtual IEnumerable<TEntity> All()
    {
        return DatabaseContext.CreateQuery<TEntity>();
    }

    public IEnumerable<TEntity> GetPagedElements<S>(int pageIndex, int pageSize, Expression<Func<TEntity, S>> orderExpression, bool ascending)
    {
        Check.Argument.IsNotNegativeOrZero(pageIndex, "pageIndex");
        Check.Argument.IsNotNegative(pageSize, "pageSize");
        Check.Argument.IsNotNull(orderExpression, "orderExpression");

        IQueryable<TEntity> objectQuery = DatabaseContext.CreateQuery<TEntity>();

        return (ascending) ? objectQuery.OrderBy(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList()

                            : objectQuery.OrderByDescending(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList();

    }

    public IEnumerable<TEntity> GetPagedElements<S>(int pageIndex, int pageSize, Expression<Func<TEntity, S>> orderExpression, ISpecification<TEntity> specification, bool ascending)
    {
        Check.Argument.IsNotNegativeOrZero(pageIndex, "pageIndex");
        Check.Argument.IsNotNegative(pageSize, "pageSize");
        Check.Argument.IsNotNull(orderExpression, "orderExpression");
        Check.Argument.IsNotNull(specification, "specification");

        IQueryable<TEntity> objectQuery = DatabaseContext.CreateQuery<TEntity>();

        return (ascending) ? objectQuery.Where(specification.SatisfiedBy())
                                        .OrderBy(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList()

                            : objectQuery.Where(specification.SatisfiedBy())
                                        .OrderByDescending(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList();

    }
}
Jak widac wiekszosc metod tutaj to proste operacje na bazie danych za pomoca LINQ.
Oprocz tego jest metoda ktora za pomoca wczesniej wspomnianej specyfikacji wyciaga dane z bazy. Teraz przyklad prostego repozytorium uzytkownika:
public class UserRepository : BaseRepository<User>, IUserRepository
{
    public UserRepository(IDatabaseFactory dbFactory)
        : base(dbFactory)
    {

    }

    public int CountAllUsers()
    {
        return DatabaseContext.Users.Count();
    }

    public int CountNumberOfOnlineUsers()
    {
        UserOnlineUsersSpecification specification = new UserOnlineUsersSpecification();

        return this.GetBySpec(specification).Count<User>();
    }

    public IEnumerable<User> GetAllUsersOnline()
    {
        UserOnlineUsersSpecification specification = new UserOnlineUsersSpecification();

        return this.GetBySpec(specification);
    }

    public User FindUserByUsername(string username)
    {
        Check.Argument.IsNotEmpty(username, "username");

        UserUsernameSpecification specification = new UserUsernameSpecification(username);

        return this.GetBySpec(specification).SingleOrDefault<User>();
    }


    public User FindUserByEmail(string email)
    {
        Check.Argument.IsNotEmpty(email, "email");
        Check.Argument.IsNotInvalidEmail(email, "email");

        UserEmailSpecification specification = new UserEmailSpecification(email);

        return this.GetBySpec(specification).SingleOrDefault<User>();
    }
}
W tym przykładzie możemy zobaczyć wykorzystanie już tych tajemniczych specyfikacji. Jak widać to rozwiązanie dosyć fajnie się prezentuje i oddziela logikę od samej bazy danych. W dodatku można ich wielokrotnie używać w rożnych miejscach naszego repozytorium. Dodałem 3 rodzaje specyfikacji do projektu związane z użytkownikiem. Wiec można sobie tez oglądnąć. To repozytorium ich właśnie używa.

W zasadzie cały interfejs dostępu do bazy danych już jest prawie gotowy. Możliwe są ewentualnie małe poprawki. W projekcie testów NHibernate dodałem testy związane z klasami powyżej.

Brak komentarzy:

Prześlij komentarz