I'm trying to implement the repository pattern in the following manner:
Interface:
public interface ISurveyRepository
{
Survey Add(Survey survey);
IEnumerable<Survey> GetAll();
Survey GetById(int id);
bool Update(Survey survey);
bool Delete(int id);
}
Implementation:
public class SurveyRepository : ISurveyRepository
{
private readonly ISurveyRepository _dbContext;
public SurveyRepository(ISurveyRepository dbContext)
{
_dbContext = dbContext;
}
public Survey Add(Survey survey)
{
return _dbContext.Add(survey);
}
public IEnumerable<Survey> GetAll()
{
return _dbContext.GetAll();
}
public Survey GetById(int id)
{
return _dbContext.GetById(id);
}
public bool Update(Survey survey)
{
return _dbContext.Update(survey);
}
public bool Delete(int id)
{
return _dbContext.Delete(id);
}
}
And here are the creation of 2 different contexts, this could be oracle database, sql server or whathever, each one of those with a separate Context class
public class SurveyMemoryContext : ISurveyRepository
{
private List<Survey> _surveys;
public SurveyMemoryContext()
{
_surveys = new List<Survey>();
}
public Survey Add(Survey survey)
{
_surveys.Add(survey);
return survey;
}
public IEnumerable<Survey> GetAll()
{
return _surveys;
}
public Survey GetById(int id)
{
return _surveys.Find(p => p.Id == id);
}
public bool Update(Survey survey)
{
var updatedSurvey = _surveys.Find(p => p.Id == survey.Id);
if (updatedSurvey == null) return false;
_surveys.Remove(updatedSurvey);
updatedSurvey.Id = survey.Id;
updatedSurvey.Name = survey.Name;
updatedSurvey.Description = survey.Description;
_surveys.Add(updatedSurvey);
return true;
}
public bool Delete(int id)
{
var deletingSurvey = _surveys.Find(p => p.Id == id);
_surveys.Remove(deletingSurvey);
return true;
}
}
Oracle Database context:
public class SurveyDbContext : ISurveyRepository, IDisposable
{
private IDbConnection _connection;
private IDbCommand _command;
private IDbTransaction _transaction;
public SurveyDbContext()
{
_connection = new OracleConnection(Properties.Settings.Default.OracleConnectionString);
if (_connection.State != ConnectionState.Open)
_connection.Open();
_command = _connection.CreateCommand();
}
public Survey Add(Survey survey)
{
try
{
using (_command = _connection.CreateCommand())
{
_transaction = _connection.BeginTransaction();
_command.CommandText = "INSERT INTO PREFIX_TABLE " +
" (Id, Name, Description) " +
" VALUES(:Id, :Name, :Description)";
_command.AddOracleParameter(":Id", survey.Id);
_command.AddOracleParameter(":Name", survey.Name);
_command.AddOracleParameter(":Description", survey.Description);
_command.ExecuteNonQuery();
_transaction.Commit();
}
}
catch (Exception ex)
{
_transaction.Rollback();
ex.Log();
throw;
}
return survey;
}
public IEnumerable<Survey> GetAll()
{
throw new System.NotImplementedException();
}
public Survey GetById(int id)
{
throw new System.NotImplementedException();
}
public bool Update(Survey survey)
{
throw new System.NotImplementedException();
}
public bool Delete(int id)
{
throw new System.NotImplementedException();
}
private void Dispose(bool isDisposing)
{
if (isDisposing)
{
if (_connection != null)
{
_connection.Close();
_connection.Dispose();
_connection = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Finally, the methods are called like that (in unit testing only at the moment)
[TestClass]
public class SurveyContextTest
{
private SurveyRepository _repository;
[TestInitialize]
public void TestInitialize()
{
//_repository = new SurveyRepository(new SurveyDbContext());
_repository = new SurveyRepository(new SurveyMemoryContext());
}
[TestCleanup]
public void TestCleanup()
{
_repository = null;
}
[TestMethod]
public void AddNewSurveyReturnsInsertedSurvey()
{
// Arrange
var survey = new Survey
{
Id = 100,
Name = "Name",
Description = "Description"
};
// Act
var insertedSurvey = _repository.Add(survey);
// Assert
Assert.AreEqual(survey, insertedSurvey);
}
[TestMethod]
public void UpdateSurveyReturnsTrue()
{
// Arrange
var survey = new Survey
{
Id = 100,
Name = "Name",
Description = "Description"
};
var insertedSurvey = _repository.Add(survey);
insertedSurvey.Id = 110;
insertedSurvey.Name = "Name 2";
insertedSurvey.Description = "Description 2";
// Act
var updatedSurvey = _repository.Update(insertedSurvey);
// Assert
Assert.IsTrue(updatedSurvey);
}
[TestMethod]
public void GetAllReturnsAllTheSurveys()
{
// Arrange
CreateMockSurveysInRepository(5);
// Act
var insertedSurveys = _repository.GetAll();
// Assert
Assert.IsTrue(insertedSurveys.Count() == 5);
}
[TestMethod]
public void GetByIdReturnsTheExistingSurvey()
{
// Arrange
CreateMockSurveysInRepository(5);
// Act
var insertedSurvey = _repository.GetById(101);
// Assert
Assert.IsNotNull(insertedSurvey);
}
[TestMethod]
public void DeleteExistingSurveyReturnsTrue()
{
// Arrange
CreateMockSurveysInRepository(5);
// Act
var isDeleted = _repository.Delete(101);
// Assert
Assert.IsTrue(isDeleted);
}
private void CreateMockSurveysInRepository(int number)
{
for (int i = 0; i < number; i++)
{
var survey = new Survey
{
Id = 100 + i,
Name = "Name " + i,
Description = "Description " + i,
};
_repository.Add(survey);
}
}
}
Here is the schema for clarity
Is this a good implementation of the repository pattern? The _transaction
in the database implementation shall be intended as the Unit of Work.
Unit tests on the db could be done by creating a separate class (SurveyMockDbContext) that does everything as the normal DbContext except it always rolls back everything.
Is this a principle of Dependency Injection, since the constructor of the SurveyRepository class takes in an interface to perform operations, without actually knowing who's really acting?
The model of course is just a simple poco
public class Survey
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
// test code
? Why even bother include it in the post if you don't want it reviewed? What's the// command code
actually doing? A meaningful peer review requires that we review your actual code, not just its skeleton. Also... isn't the interface missing something to select surveys in some way? "code simplified for readability" isn't helping you on this site. Please include your actual code. \$\endgroup\$SurveyRepository
for? It looks like a decorator that doesn't add functionallity... Therefore you can drop it. \$\endgroup\$