Object reference updating, update all collections property? - c#

Introduction
I found a very weird situation, so essentially I've a collection property called Matches and an object property called Match.
The Matches collections contains a list of item of Match, like this:
private ObservableCollection<Match> _matches = new ObservableCollection<Match>();
public ObservableCollection<Match> Matches
{
get { return _matches; }
}
this collection is valorized when the application start, infact, the software take some data from an Internet site and then, with a scraper fill the collection with the correspond object model Match.
Where the bug start
The Matches collection is binded to a DataGrid. When the user click on an element (Match), available on the DataGrid, the code fire the event SelectionChanged, inside this event I create a copy of the Match clicked, so I can use this object inside all my application:
var match = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
as you can see with the use of Linq, I check if the Match clicked by the user have the same link of a Match contained in the Matches collection, infact, each Match in the collection have a unique link like a GUID for example.
The bug
The Match object look like this:
private Match _match;
public Match Match
{
get { return _match; }
set
{
_match = value;
OnPropertyChanged();
}
}
and as I said it contains the Match clicked by the user. This object allow me to get the data from Internet only for this Match, from all methods inside my app. This working pretty well. Until now.
My application allow the user to apply some filters, essentially the user press a button and then the Match saved is updated with the property filled by the user, for example:
Match.League.Rounds = availableRounds;
this code cause the bug, but I'll try to explain the bug better later, I need to explain a bit what happen here.
Essentially the current Match saved in the application should update only the own property League.Rounds, this property is a list of Rounds available for this Match, the structure is very simple:
public class Round
{
public int Id { get; set; }
public string Name { get; set; }
}
the update working good but, the line Match.League.Rounds = availableRounds; update also all the property League.Rounds available in the objects collection Matches.
I don't understand why happen this, I've not created a reference of the object clicked:
var match = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
Practice example of what's happening
before filters applied
Matches Collection
Match.Leagues.Rounds[0] contains Id 10 and Name foo
after filters applied
Matches Collection
Match.Leagues.Rounds[0] contains Id 11 and Name foofoo
but it should not be modified, only the Match should be modified.
but a new object. Someone could explain how to fix this? Best regards.

I've not created a reference of the object clicked
Yes, you have. This does not create a new Match object:
var match = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
It gets a reference to the already existing Match object in the Matches collection.
If you want to create a new object, you should use the new operator:
var existing = controller.Matches.FirstOrDefault(c => c.MatchLink == ((Match)Matches.SelectedItem).MatchLink);
Match match = new Match();
//set all properties of the new object...
match.Prop1 = existing.Prop1;
Also note that you need to create new Round objects as well. You should consider implementing the IClonable interface.
The FirstOrDefault() method doesn't clone the object for you.

Related

Determine is a FamilyInstance is visible in a View

I have a FamilyInstance pFam and a Autodesk.Revit.DB.View pView. I want to know if pFam is visible in pView. I've tried using
if (pFam.IsHidden(pView)
continue;
Unfortunately, this only tells me if the element is supposed to be hidden, which isn't the case. But, the element is not visible in every View, and under that circumstance, I want (or rather don't want) something to happen. There is no Visible or IsVisible properties for FamilyInstances... Does anyone know a way to handle these situations?
Thanks!
I have found that the most reliable way of knowing whether an element is visible in a view is to use a FilteredElementCollector specific to that view. There are so many different ways of controlling the visibility of an element that it would be impractical to try to determine this any other way.
Below is the utility function I use to achieve this. Note this works for any element, and not just for family instances.
public static bool IsElementVisibleInView([NotNull] this View view, [NotNull] Element el)
{
if (view == null)
{
throw new ArgumentNullException(nameof(view));
}
if (el == null)
{
throw new ArgumentNullException(nameof(el));
}
// Obtain the element's document
Document doc = el.Document;
ElementId elId = el.Id;
// Create a FilterRule that searches for an element matching the given Id
FilterRule idRule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.ID_PARAM), elId);
var idFilter = new ElementParameterFilter(idRule);
// Use an ElementCategoryFilter to speed up the search, as ElementParameterFilter is a slow filter
Category cat = el.Category;
var catFilter = new ElementCategoryFilter(cat.Id);
// Use the constructor of FilteredElementCollector that accepts a view id as a parameter to only search that view
// Also use the WhereElementIsNotElementType filter to eliminate element types
FilteredElementCollector collector =
new FilteredElementCollector(doc, view.Id).WhereElementIsNotElementType().WherePasses(catFilter).WherePasses(idFilter);
// If the collector contains any items, then we know that the element is visible in the given view
return collector.Any();
}
The category filter is used to eliminate any element not of the desired category before using the slower parameter filter to find the desired element. It is probably possible to speed this up further with clever usage of filters, but I have found that it is plenty fast enough for me in practice.
If you don't have ReSharper, delete the [NotNull] annotations you see.

Show Hide divs in views based on boolean value

I'm currently working on an app using Asp.Net MVC and C#. One of the requirement is to check what process the item is and then to only show the appropriate div. So I've decided to create a Table in the Db which consist of:
Id ProcessDescription DivOneVisible DivTwoVisible
1 Approved True False
2 Analysis True True
...
NOTE - The Id's and ProcessDescription will never change
Currently the table only holds 10 rows of data but the idea is, in future more rows/columns can be added.
I then go ahead and create the appropriate methods, one for each Div as follows
public bool ShowDivOne(int id)
{
var data = uow.GetRepository<ItemProcess>().GetById(id);
bool showDivOne = data.DivOneVisible.HasValue ? data.DivOneVisible.Value : false;
if (showDivOne)
return true;
else
return false;
}
I use the same code as above for ShowdivTwo() method, but match the different column. Then in the view I do
#if(ShowDivOne){//div one code here}
#if(ShowDivTwo){//div two code here}
This works but I was wondering if there is a more generic way where I can write one method which will cover each scenarios even if new columns or rows are added.
The main thing you still need to have a mapping between Database and ViewModel somewhere. At the moment it is hard coded in your methods.
You can make it absolutely generic if you start use reflection and have a mapping array with properties name. But I would nt recommend doing it as it over complication and hard to maintain and change.
(If you want I can go into details of implementation).
For you example I would suggest to have a viewmodel per item, that contains properties of divs to display.
public class ProcessViewModel
{
public int Id {get;set;}
public bool ShowDivOne {get;set;}
public bool ShowDivTwo {get;set;}
ProcessViewModel(){}
ProcessViewModel(ItemProcess data){
Id = data.Id;
ShowDivOne = data.DivOneVisible.HasValue ? data.DivOneVisible.Value : false;
ShowDivTwo = data.DivTwoVisible.HasValue ? data.DivTwoVisible.Value : false;
}
}
You still query for each item individually or query them altogether and pass data to viewmodel to construct it.
And a simple foreach on a view to traverse through the list of viewmodels.
Extending it to contain more properties would be very easy and strait forward, with minimum code to maintain.

IEnumerable.GroupJoin and Entity Framework objects

(See this question for a little background on this one.)
I need to match up items in a List<T> collection of Entity records with a List<T> collection of objects (the Entity object collection is of type Citation and the other is of type sRecord). There is a 1-to-1 relationship of sorts between the second collection and the first where each of the objects in the second match up to exactly one record in the first (but not necessarily vice versa). They match on a single field called ID in the Citation class and id in the sRecord class. Trying to run through nested loops to match them up quickly became bogged down, so I sought out a means to match up the entire collections once and then iterate through that matched set.
This is how I put together the suggested group join statement:
var draftMatches = draftRecords.GroupJoin(sRecords,
Citation => Citation.ID,
stiRecord => sRecord.id,
(Citations, sRecords) =>
new
{
Citation = Citations,
sRecord = sRecords.Select(sRecord => sRecord)
});
I don't have much confidence that I got it right.
What I need to do with the resulting matched set is compare fields in the sRecord object to fields in the Citation object to determine whether the Citation object needs to be updated. Since I'm working with Entity, I assumed that I needed to preserve the reference to the Citation object when I updated it in a separate method (separated for code reuse purposes), so this is how I'm trying to do that update:
DateTime recordDate = RecordUtilities.ConvertToDate(match.sRecord.FirstOrDefault().modifiedDate);
int comparison = recordDate.CompareTo((DateTime)match.Citation.modifiedDate);
if (comparison > 0)
{
EntityUtilities.MapToCitation(ref match.Citation, match.sRecord.FirstOrDefault());
updatedDraft++;
}
At this point, I'm getting an IntelliSense error on match.Citation in the call to MapToCitation, stating "A property, indexer, or dynamic member access may not be passed as an out or ref parameter."
I need to ensure that the Citation object that gets saved when I do context.Save() is updated from the sRecord object if appropriate. I also need to make sure that I'm saving time with the matching over the previous code. What do I need to change about my code to achieve these goals?
var draftRecordDic = draftRecords.ToDictionary(record => record.ID);
var sRecordsDic = sRecords.ToDictionary(record => record.ID);
var validKeys = sRecordsDic.Keys.Intersect(draftRecordDic.Keys);
foreach(var key in validKeys)
{
var recOriginal = draftRecordDic[key];
var recMo = sRecordsDic[key];
// do your code here.
}
This should work nicely & is simple to understand.

List<Type> Remove

Somebody explain to me this:
I am trying to delete items from a list with matching ids contained in another list of strings.
Step 1 is as below:
I'm trying to delete Items from myListingSyncIDs where the ListingNumber matches ListingNumbers in lstListingsUpdatedIn24Hrs.
The item at [0] Equals a value from lstListingsUpdatedIn24Hrs, as shown in Step 2:
But as shown in Step3: The Remove fails:
Then After doing a RemoveAll(func) Step4: The Remove works
Somebody explain why the Remove(item) doesn't work, Please ...
Code:
myListingSyncIDs.AddRange(myListingSync.Listings);
#region Remove Listing References Fetched In The Last 24Hrs
// Listing References Fetched In The Last 24Hrs
// These will be excluded to optimise the running of the App.
// Basically meaning that a complete sync of all listings
// will only be done once every 24hrs
// So that if this is run every hr, it will not slow down the most recent additions
List<String> lstListingsUpdatedIn24Hrs = DAL.PropertyPortalDAL.GetSahtWebserviceUpdatesIn24Hrs();
List<P24SyncService.ListingSyncItem> myListingsUpdatedIn24Hrs =
lstListingsUpdatedIn24Hrs.Select(p => new P24SyncService.ListingSyncItem()
{
ListingNumber = p,
Status = P24SyncService.ListingState.AddedModified
}).ToList();
foreach (P24SyncService.ListingSyncItem myLSI in myListingsUpdatedIn24Hrs)
{
myListingSyncIDs.Remove(myLSI);
}
myListingSyncIDs.RemoveAll(p => lstListingsUpdatedIn24Hrs.Contains(p.ListingNumber));
#endregion
ListingSyncItem is:
public partial class ListingSyncItem {
private string listingNumberField;
private ListingState statusField;
/// <remarks/>
public string ListingNumber {
get {
return this.listingNumberField;
}
set {
this.listingNumberField = value;
}
}
/// <remarks/>
public ListingState Status {
get {
return this.statusField;
}
set {
this.statusField = value;
}
}
}
At a guess, your ListingSyncItem type doesn't override Equals, so List<T>.Remove doesn't know that the item to remove is "equal" to the item in your list.
Simply overriding Equals and GetHashCode appropriately (to check for the equality of ListNumber and Status, presumably, and build a hash code based on those) should fix the problem.
For RemoveAll, you're providing a predicate. That doesn't use ListingSyncItem.Equals, which is why it's working.
I can't be sure without seeing the definition of ListingSyncItem, but I'm guessing this has to do with the fact that you have two instances referring to the same item.
You know that two different instances with the same ListingNumber refer to the same conceptual object, but the List<> doesn't. By default, .NET knows two objects are identical if they share the same instance, but since you're creating a new ListingSyncItem in your internal lambda function, it's not being removed.
What you should do is implement IEquatable in your ListingSyncItem class and make it return True for two objects with the same ListingNumber. Then List will know to remove the right items from the list.
As stated, it is because you arent overriding the Equals.
By default, it will check for ref equality. Which, obviously, isnt the case here.
Either override Equals and GetHashCode or use some way of getting the correct reference (lambdas for instance)

How to programmatically populate Sitecore items (Add item and fields)?

I'm now battling with adding items via C# to Sitecore database.
The code below executes correctly, however the items aren't being created.
Also, I noticed, that the item["FieldName"]=value; syntax doesn't actually populate the Fields collection.
And Fields collection on the item seems read only, so I can't just call .Add on it (such method doesn't exist).
So - what is the correct way of creating a child item and populating its fields?
I am using the Master database for both the Sitecore backend and this code.
The code I use below:
using (new Sitecore.SecurityModel.SecurityDisabler())
{
Database db = Factory.GetDatabase(this.Database);
foreach (var vacancy in Articles.Tables[0].Rows)
{
var rootItem = db.GetItem(this.RootItem);
DataRow dr = (DataRow) vacancy;
var newItem = rootItem.Add(string.Format("{0} {1}", dr["numericID"], dr["job_name"]),
db.GetTemplate(new ID("{GUID}")));
newItem.Editing.BeginEdit();
newItem["Job Title"] = dr["job_name"].ToString();//
newItem.Editing.EndEdit();
}
}
More info:
newItem.Template.Fields returns a collection with 100 fields
newItem.Fields returns a FieldCollection with only 9 elements in it.
When I pass through the code newItem["field"].Value = value; it does not increment the newItem.Fields collection count.
Of course the "field" key is consistent with ones present in newItem.Template.Fields[x].Name.
1) Check some things first f.ex:
assing the template to a variable and check what you get there.
and better don't do it by ID rather by path:
var templateItem = db.GetTemplate("yourTemplatePath");
now check whether that is the template you want?
make sure it's published (it can always cause some inconsistencies)
2) As to the fields not being 'visible', have you tried: item.Fields.ReadAll()
3) What do you mean by "items not being created"? how would you check that?
4) Also - are you sure that this.Database == "master" ?
I would recommend two changes:
(1) The item naming approach:
var newItem = rootItem.Add(ItemUtil.ProposeValidItemName(string.Format("{0} {1}", dr["numericID"], dr["job_name"])), db.GetTemplate(new ID("{GUID}")));
This change will handle invalid characters in the proposed name from your other data source.
(2) The field value setting approach:
newItem.Fields["Job Title"].Value = dr["job_name"].ToString();
This will set the raw value of the field to the provided string.
I would suggest setting the field value as
newItem.Fields["Job Title"].Value = dr["job_name"].ToString();//
Everything else looks ok.

Categories

Resources