EF6 - Adding new associated child not populating Navigation Properties - entity-framework

I have the following code which adds members to a parent group. The issue I'm running into is that the LAST member added to the PCSGroupMembers collection will not load it's navigation properties on demand. I have a set of 3 I've tried adding in various orders... the first 2 always work, the last one won't load the nav properties.
In the case below, you'll see 6 members total. The last 3 are the ones I just added in this request are the SAME IDS as the first 3 (unique key is groupid, memberid, startdate), so I know for sure there isn't an issue with a "bad FK" or similar. The first two of the new members are building out their nav properties, but that last one never does. Doesn't matter if I'm adding 2, 3, 4, etc. Last one never populates.
What seems REALLY odd to me is the PcsGroupID is an empty Guid on that last one even though it got added to the group via group.PcsGroupMembers.add(pcsMember) in the code below.
Edit: If you see the block labeled Hack, however, the real final member now has it's Nav properties. The faked temp member is then removed from the collection (and context) and everything magically works. So....WAT?
Code to add new members
//expire all existing members ([0..2] in this example)
group.PCSGroupMembers
.Where(m => m.EndDate == null)
.ToList().ForEach(m => {
m.EndDate = DateTime.Now.Date;
});
//add new members from request ([3..5] in this example)
foreach (PcsGroupMemberAddViewModel member in requestViewModel.Members) {
PCSGroupMember pcsMember = db.PCSGroupMembers.Create();
pcsMember.PCSSplitID = Guid.NewGuid();
pcsMember.ProjectProtocolID = member.ProjectProtocolID;
pcsMember.SplitAmount = member.Split;
pcsMember.IsPrimary = member.IsPrimary;
pcsMember.StartDate = DateTime.Now.Date;
group.PCSGroupMembers.Add(pcsMember);
}
Second to last (index 4)
Last Child (index 5)
Hack
var tempmember = db.PCSGroupMembers.Create();
//this Add seems to trigger the previous 'real' item to load it's Nav properties.... ???
group.PCSGroupMembers.Add(tempmember);
group.PCSGroupMembers.Remove(tempmember); //remove from collection
db.PCSGroupMembers.Remove(tempmember); //remove from context

Related

Added row moves to last position once filter is removed

In NatTable I am adding a row in filtered table. After removing the filter, the newly added row moves to last position in the table.
But I want to it to stay in the same position, that is next to the row which I added when the table is filtered.
I am currently using the RowInsertCommand. I don't want to add row via model or list which used to populated the table. I want to achieve only via NatTable commands. Is it possible?
It is always hard to follow the explanations and the issue without example code. But I assume you simply copied code from the NatTable examples, so I will explain your issue based on that.
First, the RowInsertCommand has several constructors. If you are using a constructor without a rowIndex or rowPositionparameter, the new object will ALWAYS be added at the end of the list.
When using the filter functionality in NatTable with GlazedLists, the list that is wrapped in the body DataLayer is the FilterList. If you are operating on the FilterList for calculating the rowIndex where the new object should be added and have the FilterList as base list in the RowInsertCommandHandler, the place where the new object is added is transformed between the FilterList and the base EventList, which might not be the desired result.
To solve this you need to create the RowInsertCommandHandler by using the base EventList.
EventList<T> eventList = GlazedLists.eventList(values);
TransformedList<T, T> rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);
SortedList<T> sortedList = new SortedList<>(rowObjectsGlazedList, null);
this.filterList = new FilterList<>(sortedList);
this.baseList = eventList;
bodyDataLayer.registerCommandHandler(new RowInsertCommandHandler<>(this.baseList));
The action that performs the add operation then of course needs to calculate the index based on the base EventList. The following code is part of the SelectionAdapter of an IMenuItemProvider:
int rowPosition = MenuItemProviders.getNatEventData(event).getRowPosition();
int rowIndex = natTable.getRowIndexByPosition(rowPosition);
Object relative = bodyLayerStack.filterList.get(rowIndex);
int baseIndex = bodyLayerStack.baseList.indexOf(relative);
Object newObject = new ...;
natTable.doCommand(new RowInsertCommand<>(baseIndex + 1, newObject));

Add or remove item in datagrid does not trigger WhenAnyPropertyChanged

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.

CheckboxTreeviewer previously set checked elements after reload

I have used JFace CheckboxTreeviewer and added ICheckStateListener to get result of checked elements.
Code is as follows
private HashSet<Object> checkElement=new HashSet<Object>();
checkboxTreeViewer.addCheckStateListener(new ICheckStateListener() {
#Override
public void checkStateChanged(CheckStateChangedEvent event) {
String childNode=null;
String rootNode=null;
Object changed = event.getElement();
if(changed instanceof ChildFacetNodeVo){
checkElement.add(changed);
ChildFacetNodeVo childFacetNodeVo=(ChildFacetNodeVo)changed;
childNode=childFacetNodeVo.getLabelFacet();
rootNode=childFacetNodeVo.getParent();
//here to get a new result after element checked and put new result to checkboxtreeviewer
List<RootFacetNodeVo> facetNodeVos=createFacetFilter(rootNode,childNode);
if(facetNodeVos!=null){
checkboxTreeViewer.setInput(facetNodeVos);
checkboxTreeViewer.expandToLevel(3);
checkboxTreeViewer.setCheckedElements(checkElement.toArray());
}
}
}
});
Now what i need is when i checked the new element the previously checked element should not get unchecked.
when i set new input in CheckboxTreeviewer nothing is visible.
So how do i set prevously checked element in CheckboxTreeviewer.
for example
p1
-----A1 - previous checked
-----A2
-----A3
-----A4
-----A5
p1
-----A6
-----A7
-----A8 - previous checked
-----A8
-----A9
p1
-----A10
-----A11
If you call setCheckedElements you must include everything that should be checked in the array you specify.
You appear to be creating a new set of objects to go in the tree so you will have to work out which of these new items needs to be checked.
You can call checkboxTreeViewer.getCheckedElements() to get the old checked elements (do this before setting the new input). This should help you work out which of the new elements need to be checked.

Which is better for database seeding: Add or AddOrUpdate?

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;
*/
}

NullReferenceException while trying to including a one-to-many relationship item after saving parent

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.