breeze - modify an entity, on the server, based upon it's navigational properties, before saving - breeze

Is there any way to get an Entity's navigational property's "current" value in BeforeSaveEntity (or anywhere else before save) in breeze on the server side? By current, I mean what exists in the database, with any incoming changes merged in. This isn't for validation, rather I am computing a value for a parent property (that I don't want on the client) based upon both the parent fields and children fields...
For example,
public class Parent {
public ICollection<Child> Children{ get; set; }
}
. . .
protected override bool BeforeSaveEntity(EntityInfo entityInfo) {
if (entityInfo.Entity.GetType() == typeof(Parent) &&
(entityInfo.EntityState == EntityState.Added || entityInfo.EntityState == EntityState.Updated)) {
// Lazy load Parent's Children collection out of breeze's context
// so items are "current' (existing merged with changes)
Parent parent = (Parent)entityInfo.Entity;
Context.Entry(parent).Collection(p => p.Children).Load();
// this throws exception Member 'Load' cannot be called for property
// 'Children' because the entity of type 'Parent' does not exist in the context.
}
}
I'm thinking they are not in the DBContext yet. All I can think to do is to retrieve the existing children from the database, and hand merge the changes in BeforeSaveEntities, which is a hassle.

Lazy loading is not enable in the DbContext that Breeze uses for saving. The reason is detailed in this SO answer.
You should load any additional entities in a separate DbContext.
Here's an example of how I did it in a project. Perhaps the MergeEntities and DetachEntities methods should be included with Breeze to make it simpler to do this.
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// create a separate context for computation, so we don't pollute the main saving context
using (var newContext = new MyDbContext(EntityConnection, false))
{
var parentFromClient = (Parent)saveMap[typeof(Parent)][0].Entity;
// Load the necessary data into the newContext
var parentFromDb = newContext.Parents.Where(p => p.ParentId == parentFromClient.ParentId)
.Include("Children").ToList();
// ... load whatever else you need...
// Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
var objectContext = ((IObjectContextAdapter)newContext).ObjectContext;
var objectStateEntries = MergeEntities(objectContext, saveMap);
// ... perform your business logic...
// Remove the entities from the second context, so they can be saved in the original context
DetachEntities(objectContext, saveMap);
}
return saveMap;
}
/// Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
Dictionary<ObjectStateEntry, EntityInfo> MergeEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
{
var oseEntityInfo = new Dictionary<ObjectStateEntry, EntityInfo>();
foreach (var type in saveMap.Keys)
{
var entitySet = this.GetEntitySetName(type);
foreach(var entityInfo in saveMap[type])
{
var entityKey = oc.CreateEntityKey(entitySet, entityInfo.Entity);
ObjectStateEntry ose;
if (oc.ObjectStateManager.TryGetObjectStateEntry(entityKey, out ose))
{
if (ose.State != System.Data.Entity.EntityState.Deleted)
ose.ApplyCurrentValues(entityInfo.Entity);
}
else
{
oc.AttachTo(entitySet, entityInfo.Entity);
ose = oc.ObjectStateManager.GetObjectStateEntry(entityKey);
}
if (entityInfo.EntityState == Breeze.ContextProvider.EntityState.Deleted)
{
ose.Delete();
}
oseEntityInfo.Add(ose, entityInfo);
}
}
return oseEntityInfo;
}
/// Remove the entities in saveMap from the ObjectContext; this separates their navigation properties
static void DetachEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
{
foreach (var type in saveMap.Keys)
{
foreach (var entityInfo in saveMap[type])
{
try
{
oc.Detach(entityInfo.Entity);
}
catch
{ // the object cannot be detached because it is not attached
}
}
}
}

Related

How do I make EF EntityType.GetProperty Eager load?

I am using the following code snippet to save a modified entity in my repository
object id1 = item.GetProperty("Id");
T originalEntity = dbSet.Find(id1); // but this doesnt update navigation properties
((DbContext)context).Entry(originalEntity).CurrentValues.SetValues(item);
navProps = GetNavigationProperties(originalEntity);
foreach (PropertyInfo navProp in navProps)
{
//Set originalEntity prop value to modifiedEntity value
var newval = (LoggedEntity)navProp.GetValue(item);
object entity = null;
if (newval != null)
{
Type tp = navProp.PropertyType;
DbSet entities = ((DbContext)context).Set(tp);
entity = entities.Find(newval.Id);
}
navProp.SetValue(originalEntity, entity);
}
which calls
public List<PropertyInfo> GetNavigationProperties(T entity)
{
Type t = entity.GetType();
ObjectContext objectContex = ((IObjectContextAdapter)((DbContext)context)).ObjectContext;
EntityType elementType = objectContex.CreateObjectSet<T>().EntitySet.ElementType;
var properties = new List<PropertyInfo>();
Type entityType = entity.GetType();
foreach (NavigationProperty navigationProperty in elementType.NavigationProperties)
{
PropertyInfo prop = entityType.GetProperty(navigationProperty.Name);
properties.Add(prop);
}
return properties;
}
However I have a problem when I want to set the navigation property to null.
The changes simply don't save.
The answer is explained in this question
Which points out that the navigation properties need to be eager loaded.
How do I modify my GetNavigationProperties procedure to eager load?
I inserted the following line
originalEntity.GetProperty(navProp.Name);
before
navProp.SetValue(originalEntity, entity);
and now the code works if entity is null

Intercepting Changes in Breezejs and turning them into new records

I have a javascript application that saves changes in the front end, pushes them to a WebAPI controller and saves them to an Entity Framework context. In order to save a history of the changes made to the table I would like to intercept certain edits to certain tables and change them from an edit row type command to a new row command with the edited data. My current controller looks like this
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _ContextProvider.SaveChanges(saveBundle);
}
How can I set this up to create the rows?
Update the 'saveMap' that is part of the BeforeSaveEntities delegate or overridden method. Use the ContextProvider's CreateEntityInfo method to create items to add to the saveMap.
public SaveResult SaveChanges(JObject saveBundle) {
_ContextProvider.BeforeSaveEntitiesDelegate = CreateNewFoo;
return _ContextProvider.SaveChanges(saveBundle);
}
// the saveMap parameter contains the entities that were passed from the client to be saved
// you can add to the map here:
private Dictionary<Type, List<EntityInfo>> CreateNewFoo(Dictionary<Type, List<EntityInfo>> saveMap) {
// create your new entity.
var foo = new Foo();
foo.CreatedOn = DateTime.Now;
// SaveOptions.Tag is free form data that can be passed from the client.
var tag = ContextProvider.SaveOptions.Tag;
foo.Comment = (tag == null) ? "Generic comment" : tag.ToString();
// create an EntityInfo object from the new entity
var ei = ContextProvider.CreateEntityInfo(foo);
// add it to the saveMap
List<EntityInfo> fooInfos;
if (!saveMap.TryGetValue(typeof(Foo), out fooInfos)) {
fooInfos = new List<EntityInfo>();
saveMap.Add(typeof(Foo), fooInfos);
}
fooInfos.Add(ei);
// return the updated saveMap
return saveMap;
}

breeze BeforeSaveEntity does not save calculated value

i need to calculate some values on the serverside when a specific entity gets updated.
if i update the entity the following code gets executed (C_CompletePrice gets set) and it even gets reflected on clientside (clientside breeze gets all the properties nicely back)
but when i check the db nothing is saved. so when clearing the browser cache and checking the entity again there are the old values...
private bool BeforeSaveTransaction(tblTransactions transaction, EntityInfo info)
{
transaction.C_CompletePrice = 11111111;
return true;
...
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
var entity = entityInfo.Entity;
if (entity is tblTransactions)
{
return BeforeSaveTransaction(entity as tblTransactions, entityInfo);
}
...
i'm using breeze 1.4.6
on the server i'm using Breeze.WebApi and Breeze.WebApi.EF
the model i'm using: http://pastebin.com/Dc03DrNe
Update
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
foreach (Type entityType in saveMap.Keys)
{
if (entityType.Name == "tblTransactions")
{
foreach (EntityInfo ei in saveMap[entityType])
{
CalculateTransaction(ei);
}
}
}
return base.BeforeSaveEntities(saveMap);
}
private void CalculateTransaction(EntityInfo entityInfo)
{
tblTransactions transaction = (tblTransactions) entityInfo.Entity;
transaction.C_CompletePrice = 1234567;
...
Using BeforeSaveEntities results in the same strange behaviour:
Entites on the client gets updatet :)
DB not :(
So before i'll use now #dominictus solution (overriding SaveAll) i'm kindly asking for the purpose of those methods i've used (bool BeforeSaveEntity(...) and BeforeSaveEntities(saveMap)). I've consulted the doc and i've watched bryan noyes brilliant pluralsight course but still my simple mind doesn't get it :)
Did you update the EntityInfo.OriginalValuesMap as described in the ContextProvider topic?
I do it a bit different. Here is an example where I save a timestamp when object was changed. This is a method in my Context class.
public override int SaveChanges()
{
foreach (
var entry in
this.ChangeTracker.Entries()
.Where((e => (e.State == (EntityState) Breeze.WebApi.EntityState.Added || e.State == (EntityState) Breeze.WebApi.EntityState.Modified))))
{
if (entry.Entity.GetType() == typeof(MyClass))
{
var entity = entry.Entity as MyClass;
if (entity != null) entity.UpdatedDateTime = DateTime.Now;
}
}
return base.SaveChanges();
}
In your case, you could just write: entity.C_CompletePrice = 11111111;
As for method BeforeSaveEntity I prefer using BeforeSaveEntities. Check breeze documentation for examples.

EF Code First, how to reflect on model

In EF code first, one specifies field properties and relationships using the fluent interface. This builds up a model. Is it possible to get a reference to this model, and reflect on it?
I want to be able to retrieve for a given field, if it is required, what its datatype is, what length, etc...
You need to access the MetadataWorkspace. The API is pretty cryptic. You may want to replace DataSpace.CSpace with DataSpace.SSpace to get the database metadata.
public class MyContext : DbContext
{
public void Test()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var mdw = objectContext.MetadataWorkspace;
var items = mdw.GetItems<EntityType>(DataSpace.CSpace);
foreach (var i in items)
{
foreach (var member in i.Members)
{
var prop = member as EdmProperty;
if (prop != null)
{
}
}
}
}

Signals in Linq to Sql?

Does anyone know of a way to do something similar to Django's signals using LINQ to SQL?
I'm trying to record when new rows are inserted and when certain columns are updated, so I really just want pre_save and post_save signals.
I can kind of do it with some models by using the partials defined like OnFooIDChanging() and OnFooIDChanged() (where FooID is a primary key), but this doesn't work for models whose primary key is not an identity, or is set by code.
For those, I could possibly use OnValidate(), but that would only be pre_save, and it makes dealing with the database tough, since OnValidate() is called from DBContext.SubmitChanges(), which of course doesn't allow a second SubmitChanges() to be called from within, making post_save basically impossible as far as I can see.
Ok, I've gone completely down the rabbit hole on this one, but I think I have a pretty cool solution:
First, add an event handler to your data context that will collect all of the post-save signals and hide the Dispose method so that we can call the event right before we dispose. (Note that I use the new keyword instead of override. This makes calling the event possible.)
partial class MyDataContext
{
internal delegate void PostSaveHandler();
internal event PostSaveHandler PostSave;
// This method hides the underlying Dispose because we need to call PostSave.
public new void Dispose(bool disposing)
{
// Obviously necessary error handling omitted for brevity's sake
PostSave();
base.Dispose(disposing);
}
}
Next, write a T4 Template that inspects the dbml file that Linq to Sql generates for you.
<#
var dbml = XDocument.Load(#"MyDataContext.dbml");
var name = XName.Get("Type", "http://schemas.microsoft.com/linqtosql/dbml/2007");
var tables = from t in dbml.Descendants(name) select t.Attribute("Name").Value;
foreach(var table in tables)
{
#>
...
For each table in the database (and thus each partial class), add on to the partial with the following methods.
public partial class Foo
{
internal void OnInsert(MyDataContext db) {
PreInsert();
db.PostSave += delegate { PostInsert(); };
}
internal void OnUpdate(MyDataContext db) {
PreUpdate();
db.PostSave += delegate { PostUpdate(); };
}
internal void OnDelete(MyDataContext db) {
PreDelete();
db.PostSave += delegate { PostDelete(); };
}
partial void PreInsert();
partial void PostInsert();
partial void PreUpdate();
partial void PostUpdate();
partial void PreDelete();
partial void PostDelete();
}
// repeat for all tables
Also add another partial MyDataContext via T4. This will be adding definitions to the partial methods that Linq to SQL gives you (as Merritt mentioned).
public partial class MyDataContext
{
// Add these three partial methods for each table
partial void InsertFoo(Foo foo)
{
foo.OnInsert(this);
ExecuteDynamicInsert(foo);
}
partial void UpdateFoo(Foo foo)
{
foo.OnUpdate(this);
ExecuteDynamicUpdate(foo);
}
partial void DeleteFoo(Foo foo)
{
foo.OnDelete(this);
ExecuteDynamicDelete(foo);
}
// ...
}
Hide those files away somewhere safe, so no one tries to mess with them.
Your signals framework is set up. Now you can write your signals. Put these either in Foo.cs or all together in a Signals.cs file:
partial class Foo
{
partial void PostInsert()
{
EventLog.AddEvent(EventType.FooInserted, this);
}
}
This is a bit complex, so if anything doesn't make sense, please leave a comment and I'll do my best to address it.
I have a much easier solution than what I already posted which didn't work anyway: override SubmitChanges(ConflictMode failureMode):
partial class MyDataContext
{
// SubmitChanges() calls this method after inserting default value for param
public override void SubmitChanges(ConflictMode failureMode)
{
// Pre-Submit Changes
//Updates
for (int changeCounter = 0; changeCounter < this.GetChangeSet().Updates.Count; changeCounter++)
{
var modifiedEntity = this.GetChangeSet().Updates[changeCounter];
// Do something, for example:
// var tableXEntry = new TableX() { Prop1 = "foo" };
// this.tableXEntries.InsertOnSubmit(tableXEntry );
}
//Inserts
for (int changeCounter = 0; changeCounter < this.GetChangeSet().Inserts.Count; changeCounter++)
{
object modifiedEntity = this.GetChangeSet().Inserts[changeCounter];
// Do Something
}
// Submit Changes
base.SubmitChanges(failureMode);
// Post Submit Changes
//Updates
for (int changeCounter = 0; changeCounter < this.GetChangeSet().Updates.Count; changeCounter++)
{
var modifiedEntity = this.GetChangeSet().Updates[changeCounter];
// Do something, for example:
// var tableXEntry = new TableX() { Prop1 = "foo" };
// this.tableXEntries.InsertOnSubmit(tableXEntry );
}
//Inserts
for (int changeCounter = 0; changeCounter < this.GetChangeSet().Inserts.Count; changeCounter++)
{
object modifiedEntity = this.GetChangeSet().Inserts[changeCounter];
// Do Something
}
}
With the Entity Framework, I do something similar to what you are trying to do: after I save a entity, I insert a new entry into a different table for auditing purposes (it's a copy of the entity before the changes). there is a SaveChanges() event on the EF entities container (like the data context) that allows you to add to the current context before changes are saved.

Resources