EF Core entity refresh - entity-framework

I would like if there is a way to trigger an object hierachy's lazy load after it has been added in the cache following an insert.
If I insert an object only providing it the foreign keys ids and try to request it right after, none of these properties / collections are retrieved.
Example:
_dbContext.Set<MYTYPE>().Add(myObject);
await _dbContext.SaveChangesAsync(ct);
_dbContext.Entry(myObject).Collection(c => c.SomeCollection).Load();
_dbContext.Entry(myObject).Reference(c => c.Property1).Load();
_dbContext.Entry(myObject.Property1).Reference(c => c.NestedProperty).Load();
_dbContext.Entry(myObject).Reference(c => c.Property2).Load();
_dbContext.Entry(myObject).Collection(c => c.SomeCollection2).Load();
_dbContext.Entry(myObject).Reference(c => c.Property3).Load();
_dbContext.Entry(myObject).Reference(c => c.Property4).Load();
_dbContext.Entry(myObject.Property4).Reference(c => c.SomeOtherNestedProperty).Load();
_dbContext.Entry(myObject).Reference(c => c.Property5).Load();
_dbContext.Entry(myObject).Reference(c => c.Property6).Load();
return _dbContext.Set<MYTYPE>().Where(x => x.Id == myObject.Id);
Unless I do all the Load() (having to nest into several layers sometimes ...), I can't have any of the sub properties properly lazy loaded.
Note: doing
_dbContext.Entry(myObject).Reload()
or
_dbContext.Entry(myObject).State = EntityState.Detached;
doesn't work.
Any idea? Because I have several cases like this, with HEAVY object nesting, and it feels really bad to have to do all the loads manually.
Thanks.

The thing is, if you try to add some line using only the integer as a foreign key like below :
db.tablename.Add(new tablename{
name = "hello",
FK_IdofanotherTable = 5
});
db.SaveChanges();
Then entity does not care about linking the object immediately, you won't be able to access the linked object as a property.
This is how it's actually done in modern Entity Framework :
var TheActualCompleteObject = db.AnotherTable.First(t => t.id == 5);
db.tablename.Add(new tablename{
name = "hello",
AnotherTable = TheActualCompleteObject
});
db.SaveChanges();
So... basically, you will have loaded the object anyway, it's not gonna make anything faster, but it will be more consistant. You will be able to navigate through foreign keys properties.
If this is not possible, then you will have to query back AFTER SaveChanges to retrieve your object AND have access to foreign properties (eagerly loading them, of course, or else it's the same disaster)
Exemple :
_dbContext.Configuration.LazyLoadingEnabled = false;
_dbContext.Set<MYTYPE>().Add(myObject);
await _dbContext.SaveChangesAsync(ct);
var v = _dbContext.First(t => t.id == myObject.id);
var fk_prop = v.Property1;

I also found no easy way around this.
The following might make it simpler...
public static void Reset(this DbContext context)
{
var entries = context.ChangeTracker
.Entries()
.Where(e => e.State != EntityState.Unchanged)
.ToArray();
foreach (var entry in entries)
{
switch (entry.State)
{
case EntityState.Modified:
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Deleted:
entry.Reload();
break;
}
}
}
Hope this helps!
JS

Related

Audit.NET Entity Framework to audit one same entry to two tables

I Have 2 Audit tables one is Audit_ProfileFan and second is Audit_StatusChanges
The first table AuditProfileFan should audit every time update or insert has been made to ProfileFan Table.
The second Audit_StatusChanges should only audit when an update of a certain column FanStatusId is made.
Audit.Core.Configuration.Setup() .UseEntityFramework(ef => ef.AuditTypeExplicitMapper(m => m
.Map<FanActivity, Audit_FanActivity>((fanActivity, auditFanActivity) =>
{
auditFanActivity.ProfileFanId = fanActivity.ProfileFanId;
auditFanActivity.ActivityId = auditFanActivity.ActivityId;
})
.Map<DeliveryActions, Audit_DeliveryActions>((deliveryAction, auditDeliveryAction) =>
{
auditDeliveryAction.ProfileFanId = deliveryAction.FanId;
auditDeliveryAction.DeliveryActionId = deliveryAction.DeliveryActionId;
})
.Map<Fan, Audit_Fan>()
.Map<ProfileFan, Audit_StatusChanges>((profileFan, auditStatusChanges) =>
{
auditStatusChanges.ProfileFanId = profileFan.Id;
//auditStatusChanges.OriginalValue = profileFan.FanStatusId;
//auditStatusChanges.NewValue = profileFan.FanStatusId;
})
.Map<ProfileFan, Audit_ProfileFan>((profileFan, auditProfileFan) =>
{
auditProfileFan.ProfileFanId = profileFan.Id;
auditProfileFan.FanId = profileFan.FanId;
auditProfileFan.EmailResponseStatusId = profileFan.EmailResponseStatusId;
auditProfileFan.FanStatusId = profileFan.FanStatusId;
})
.Map<TagFan, Audit_TagFan>((tagFan, auditTagFan) =>
{
auditTagFan.ProfileFanId = tagFan.ProfileFanId;
auditTagFan.TagId = tagFan.TagId;
})
.AuditEntityAction<IAuditLog>((evt, entry, auditEntity) =>
{
if(entry.Table=="ProfileFan" && entry.Action=="Update")
{
//auditEntity.OriginalValue = profileFan.FanStatusId;
//auditEntity.NewValue = profileFan.FanStatusId;
}
auditEntity.AuditDate = DateTime.Now;
auditEntity.AuditUser = evt.Environment.UserName;
auditEntity.Action = entry.Action; // Insert, Update, Delete
auditEntity.AuditUsername = HttpContext.Current.User.Identity.Name;
})
)
);
But every time an update is made it audits only one table in this case Audit_ProfileFan.
Is my requirement possible or should I do some kind of workaround?
This is not possible with the current version of the EntityFramework data provider, since you can only map from the known entity type and nothing else.
But I've found a way to allow that kind of use cases with minimal impact, by adding a new Map<T> overload that lets you specify the final audit type as a function of the EventEntry, so you would be able to map the same input data type to multiple output audit types, depending on the modified entry.
So for example you could map ProfileFan to different tables depending on the SQL operation (insert/update), with something like this:
Audit.Core.Configuration.Setup()
.UseEntityFramework(ef => ef.AuditTypeExplicitMapper(m => m
.Map<ProfileFan>(
mapper: entry => entry.Action == "Insert" ? typeof(Audit_ProfileFan) : typeof(Audit_StatusChanges),
entityAction: (ev, entry, entity) =>
{
if (entity is Audit_ProfileFan pf)
{
// action for profile fan
// pf.xxxx = ...;
}
else if (entity is Audit_StatusChanges ss)
{
// action for status changes
// ss.xxxx = ...;
}
})
.Map<TagFan, Audit_TagFan>(/*...*/)
.AuditEntityAction<IAuditLog>((evt, entry, auditEntity) =>
{
// common action...
})));
This will be released soon, here is the commit with the changes.
UPDATE
This is included on Audit.EntityFramework library starting on version 14.6.2

String case sensitive in LiteDB query

In various environment, the user name is case insensitive. We query admin equal to ADMIN. I have searched LiteDB called CompareTo to compare two objects that I can't find a point to make string compared as case insensitive.
The code in QueryEquals.cs
internal override IEnumerable<IndexNode> ExecuteIndex(IndexService indexer, CollectionIndex index)
{
var node = indexer.Find(index, _value, false, Query.Ascending);
if (node == null) yield break;
yield return node;
if (index.Unique == false)
{
// navigate using next[0] do next node - if equals, returns
while (!node.Next[0].IsEmpty && ((node = indexer.GetNode(node.Next[0])).Key.CompareTo(_value) == 0))
{
if (node.IsHeadTail(index)) yield break;
yield return node;
}
}
}
The propositional we can case insensitive
using (var db = new LiteRepository("lite.db"))
{
db.Insert(new User { Name = "John" });
var user = db.Query<User>()
.Where(x => x.Name == "JOHN")
.FirstOrDefault(); // proposal return John
var fail = db.Query<User>()
.Where(x => string.Equals(x.Name, "JOHN", StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(); // throw exception
}
Another consideration, it is a possible to execute lambda expression in LiteDB without conversion by visitor?
In LiteDB v4, you can use expressions to make sure that the index of username is stored as lowercase, then you can perform a "lowercase-compare"
// Create index
entity.EnsureIndex(e => e.Username, "LOWER($.Username)");
...
// Find user
collection.Find(e => e.Username == username.ToLower);
If you dont want to use expressions, I guess that you could make sure the username is lowercase before saving in LiteDB or you can use an index less query (slower) by using collection.Where (not quite sure on that method though).

EF. How to include only some sub results in a model?

I'm trying to select list of Users and for each User JobTitle in correct language depended of strLang selected by user.
Something like that:
IList<User> myData;
myData = Context.Users.Where(u => u.Location == strLocation)
.Include(u => u.JobTitles.Where(e => e.Language == strLang))
.ToList();
But it seems Include doesn't like Where clause
You can't conditionally include only a few entities of a related collection, so you should use projection to get the stuff you need:
IList<User> myData;
var temp = Context.Users.Where(u => u.Location == strLocation)
.Select(u => new
{
User = u;
Locations = u.JobTitles.Where(e => e.Language == strLang));
});
foreach(var t in temp)
{
User user = t.User;
user.Locations = t.Locations;
myData.Add(user);
}
You cannot do it by using the "Include" method since it only take naviation properties.
Disclaimer: I'm the owner of the project EF+ on github.
EF+ Query IncludeFilter allow you to easily filter related entities:
// using Z.EntityFramework.Plus; // Don't forget to include this.
IList<User> myData;
myData = Context.Users.Where(u => u.Location == strLocation)
.IncludeFilter(u => u.JobTitles.Where(e => e.Language == strLang))
.ToList();
You can find the project here
You can find the documentation here
Behind the code, IncludeFilter do exactly like Alexander answer by using a projection.

Which is the good way to update object in EF6

I have searched and find 2 way to update object in EF
var attachedEntity = _context.EntityClasses.Local.First(t => t.Id == entity.Id);
//We have it in the context, need to update.
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
////If it's not found locally, we can attach it by setting state to modified.
////This would result in a SQL update statement for all fields
////when SaveChanges is called.
var entry = _context.Entry(entity);
entry.State = EntityState.Modified;
}
_context.SaveChanges();
And other way is seem more easy
var entity = _context.EntityClasses.FirstOrDefault(t => t.Id == entity.Id);
_context.Entry(entity ).EntityState.Modified
_context.SaveChanges();
What is best way to update object?
NOTE: the performence is importance with me
_context.EntityClasses.Local.First(t => t.Id == entity.Id)
=> means that you want to double check the entity on local (the latest loading from DB) and it is not send to DB to find the record so the performance is faster.
_context.EntityClasses.FirstOrDefault(t => t.Id == entity.Id): This command is look up the entity in DB. That means EF creates the query and look up in DB.
The below link is the difference of between Entity.Local.Find & Entity.Find http://msdn.microsoft.com/en-us/data/jj592872.aspx
Hope it helps!

EF Code First + lazy loading, projection does not work as expected

I have something like this:
var tmp =_forumsDb.Threads
.Where(t => t.Id == variable)
.Select(t => new { Thread = t, Posts = t.Posts.Take(1) })
.Single();
Now, i expect tmp.Thread.Posts.Count(); to be 1, but it takes all posts that i have in database. Is it possible to use projection that takes explicit amount of posts, do it in a single query without turning off lazy loading?
Edit:
I tried doing something like this, but it does not work either:
var tmp =_forumsDb.Threads
.Where(t => t.Id == variable)
.Select(t => new { Thread = t, Posts = t.Posts.OrderBy(p => p.DateCreated).Take(1) })
.Select(t => t.Thread)
.Single();
tmp.Thread.Posts is the navigation property for which lazy loading is configured. Since it isn't yet loaded, accessing it loads all the remaining posts.
tmp.Posts is not a navigation property. That's the one you should be able to access without triggering another query.