1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
| public class Entity
{
public Guid Id { get; set; }
}
public class Product : Entity
{
public virtual string Reference { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
public interface IRepository<T> where T : Entity
{
bool Create(T entity);
T Read(Guid id);
bool Update(T entity);
bool Delete(Guid id);
}
public abstract class SqlRepositoryBase<T> : IDisposable, IRepository<T> where T : Entity, new()
{
protected SqlConnection Connection { get; }
protected SqlRepositoryBase(SqlConnection connection)
{
Connection = connection;
connection.Open();
}
#region Default CRUD implementation
// Uses reflection to automap class name and property names to table name and column names respectively
// These may be overided with more appropriate versions
public virtual bool Create(T entity)
{
if (entity.Id == Guid.Empty) entity.Id = Guid.NewGuid();
var mapping = ReflectionHelper.SelectMappingProperties<T>().ToList();
var parameters = mapping.Select(p => $"{p.Name} = @{p.Name}").ToArray();
var values = string.Join(", ", parameters);
var sql = $"INSERT INTO {typeof(T).Name} VALUES ({values})";
SqlTransaction tr = null;
try
{
var command = Connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = sql;
mapping.ForEach(p => command.Parameters.AddWithValue("@" + p.Name, p.GetValue(entity)));
tr = Connection.BeginTransaction();
command.Transaction = tr;
var result = command.ExecuteNonQuery();
tr.Commit();
return result > 0;
}
catch
{
tr?.Rollback();
return false;
}
finally { tr?.Dispose(); }
}
public virtual bool Delete(Guid id)
{
throw new NotImplementedException();
}
public virtual T Read(Guid id)
{
if (id == Guid.Empty) return null;
var sql = $"SELECT * FROM {typeof(T).Name} WHERE Id = @Id";
var command = Connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = sql;
var reader = command.ExecuteReader();
if (!reader.NextResult()) return null;
var entity = new T();
ReflectionHelper.SelectMappingProperties<T>().ToList().ForEach(p => p.SetValue(entity, reader[p.Name]));
return entity;
}
public virtual bool Update(T entity)
{
throw new NotImplementedException();
}
#endregion
#region IDisposable
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) { Connection.Dispose(); }
_disposed = true;
}
~SqlRepositoryBase() { Dispose(false); }
#endregion
}
public interface IProductRepository : IRepository<Product>
{
Product FindByReference(string reference);
}
public class ProductSqlRepository : SqlRepositoryBase<Product>, IProductRepository
{
public ProductSqlRepository(SqlConnection connection) : base(connection) { }
public Product FindByReference(string reference)
{
var sql = "SELECT Id FROM Product WHERE Reference = @Reference";
var command = Connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = sql;
command.Parameters.AddWithValue("@Reference", reference);
var reader = command.ExecuteReader();
if (!reader.NextResult()) return null;
var id = (Guid)reader["Id"];
return Read(id);
}
}
public static class ReflectionHelper
{
public static IEnumerable<PropertyInfo> SelectMappingProperties<T>()
{
return SelectMappingProperties(typeof(T));
}
public static IEnumerable<PropertyInfo> SelectMappingProperties(Type t)
{
return t.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead && p.CanWrite).ToArray();
}
} |
Partager