Skip to content

Instantly share code, notes, and snippets.

@jpolvora
Last active December 20, 2015 08:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpolvora/6099134 to your computer and use it in GitHub Desktop.
Save jpolvora/6099134 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DbContextCaching {
public abstract class EntityBase {
[Key]
public int Id { get; set; }
public DateTime DataCadastro { get; set; }
public DateTime DataAlteracao { get; set; }
public string Modifications { get; set; }
}
public class Log {
[Key]
public int Id { get; set; }
public string EntityType { get; set; }
public DateTime DateTime { get; set; }
public string UserName { get; set; }
public int RecordId { get; set; }
public string FieldName { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
public override string ToString() {
return string.Format("{0}: {1}, {2} - {3}", FieldName, OldValue, NewValue, DateTime);
}
}
public class Usuario : EntityBase {
[StringLength(50)]
public string Nome { get; set; }
public override string ToString() {
return string.Format("{0} -{1}", Id, Nome);
}
}
public static class DbContextExtensions {
public static DbEntityEntry<T> InsertOrUpdate<T>(this DbContext context, T entity) where T : EntityBase {
var entry = context.Entry(entity);
entry.State = entity.Id == 0 ? EntityState.Added : EntityState.Modified;
return entry;
}
}
public abstract class DbContextCache<T, TContext>
where T : class, new()
where TContext : DbContext, new() {
private readonly Lazy<TContext> _context = new Lazy<TContext>(true);
public TContext Context {
get { return _context.Value; }
}
public bool IsCached(Func<T, bool> predicate) {
if (predicate == null)
predicate = _ => true;
return Context.Set<T>().Local.Any(predicate);
}
public T GetFromCache(Func<T, bool> predicate = null) {
if (predicate == null)
predicate = _ => true;
return Context.Set<T>().Local.FirstOrDefault(predicate);
}
public T Get(Func<T, bool> predicate = null) {
if (predicate == null)
predicate = _ => true;
return GetFromCache(predicate) ?? Context.Set<T>().FirstOrDefault(predicate);
}
public T GetById(params object[] keyValues) {
return Context.Set<T>().Find(keyValues: keyValues);
}
protected virtual bool IsSatisfiedBy(T left, T right) {
return left == right;
}
public virtual void InvalidateEntries(TContext anotherContext) {
var modifiedOrDeletedEntities = anotherContext.ChangeTracker.Entries<T>()
.Where(x => x.State == EntityState.Modified || x.State == EntityState.Deleted || x.State == EntityState.Detached)
.Select(s => s.Entity);
foreach (var modifiedOrDeleted in modifiedOrDeletedEntities) {
var current = modifiedOrDeleted;
var entity = Context.ChangeTracker
.Entries<T>()
.Select(x => x.Entity)
.FirstOrDefault(x => IsSatisfiedBy(x, current));
if (entity == null)
continue;
var entry = Context.Entry(entity);
entry.State = EntityState.Detached;
}
}
}
/// <summary>
/// Fecha o parametro generico
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class CacheTesteContext<T> : DbContextCache<T, TesteContext> where T : EntityBase, new() {
protected override bool IsSatisfiedBy(T left, T right) {
return left.Id == right.Id;
}
}
public class CacheUsuarios : CacheTesteContext<Usuario> {
//protected override bool IsSatisfiedBy(Usuario left, Usuario right) {
// return left.Id == right.Id;
//}
}
public class TesteContextInitializer : CreateDatabaseIfNotExists<TesteContext> {
protected override void Seed(TesteContext context) {
var usuario = new Usuario {
Nome = "Teste"
};
context.Usuarios.Add(usuario);
base.Seed(context);
}
}
public class TesteContext : DbContext {
static readonly CacheUsuarios _CacheUsuarios = new CacheUsuarios();
public static DbContextCache<Usuario, TesteContext> CacheUsuarios {
get { return _CacheUsuarios; }
}
static TesteContext() {
Database.SetInitializer(new TesteContextInitializer());
}
public DbSet<Usuario> Usuarios { get; set; }
public DbSet<Log> Logs { get; set; }
public override int SaveChanges() {
foreach (var dbEntityEntry in ChangeTracker.Entries<EntityBase>().Where(x => x.State == EntityState.Modified)) {
var typeName = dbEntityEntry.Entity.GetType().Name;
foreach (var propertyName in dbEntityEntry.OriginalValues.PropertyNames) {
var property = dbEntityEntry.Property(propertyName);
if (!property.IsModified) continue;
var log = new Log {
DateTime = DateTime.UtcNow,
EntityType = typeName,
FieldName = property.Name,
OldValue = property.OriginalValue != null ? property.OriginalValue.ToString() : "",
NewValue = property.CurrentValue != null ? property.CurrentValue.ToString() : "",
RecordId = dbEntityEntry.Entity.Id,
UserName = Thread.CurrentPrincipal.ToString()
};
Logs.Add(log);
}
}
var entities = ChangeTracker.Entries<EntityBase>();
foreach (var entry in entities) {
var current = entry;
switch (current.State) {
case EntityState.Added: {
current.Entity.DataCadastro = DateTime.UtcNow;
current.Entity.DataAlteracao = DateTime.UtcNow;
break;
}
case EntityState.Modified: {
current.Property(x => x.DataCadastro).IsModified = false;
current.Entity.DataAlteracao = DateTime.UtcNow;
current.Entity.Modifications = GetLastChanges(this, current);
break;
}
}
}
_CacheUsuarios.InvalidateEntries(this);
return base.SaveChanges();
}
static string GetLastChanges(TesteContext ctx, DbEntityEntry<EntityBase> current) {
var logs = ctx.Logs.Local.Where(x => x.EntityType == current.Entity.GetType().Name);
var sb = new StringBuilder();
foreach (var log in logs) {
sb.AppendLine(log.ToString());
}
return sb.ToString();
}
}
class Program {
static void Main(string[] args) {
using (var ctx = new TesteContext()) {
ctx.Database.Initialize(false);
}
using (var ctx = new TesteContext()) {
foreach (var usuario in ctx.Usuarios) {
Console.WriteLine(usuario);
Console.WriteLine("Data de Modificação: {0}", usuario.DataAlteracao);
}
}
//cache vazio
Debug.Assert(TesteContext.CacheUsuarios.IsCached(_ => true) == false);
//coloca o usuário no cache
Debug.Assert(TesteContext.CacheUsuarios.Get() != null);
//verifica se está no cache realmente
Debug.Assert(TesteContext.CacheUsuarios.IsCached(_ => true));
//para remover do cache, basta alterar o usuário
using (var ctx = new TesteContext()) {
var usuario = ctx.Usuarios.First();
usuario.Nome = Guid.NewGuid().ToString().Substring(0, 10);
ctx.SaveChanges();
Console.WriteLine("Usuário alterado normalmente: {0} / {1}", usuario, usuario.DataAlteracao);
}
using (var logctx = new TesteContext()) {
foreach (var log in logctx.Logs.GroupBy(x => x.EntityType)) {
Console.WriteLine("Nome da Tabela: {0} ", log.Key);
foreach (var log1 in log) {
//Console.WriteLine(log1);
var format = String.Format("{0}|{1}|{2}|{3}", log1.FieldName.PadRight(10), log1.OldValue.PadRight(10), log1.NewValue.PadRight(10), log1.DateTime);
Console.WriteLine(format);
}
}
}
//teste
var user = new Usuario();
var dbc = new TesteContext();
var entry = dbc.Entry(user);
entry.State = EntityState.Added;
//nao está mais no cache
Debug.Assert(TesteContext.CacheUsuarios.IsCached(_ => true) == false);
//coloca no cache e recupera
var normal = TesteContext.CacheUsuarios.Get();
Console.WriteLine("Usuário recuperado {0}", normal);
Console.WriteLine("MOdificações: {0}", normal.Modifications);
Console.ReadLine();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment