Repository Pattern is to create a single channel to access data by gathering the needs on an interface. The aim is to separate Bussines Logic and the Data Layer from each other.

Repository in Unity3D

There are many areas where we can put data in Unity3D, for example temp data in the most commonly used memory, PlayerPrefs, TextAssets, LogFile, SqlLite. Repository is a very abstract layer, manipulated data can be remote, not local. From this point of view, what we have told is graphically shown below:

Repository UML

As can be seen, all operations can be performed with an interface where all operations are combined.

Definition Repository

Adding, deleting, updating and selecting are the basic operations that the Repository will do on the data.

public interface IRepository<T> where T:class,new()
{
    void Insert(T instance);
    void Delete(T instance);
    void Update(T instance);
    IEnumerable<T> Select(Func<T,bool> func );
}

There is a hidden problem here, a preferred problem. Some data in Unity3D is only Read-Only, so updating and deletion cannot be done. In this case, we can separate the operations into interfaces and have fully applied the SOLID principles, but this makes the code difficult to read. Here we chose to go this way.
The developer can use different access methods for different data sources, it seems obvious. Below you can see the implementation of Repository for common data sources:

public class UnityResourcesRepository<T> : IRepository<T> where T : class, new()
{
    //......
    public IEnumerable<T> Select(Func<T, bool> func)
    {
        List<T> items = new List<T>();
        try
        {
            TextAsset[] textAssets = Resources.LoadAll<TextAsset>(DataDirectory);
            for (int i = 0; i < textAssets.Length; i++)
            {
                TextAsset textAsset = textAssets[i];
                T item = Serializer.Deserialize<T>(textAsset.text);
                items.Add(item);
            }
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
        return items.Where(func);
    }
}

The above Repo is made for accessing TextAsset as UnityResource.

public class PlayerPrefsRepository<T> : IRepository<T> where T : class, new()
{
    //......
    public void Insert(T instance)
    {
        try
        {
            string serializedObject = Serializer.Serialize<T>(instance, true);
            PlayerPrefs.SetString(KeysIndexName, serializedObject);
          
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
    }
}

Repo for Unity’s PlayerPrefs datasource.

public class FileSystemRepository<T> : IRepository<T> where T:class,new()
{        
    //......
    public void Insert(T instance)
    {
        try
        {
            string filename = GetFilename(Guid.NewGuid());
            if (File.Exists(filename))
            {
                throw new Exception("Attempting to insert an object which already exists. Filename=" + filename);
            }

            string serializedObject = Serializer.Serialize<T>(instance, true);
            using (StreamWriter stream = new StreamWriter(filename))
            {
                stream.Write(serializedObject);
            }
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
    }
}

The FileSystem repository was made to read and write log files.

public class MemoryRepository<T> : IRepository<T> where T : class, new()
{
    //.....

    private Dictionary<object, T> repository = new Dictionary<object, T>();

    public MemoryRepository()
    {
        FindKeyPropertyInDataType();
    }

    public void Insert(T instance)
    {
        try
        {
            var id = KeyPropertyInfo.GetValue(instance, null);
            repository[id] = instance;
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
    }

    private void FindKeyPropertyInDataType()
    {
        foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
        {
            object[] attributes = propertyInfo.GetCustomAttributes(typeof(RepositoryKey), false);
            if (attributes != null && attributes.Length == 1)
            {
                KeyPropertyInfo = propertyInfo;
            }
            else
            {
                throw new Exception("more than one repository key exist");
            }
        }
    }
}

MemoryRepository was made for temp data discovery.

public class DbRepository<T> : IRepository<T> where T : class, new()
{
    private readonly SQLiteConnection _connection;

    //.....

    public void Insert(T instance)
    {
        try
        {
            _connection.Insert(instance);
        }
        catch (Exception e)
        {
           throw new Exception(e.ToString());
        }
    }
}

DbRepository was made to use Sqlite which is a relational database.

public class RestRepository<T, R>:IRepository<T> where T : class, new() where R : class, new()
{
    //.....

    public void Insert(T instance)
    {
        //Send messages remotely via WWW like
    }
}

RestRepository, to retrieve data from restful web service.