(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.
Related
I have two large set of collection List<Class> with hundreds of properties.
e.g. Original Collection List<OriginalCollection> and Updated Collection List<UpdatedColleciton>
UpdatedCollection will contain value in certain columns which most probably will not be part of OriginalCollection and UpdatedCollection might have certain KeyColumn [ID Column] which might not be part of OriginalCollection, and I'm receiving thousands of data set in OriginalCollection and UpdatedColletion will increase in records over a period of time.
I do have a requirement where only null or empty column of OriginalCollection should get replaced with matching UpdatedCollection value by ID and if no matching ID is available then those records should get added in OriginalCollection from UpdatedCollection.
I tried with AutoMapper, where I tried to Update OriginalCollection with UpdatedCollection based on matching ID, for which I'm unable to find any AutoMapper configuration for my sets of above mentioned requirement.
I'm looking for most effective solution which should not impact on performance, thats why I did not go threw typical way of Union and Intersection, as Modal have hundreds of property and thousands of records are there, and as I do have plenty of properties I think library like AutoMapper would be good option than writing logic in loop to check value on each column for all thousands of record.
Please suggest any better and performance efficient solution like AutoMapper Configuration or any other .Net inbuilt feature to achieve this scenario.
I also checked with AutoMapper.Collection as below from https://github.com/AutoMapper/AutoMapper.Collection
cfg.CreateMap<OrderItemDTO, OrderItem>().EqualityComparison((odto, o) => odto.ID == o.ID);
Mapping OrderDTO back to Order will compare Order items list based on if their ID's match
Mapper.Map<List<OrderDTO>,List<Order>>(orderDtos, orders);
but it has below behavior and not working as expected to my requirement
If ID's match will map OrderDTO to Order
If OrderDTO exists and Order doesn't add to collection Not Working for me
If Order exists and OrderDTO doesn't remove from collection Not Working for me
AutoMapper is known mapping library and has good documentation as well, but unable to find similar detaild documentation for AutoMapper.Collection, I've explored AutoMapper.Collection but it was not providing solution as per my requirement.
So, I need to go traditional way.
Prepared differences of collection by LINQ query.
var common = original.Where(x => revised.Exists(z => z.ID == x.ID)).ToList();
var nonCommon = revised.Where(x => !original.Exists(z => z.ID == x.ID)).ToList();
foreach(var item in common)
{
var derived = revised.FirstOrDefault(x => x.ID == item.ID);
// Added Extention Method to compare and update property
var data = item.UpdateProperties(derived);
}
common.AddRange(nonCommon);
Utilized Reflection to compare objects and update it's value after comparision on property level, for all datatypes.
public static T UpdateProperties<T>(this T source, T destination)
{
Type type = source.GetType(); // Gets Source Object Type
PropertyInfo[] props = type.GetProperties(); // Gets Source object Properties
foreach (var prop in props) // Iterate threw all properties of source object
{
var sourceValue = prop.GetValue(source); // Get source object value by Property Name
var destinationValue = prop.GetValue(destination); // Get destination object value by Property Name, to update source object
// Update source object property value only if derived object's property has value and source object doesn't
if (string.IsNullOrEmpty(sourceValue?.ToString()) && !string.IsNullOrEmpty(destinationValue?.ToString()))
{
prop.SetValue(source, destinationValue); // Update source object's property with value of derived object
}
}
return source;
}
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.
I'm using TweetInvi to grab a bunch of tweets that match a specified hashtag. I do this with the following:
var matchingTweets = Search.SearchTweets(hashtag);
This returns an IEnumerable (named ITweet, interface of Tweet), however I cannot create a List<> of Tweets, because Tweet is a static type.
I made, instead, a list of objects, using:
List<object> matches = matchingTweets.Cast<object>().ToList();
However, although each member of the matchingTweets IEnumerable has a number of properties, I cannot access them using:
long tweetID = matches[i].<property>;
Using matches[i].ToString() returns the tweet content, so how can I effectively cast the results in matchingTweets to a list, and subsequently access the properties of those list members? I would ideally like to avoid using dynamic.
In your example above you were trying to grab the ID from the tweet. ITweet implements ITweetIdentifier which contains the Id property. You can literally just access it by:
var matchingTweets = Search.SearchTweets(hashtag);
//Grab the first 5 tweets from the results.
var firstFiveTweets = matchingTweets.Take(5).ToList();
//if you only want the ids and not the entire object
var firstFiveTweetIds = matchingTweets.Take(5).Select(t => t.Id).ToList();
//Iterate through and do stuff
foreach (var tweet in matchingTweets)
{
//These are just examples of the properties accessible to you...
if(tweet.Favorited)
{
var text = tweet.FullText;
}
if(tweet.RetweetCount > 100)
{
//TODO: Handle popular tweets...
}
}
//Get item at specific index
matchingTweets.ElementAt(index);
I don't know exactly what you want to do with all the info, but since the SearchTweets returns a IEnumerable of ITweets you have access to anything an ITweet has defined.
I highly recommend looking through their wiki. It's pretty well organized and gives you clear examples of some basic tasks.
It makes sense you cannot access the properties. You cast it into object so you can only access the objects properties and methods (that like you said might have been overridden).
It should be fine to just access it like this:
List<ITweet> tweets = matchingTweets.Take(5).ToList();
What you can do is project it to a new object of yours:
var tweets = matchingTweets.Select(item => new {
property1 = item.property1,
property2 = item.property2
})
.Take(5).ToList();
Then you will be able to access what you need. Now, if you need to share this data outside the scope of that function create a DTO object and initialize it instead of the anonymous type.
Depending on the size of the project and amount of effort usually it is in any case a good practice to create a layer of DTO objects when you interact with an external service like this. Then if their models changed you can contain your changes only to the DTOs.
If all you want are the ids of the first 5 then:
var ids = matchingTweets.Take(5).Select(item => item.id).ToList();
I am assigning property names of a dynamic object as ints in string form. The int value represents an int ID in a database I am using. However I am stuck on how to retrieve the value assigned to the property as shown below:
dynamic test = new ExpandoObject()
IDictionary<string, object> proxyFiler = test as IDictionary<string, object>;
proxyFiler["four"] = 4;
proxyFiler["5"] = 5;
int r = test.four; // Works
int s = test.5; // Doesn't work
A method which reads the database will return an "int" and I would like to be able to access the property value with that property name.
To expand on this: what if I wanted to do a linq query to sort out a list of dynamic objects according to a property name? In this case I need to get the propertyName which I have retrieved as a string e.g. "15":
return disorderedList.OrderBy(o => o.propertyName).ToList();
Does anyone know a simple solution to this problem or do you recommend a different approach? Thanks.
In order for dynamic to work in this way, the key has to follow the rules for valid identifier names in C# (the rules are specified in this outdated MSDN page, but also in the C# language specification). A single number (5) is not an allowed identifier name, which is why that doesn't work.
Note that you can still retrieve the value by using the type as a dictionary, in a similar manner to how you populated it.
As for your second example - you are never using value, so it has no effect. It's the same as just writing int r = test.four;
Edit:
I believe, given your approach, you'd need to cast to a dictionary:
return disorderedList
.OrderBy(o => ((IDictionary<string, object>)o)[propertyName]).ToList();
Recently I asked a more general question about getting properties of model through foreign key.
Now I moved a bit further but still have no idea how transform objects on the fly.
What I've got is an IEnumerable collection which I get through repository
regionRaw = unitOfWork.RegionRepository.Get(
keyOrder: q => q.OrderBy(d => d.RegionID),
filter: p => p.FullName.Contains(lastname) || p.ShortName.Contains(lastname),
orderBy: jtSorting,
includeProperties: "District, ISO31662, GOST767Region");
Further I am going to export data from this collection to Excel. So I need a select statement that gets all the fields I need.
dt = regionRaw
.Select(x => new
{
ISO = x.ISO31662.GOSTName,
DistrictName = x.District.ShortName
})
I do not want to enumerate all the fields I need like on the top.
I am able to make a method that recognizes which of the fields have simple values and which have objects referenced through foreign key. And then that method will return a list of properties.
Now I need some way to write something like a foreach inside select. I see something like this:
dt = regionRaw
.Select(x => new
{
foreach (prop in propList)
{
prop.PropertyName = x.GetType()
.GetProperty(prop.TableName)
.GetValue(x, null).GetType()
.GetProperty(prop.PropertyName)
.GetValue(
x.GetType().GetProperty(prop.TableName).GetValue(x, null),
null);
}
}
Where propList is a collection of properties that I get before.
I do totally realize that upper code is more a pseudo-code but I have no idea how to realize this in .NET.
So if you can suggest some solution for this task I will be very grateful. Or maybe you could explain that all this is a bad idea and shouldn't be realized.
You wont be able to create an anonymous type with dynamic properties as anon types are created during compile and your properties are created during execution.
But why do you need strongly typed properties if you're not going to code against them, as you wont know them until someone executes the query?
Expando object may be of use to you?http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx