Last active
December 20, 2015 08:18
-
-
Save jpolvora/6099134 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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