I don't understand why it is recommended everywhere to use AddOrUpdate in the Seed method?
We develop application for half a year already and the AddOrUpdates overwrites user changes every time we update the server. E.g. if we call in the Seed:
context.Styles.AddOrUpdate(new Style { Id = 1, Color = "red" });
And user changes the Style to "green" then on next server update we overwrite it to "red" again and we get very annoyed user.
It looks that if we change AddOrUpdate to Add we will be guaranteed from overwriting user data. If we still need some special case we can put it to separate migration. Unlike the general Configuration.Seed method particular migrations don't run twice over the same database version.
I assume that Style's primary key is Id. The overload of AddOrUpdate that you use only checks if there is a record having Id == 1. If so, it updates it. That's all.
What's going wrong here is that the primary key is a surrogate key, i.e. it's there for querying convenience, but it's got no business meaning. Usually, with migrations you want to look for the natural keys of entities though. That's how the user identifies data. S/he wants a green style, not a style identified by 1.
So I think you should use this overload of AddOrUpdate:
context.Styles.AddOrUpdate( s => s.Color,
new Style { Id = 1, Color = "red" });
Now when there is no red style anymore, a new one is inserted, overriding the Id value (assuming that it's generated by the database).
From your later comments I understand that you want to Add data when they're new, but not update them when they exist (compared by primary key). For this you could use a slightly adapted version of an AddWhenNew method I described here. For your case I would do it like so:
public T void MarkAsAddedWhenNew<T>(this DbContext context,
Expression<Func<T, object>> identifierExpression, T item)
where T : class
{
context.Set<T>().AddOrUpdate(identifierExpression, item);
if (context.Entry(item).State != System.Data.Entity.EntityState.Added)
{
var identifierFunction = identifierExpression.Compile();
item = context.Set<T>()
.Local
.Single(x => identifierFunction(item)
.Equals(identifierFunction(x)));
context.Entry(item).State = System.Data.Entity.EntityState.Unchanged;
}
return item;
}
Re-fetching the item from the local collection is a nuisance, but necessary because of a bug in AddOrUpdate(). This bug also caused the error you got when setting the state of the original entry to Unchanged: it was a different instance than the attached one.
The way Add method acts is misleading. It Inserts data into database even if there is already a row with the same PrimaryKey as we do Add. It just creates new PrimaryKey ignoring our value silently. I should have tried it before asking the question, but anyway, I think I'm not the only one who confused by this. So, in my situation Add is even worse than AddOrUpdate.
The only solution I've come to is following:
public static void AddWhenNew<T>(this DbContext ctx, T item) where T : Entity
{
var old = ctx.Set<T>().Find(item.Id);
if (old == null)
ctx.Set<T>().AddOrUpdate(item);
/* Unfortunately this approach throws exception when I try to set state to Unchanged.
Something like:"The entity already exists in the context"
ctx.Set<T>().AddOrUpdate(item);
if (ctx.Entry(item).State != System.Data.Entity.EntityState.Added)
ctx.Entry(item).State = System.Data.Entity.EntityState.Unchanged;
*/
}
Related
Is there a method to call in Entity Framework to see the data that has changed (in memory) and would be written to disk when SaveChanges is called?
I'd like to display something to the user indicating that there is an unsaved change. Table level is ok, but I'd prefer to know if a particular field/column has an unsaved change.
For example, add this method to your context:
IEnumerable<(string Key, string Entity, EntityState state,
IEnumerable<(string Property, object OriginalValue, object CurrentValue)> Properties)> GetChanges()
{
var states = new[] { EntityState.Added, EntityState.Modified, EntityState.Deleted };
return this.ChangeTracker.Entries().Where(c => states.Contains(c.State))
.Select(entry =>
(
string.Join(",", entry.Metadata.FindPrimaryKey()
.Properties.Select(p => p.PropertyInfo.GetValue(entry.Entity))),
entry.Metadata.ClrType.Name,
entry.State,
entry.Properties
.Where(p => p.IsModified == (p.EntityEntry.State == EntityState.Modified))
.Select(prop =>
(
prop.Metadata.PropertyInfo.Name,
prop.OriginalValue,
prop.CurrentValue
)
)));
}
It returns added, modified or deleted entity objects, listing their class names (not table names) and their properties, with original and current values. The key values are also included to be able to make a distinction between objects of the same type.
Of modified entities only the changed properties are listed.
Simply enumerate the ChangeTracker.Entries to examine all the tracked entities, and their EntityEntry.State.
I am using dynamic data with reactiveui,
` _propList.Connect()
.WhenAnyPropertyChanged()
.Subscribe(t =>
{
}`
the code will be trigger if I just edit any item in the grid. However, when I try to add or remove an item, it is not triggered.
In my view model I have something like this
private SourceList<Decision> _myList { get; set; } = new SourceList<Decision>();
private readonly IObservableCollection<Decision> _targetCollection = new ObservableCollectionExtended<Decision>();
public IObservableCollection<Decision> TargetCollection => _targetCollection;
in my view, I simply
this.OneWayBind(VM, vm => vm.TargetCollection, v => v.DataGrid1.DataSource);
If I remove or Add item in the grid, and press Save
_myList.Count() didn't change, but
_TargetCollection.Count() will increase or decrease by number of items I delete
In my ViewModel
OKCmd = ReactiveCommand.Create(() =>
{
//// _myList.Connect()
////.Subscribe(t =>
//// {
//// ;
//// }
//// );
t.Items.count() and it is the initial load items, but I couldn't seem to know what items have been added or removed. Am I missing something.
Of course, I can keep track of what items are added or removed in the UI, but I am hoping I don't have to do that.
Thanks.
To help me answer your question, I need to better understand what you are trying to achieve but first I will explain what the default behaviour of DD is.
If you want add / remove events you need _propList.Connect().Subscribe(changes => ...). These are the collection changes and you will receive all collection change events including the initial load, but no inline changes.
By default, no property changes are wire in. This is because to monitor property changes is expensive and is opt in only. Also WhenAnyPropertyChanged() never tiggers for the initial load. This is because the item is already loaded and no properties have changed between Connect being called and the property changed observable being subscribed to.
Following on from 2, you will never receive a property changed when an item is removed from the underlying source. This is because when an item it removed, any inline subscriptions are disposed of. Otherwise there would be memory leaks.
Another option for monitoring inline changes is to make use of 'MergeMany' which allows you to craft any observable on a specific item, and in your case you can create an observable to return the initial value as well as as subsequent changes.
It is possible using standard rx to listen to collection changes and inline changes in a single observable, which you would have to compose yourself. For example
var myCollectionChanges = _propList.Connect();
var myPropertyChanges = _propList.Connect().WhenAnyPropertyChanged();
var allMyChanges = myCollectionChanges.Select(_ => Unit.Default)
.Merge(myPropertyChanges.Select(_ => Unit.Default));
In the this example, I have used Select(_ => Unit.Default) to enable the merge operator as it requires the same signature. However what signature is returned is up to you, the key point being that the signatures must match.
I've seen sample code for reading an Entity Framework EdmProperty's StoreGeneratedPattern property (computed, identity, or none) in at least 2 places (here & here), but it doesn't work with my model. My context is an ObjectContext, the version is EF5; maybe this code broke with EF5? When I look at the properties for this property/column in the model, it shows identity.
Here is my code:
using ( var context = new MyApplicationEntities() )
{
var entityType = ( (EntityConnection)context.Connection )
.GetMetadataWorkspace() // can't call context.MetadataWorkspace - storage model will not be present
.GetType( "MyEntityTypeWithIdentityColumn", "MyApplicationModel.Store", DataSpace.SSpace ) as EntityType;
EdmMember identityColumn = entityType.Members["MyIdentityColumn"];
Facet item;
// All I get here for Facets is Nullable & DefaultValue
if ( identityColumn.TypeUsage.Facets.TryGetValue( "StoreGeneratedPattern", false, out item ) )
{
var value = ( (StoreGeneratedPattern)item.Value ) == StoreGeneratedPattern.Identity;
}
}
Pawel's question led me to examine the edmx and discover the answer. I said in my question that I could look at the model (UI) and see that the column in question was an identity column. In fact, it was not. In any case I expected the StoreGeneratedPattern property to exist since the enumeration contains all possible values (Identity, Computed, None).
My actual goal was to detect all entities whose primary key column/property was not identity.
I discovered through subsequent testing that the StoreGeneratedPattern property only exists for identity & computed; if the column is not store generated, it doesn't exist (StoreGeneratedPattern.None is never referenced). Thus, the sample code above is fine as long as it's modified to handle the absence of the property (which seems strange to me).
I have a JPA domain entity that I'm updating from user input. Depending on lots of factors about that object, it has actions to perform when it's updated (in this case, the update is to "mark it completed").
In the database, two "post-completion action configuration" fields (note and next_workflow) are nullable, and either have a value of interest, or are NULL. There may be very many of these, but I'm starting with these two.
I wrote the following method in the model class:
#PostUpdate
public void gotUpdate() {
System.out.println("Got post update for " + this.getDescription());
if (! this.getNote().isEmpty()) {
Note n = new Note();
n.setAssetId(this.getAssetId());
n.setNotifyLessor(1);
n.setNote(this.getLessorNote() + this.getCapturedData());
n.setCreatedDate(new Date());
n.persist();
}
System.out.println("In the middle of post update for " + this.getDescription());
if (this.getNextWorkflow() != 0) {
Asset a = this.getAssetId();
a.setWorkflowId(Workflow.findWorkflow(this.getNextWorkflow()));
a.merge();
}
System.out.println("Finishing post update for " + this.getDescription());
}
For entities with NULL "note" values, the console output is:
Got post update for this item
For entities with non-NULL "note" values and with NULL "nextWorkflow" values, the console output is:
Got post update for this item
In the middle of post update for this item
No errors anywhere, no stack dump, no nothing. The method just silently quits, AND the merge I'm doing on this entity doesn't complete (the database remains unchanged).
Stepping through this in the debugger gets to that line where things are being tested, and if the value is NULL, it pops a tab saying "Source not found", which I don't really know what to make of. I think that's just the debugger saying it can't step into something, but I'm not actually asking it to...
Rookie question, it turns out.
If Object's field field is null, then object.getField().length() is a call to a null pointer.
The answer is to test the field for nullness, not some side-effect of nullness.
if (! (this.getNote() == null)) {
...
}
Framework: I'm using using MVC 3 + EntityFramework 4.1 Code-First.
Concept: One Legislation entity has many Provision entities. The idea is that the user enters a Legislation entity, that gets saved then the function that saves it passes it along to another function to see whether that Legislation has a ShortTitle. If it does, then it formats it into a properly worded string and includes it as the Legislation's first Provision, then saves the changes to db.
Issue: The problem is, I've tried coding it in different ways, I keep getting a NullReferenceException, telling me to create a new object instance with the "new" keyword, and points me to the savedLegislation.Provisions.Add(provision); line in my second function.
Here are the two functions at issue, this first one saves the Legislation proper:
public Legislation Save(NewLegislationView legislation)
{
Legislation newLegislation = new Legislation();
// Simple transfers
newLegislation.ShortTile = legislation.ShortTile;
newLegislation.LongTitle = legislation.LongTitle;
newLegislation.BillType = legislation.BillType;
newLegislation.OriginatingChamber = legislation.OriginatingChamber;
newLegislation.Preamble = legislation.Preamble;
// More complicated properties
newLegislation.Stage = 1;
this.NumberBill(newLegislation); // Provides bill number
newLegislation.Parliament = db.LegislativeSessions.First(p => p.Ending >= DateTime.Today);
newLegislation.Sponsor = db.Members.Single(m => m.Username == HttpContext.Current.User.Identity.Name);
// And save
db.Legislations.Add(newLegislation);
db.SaveChanges();
// Check for Short titles
this.IncludeShortTitle(newLegislation);
// return the saved legislation
return newLegislation;
}
And the second function which is invoked by the first one deals with checking whether ShortTitle is not empty and create a Provision that is related to that Legislation, then save changes.
public void IncludeShortTitle(Legislation legislation)
{
var savedLegislation = db.Legislations.Find(legislation.LegislationID);
if (savedLegislation.ShortTile.Any() && savedLegislation.ShortTile.ToString().Length >= 5)
{
string shortTitle = "This Act may be cited as the <i>" + savedLegislation.ShortTile.ToString() + "</i>.";
var provision = new Provision()
{
Article = Numbers.CountOrNull(savedLegislation.Provisions) + 1,
Proponent = savedLegislation.Sponsor,
Text = shortTitle
};
savedLegislation.Provisions.Add(provision);
db.SaveChanges();
}
}
I've been researching how SaveChanges() works and whether it is properly returning the updated entity, it does (since I get no issue looking it up in the second function). If it works properly, and the legislation is found and the provision is newly created in the second function, I don't see what is the "null" reference it keeps spitting out.
The null reference in this case would be savedLegislation.Provisions. The Provisions collection won't be initialized to a new List<Provision> when EF returns your Legislation instance from the db.Legislations.Find(...) method.
The first thing I'd try is something like this:
var savedLegislation = db.Legislations
.Include("Provisions")
.First(l => l.LegislationID == legislation.LegislationID);
... but I'd also consider just using the legislation instance that was passed into the method rather than fetching it from the database again.