3
\$\begingroup\$

Continuation of this question: Unit of Work with Generic Repository Pattern MVVM, vol. 3

I have made some changes.

  • The methods GetFeedBySomething() have been replaced by Find()
  • GetRepository() has been added to UoW
  • I separated DAL from Model
  • Model inherits IBaseEntity from DAL (not sure is it correct)
  • IRepositoryWithId made TEntity to become IBaseEntity because I need to have Id property. (again not sure is it correct, maybe shall I use reflection?)

This is my code map: Code Map

And here are my code

DAL CLASSES

IRepository

public interface IRepository<TEntity> where TEntity : class
{
    TEntity Add(TEntity entity);
    TEntity Update(TEntity entity);

    IQueryable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate);
    IEnumerable<TEntity> GetAll();

    void Remove(TEntity entity);
}

IRepositoryWithId

public interface IRepositoryWithId<T> : IRepository<T> where T : class, IBaseEntity
{
    int Count { get; }

    T GetById(int feedId);

    void RemoveById(int feedId);
}

IBaseEntity

public interface IBaseEntity
{
    int Id { get; set; }
}

IFeedRepository

internal class FeedRepository<T> : IRepositoryWithId<T>
    where T : class, IBaseEntity, new()
{
    private readonly SQLiteConnection _dbConnection;
    protected Lazy<IList<T>> _feeds;

    public int Count
    {
        get
        {
            if (this._feeds.IsValueCreated)
                return this._feeds.Value.Count;

            return this._dbConnection.Table<T>().Count();
        }
    }

    public FeedRepository(SQLiteConnection dbConnection)
    {
        this._dbConnection = dbConnection;
        this._feeds = new Lazy<IList<T>>(() => this._dbConnection.Table<T>().ToList());
    }

    public T Add(T feed)
    {
        if (this._feeds.IsValueCreated)
            this._feeds.Value.Add(feed);

        this._dbConnection.Insert(feed);
        return feed;
    }

    public T GetById(int feedId)
    {
        return this._feeds.Value.Where(feed => int.Equals(feed.Id, feedId)).SingleOrDefault();
        //return this._feeds.Value.Where(feed => int.Equals(typeof(T).GetRuntimeProperty("Id").GetValue(feed), feedId)).SingleOrDefault();
    }

    public T Update(T feed)
    {
        int indexOfFeed = this.GetIndexOfFeed(feed);
        if (indexOfFeed != -1)
            this._feeds.Value[indexOfFeed] = feed;

        this._dbConnection.Update(feed);

        return feed;
    }

    public IEnumerable<T> GetAll()
    {
        return this._feeds.Value;
    }

    public IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {
        if (predicate != null)
            return this._feeds.Value.AsQueryable().Where(predicate);

        return this._feeds.Value.AsQueryable();
    }

    public void Remove(T feed)
    {
        int indexOfFeed = this.GetIndexOfFeed(feed);
        if (indexOfFeed != -1)
            this._feeds.Value.RemoveAt(indexOfFeed);

        this._dbConnection.Delete(feed);
    }

    public void RemoveById(int feedId)
    {
        T feed = this.GetById(feedId);

        if (feed != null)
            this.Remove(this.GetById(feedId));
    }

    private int GetIndexOfFeed(T feed)
    {
        if (this._feeds.IsValueCreated)
        {
            //int id = (int)typeof(T).GetRuntimeProperty("Id").GetValue(feed);
            int indexOfFeed = this._feeds.Value.IndexOf(this.GetById(feed.Id));

            return indexOfFeed;
        }

        return -1;
    }
}

IUnitOfWork

internal interface IUnitOfWork
{
    bool IsInTransaction { get; }

    IRepositoryWithId<TEntity> GetFeedRepository<TEntity>() where TEntity : class, IBaseEntity, new();

    void Commit();
    void Rollback();
}

UnitOfWork

public class UnitOfWork : IUnitOfWork
{
    private readonly SQLiteConnection _dbConnection;
    private readonly Dictionary<Type, object> _repositories;

    public bool IsInTransaction
    {
        get { return this._dbConnection.IsInTransaction; }
    }

    public UnitOfWork(SQLiteConnection dbConnection)
    {
        this._repositories = new Dictionary<Type, object>();
        this._dbConnection = dbConnection;
        this._dbConnection.BeginTransaction();
    }

    public IRepositoryWithId<TEntity> GetFeedRepository<TEntity>()
        where TEntity : class, IBaseEntity, new()
    {
        if (this._repositories.Keys.Contains(typeof(TEntity)))
            return this._repositories[typeof(TEntity)] as IRepositoryWithId<TEntity>;

        var repository = new FeedRepository<TEntity>(this._dbConnection);

        this._repositories.Add(typeof(TEntity), repository);

        return repository;
    }

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

    public void Rollback()
    {
        this._dbConnection.Rollback();
    }
}

MODEL CLASSES

IFeed

public interface IFeed : IBaseEntity
{
    string Title { get; set; }
    Uri Link { get; set; }
    DateTime PubDate { get; set; }
}

FeedData and FeedItem

public sealed class FeedData : IFeed, INotifyPropertyChanged { }
public sealed class FeedItem : IFeed, INotifyPropertyChanged { }

And now I have some questions about:

  • IBaseEntity is it necessary or shall I use reflection
\$\endgroup\$
5
\$\begingroup\$

Use IBaseEntity, reflection should never be used unless you really need to.

I'm not a fan of returning a not executed IQueryable<> as you do in your Find method, you should consider executing the query and returning the IEnumerable<>, otherwise you expose yourself to potential bugs (If the client of the method messes up with the query) and it is harder to debug after since you might not know when is your query executed.

You should consider commenting your code, one day you will be happy you did it!

I'm not sure why your generic repository is named FeedRepository, maybe it's because it has a meaning I don't understand in english but you might want to rename it in a more generic way (Say... SqLiteRepository since it is an implementation of your interface for an SqlLite data access).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.