I've seen other answers similar to mine but I'm having a hard time applying it to my case.
I've created a LINQ query that will search through two collections and give me a third collection of items from the second collection whose particular property doesn't match the same property from the first collection:
private void GetOnOffConflicts()
{
DataAccess da = new DataAccess();
if (OnOffConflictLayers != null && OnOffConflictLayers.Count != 0)
{
OnOffConflictLayers.Clear();
}
var onOffQuery = from target in TargetDrawingLayers
from source in SourceDrawingLayers
where target.Name == source.Name && target.OnOff != source.OnOff
select target;
ObservableCollection<LayerModel> q = new ObservableCollection<LayerModel>(onOffQuery);
OnOffConflictLayers = q;
}
Now that I have that third collection of conflicts I can run the following method to correct the conflicts:
private void FixOnOffConflictsClick()
{
if (OnOffConflictLayers != null && OnOffConflictLayers.Count != 0)
{
DataAccess da = new DataAccess();
foreach (LayerModel onOffConflict in OnOffConflictLayers)
{
foreach(LayerModel sourceLayer in SourceDrawingLayers)
{
if(onOffConflict.Name == sourceLayer.Name)
{
string desiredSetting = sourceLayer.OnOff;
da.FixLayerConflict(onOffConflict.Path, onOffConflict.Name, desiredSetting);
}
}
}
}
}
I would like to convert that nested foreach loop into a statement that is similar to my first LINQ query, but calls da.FixLayerConflict(); as part of the query.
Would that be a meaningful improvement over the nested foreach, and how could I go about doing it?
I have tried something like:
var fixOnOffQuery = from conflict in OnOffConflictLayers
from source in SourceDrawingLayers
where conflict.Name == source.Name && conflict.OnOff != source.OnOff
select new
{
da.FixLayerConflict(conflict.Path, conflict.Name, source.OnOff)
};
But I don't know enough about the LINQ syntax to create something that works.
I cannot add the fixing method to the original query, by the way, as it's part of the requirement of the program that the user review the conflicts before action is taken.
Instead of trying to do the method call within a LINQ query, I would recommend you remove your nested foreach by querying to the items you wish to call your function on, then using that collection within a single foreach.
Something like this would work:
var conflictsAndSources = from conflict in OnOffConflictLayers
from source in SourceDrawingLayers
where conflict.Name == source.Name && conflict.OnOff != source.OnOff
select new { Conflict = conflict, Source = source };
foreach(var conflictAndSource in conflictsAndSources)
da.FixLayerConflict(conflictAndSource.Conflict.Path, conflictAndSource.Conflict.Name, conflictAndSource.Source.OnOff);
Related
I've been trying to write a LINQ statement that will compare a property from two collections of the same type of object model and produce a third collection of just those where the OnOff property is different. I've only been using LINQ for about a week now so I'm still getting used to it.
My understanding is a join will combine two collections and discard any items from them that are exactly the same, so you don't have duplicates.
I got advice to try a Join to do this and I'm getting close but I'm not there yet.
My model has string properties for Name, OnOff, Color, etc... I am trying to match models in the collections by name, and then give me a the models where the OnOff property is different.
Written out what I need is, for each LayerModel in SourceDrawingLayers, look into every LayerModel in TargetDrawingLayers and see if you find one with a matching Name. If you do, check the OnOff property. If it is NOT equal, Add it to the OnOffConflictLayers collection.
I've tried doing this with nested foreach statements and from x from y LINQ queries but I get a ton of duplicates. That's when Join was suggested to me. I just need help taking it home.
so far I have this:
var onOffQuery = from source in SourceDrawingLayers
join target in TargetDrawingLayers
on source.Name equals target.Name
//give me every target where target.OnOff does not equal source.OnOff
select new { target.OnOff }; //I don't know if I need this line it's just where I'm stuck at right now.
when finished I have to populate a collection with the result:
ObservableCollection<LayerModel> q = new ObservableCollection<LayerModel>(onOffQuery);
OnOffConflictLayers = q;
I would greatly appreciate help with this (probably super simple) query I am trying to run. Thanks!
Update: See my edit below.
private void GetOnOffConflicts()
{
DataAccess da = new DataAccess();
if (TargetDrawingLayers != null && TargetDrawingLayers.Count != 0)
{
TargetDrawingLayers.Clear();
}
if (OnOffConflictLayers != null && OnOffConflictLayers.Count != 0)
{
AllConflictLayers.Clear();
}
foreach (TargetDrawingModel targetDrawingModel in TargetDrawings)
{
foreach (var result in da.GetDrawingLayers(targetDrawingModel.DrawingPath))
{
TargetDrawingLayers.Add(result);
//everything works fine up to here. My collection is populated correctly.
}
}
var onOffQuery = from source in SourceDrawingLayers
from target in TargetDrawingLayers
where source.Name == target.Name && source.OnOff != target.OnOff
select target;
ObservableCollection<LayerModel> q = new ObservableCollection<LayerModel>(onOffQuery);
OnOffConflictLayers = q;
}
Edit: Matt U's answer below is correct. I mistakenly had not set up my target drawing correctly and all of the on/off properties actually were the same, so no wonder my code returned nothing.
Marked Matt's answer as correct.
It sounds like you're looking for what's known as a non-equijoin. Instead of a join, you'll use two from clauses and a where.
var onOffQuery = from source in SourceDrawingLayers
from target in TargetDrawingLayers
where source.Name == target.Name && source.OnOff != target.OnOff
select target;
// onOffQuery should contain the "target" objects where there is a matching Name in source, but OnOff differs.
// You can "select source" if you want the source objects
More on non-equijoin and other custom "joins": https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-custom-join-operations
I am trying to make a list of class objects that I gather from a database. I am able to use a Linq-to-SQL class in order to use Linq to make a list of class objects.
I am able to look up things in the list however it fails when I look up something that is not in the list (null error exception). I would like to test for null or somehow figure out if my item is not in the list but
q.Find(x => (x.fid1 == "abc123")).fid1 = "9454a3" == null
generates a null error exception. This is my first time using the Linq-to-SQL class so any input as to how I am using it would be welcome as well.
class TestClass
{
public class FIDClass
{
public System.String fid1 { get; set; }
public System.String fid2 { get; set; }
public System.Int32 fid3 { get; set; }
}
static void Main()
{
// linq to SQL class
DataClasses1DataContext db = new DataClasses1DataContext();
var q = (from dbfile in db.GetTable<DataTable>()
where dbfilefid3 == 15
select new FIDClass
{
fid1 = dbfile.fid1,
fid2 = dbfile.fid2,
fid3 = (int) dbfile.fid3
}).ToList<FIDClass>();
// if it's found, the following works fine
// if it's not found, I get a null error exception on the if stmt
if (q.Find(x => (x.fid1 == "abc123")).fid1 == "abc123" )
{ MessageBox.Show("Found");
}
}
}
Any help, thoughts, feedback would be greatly appreciated
On my iPad, so can't test this out.
Depending on version of .net you could try null coalesce
if (q.Any(x => (x?.fid1 == "abc123")) )
Otherwise,
if (q.Any(x => x!=null && (x.fid1 == "abc123"))
You should check for null as below:
if (q.Find(x => x.fid1 == "abc123") != null )
{ MessageBox.Show("Found");
}
else
{ MessageBox.Show("NOT Found");
}
Please mark as answered if that solved your problem.
First of all it's 2017 don't use Linq to SQL it's been depreciated for a while now.
Second your query is not optimized at all. You are pulling all the data from the server with the ToList method that is bad what you want to use is IQueriables to do the query operations.
And the problem you are having is that the find method returns a null if no object is found so use the Any method if you just want to check if there is an object that satisfies the condition Any((x)=>x.fid1 == "abc123")
I already use linq but search id only and it worked perfectly
var obj = (from VAR in db.infos
where VAR.id == 22
select new
{
title = VAR.title,
}).SingleOrDefault();
Label2.Text = obj.title.Trim();
If I try to search by location get a error
var obj = (from VAR in db.infos
where VAR.location.Trim() == "Lim"
select new
{
title = VAR.title.Trim(),
}).SingleOrDefault();
SearchJob.Items.Add(obj.title.Trim());
Label2.Text = obj.title;
Have a error in label2 line
Object reference not set to an instance of an object.
How do I fix it?
if (obj.title != null)
{
SearchJob.Items.Add(obj.title.Trim());
Label2.Text = obj.title;
}
Object reference not set to an instance of an object.
SOLUTION
Change SingleOrDefault() to FirstOrDefault()
You're doing some nasty stuff there, VERY bad habits. For instance this:
var obj = (from VAR in db.infos
where VAR.location.Trim() == "Lim"
select new
{
title = VAR.title.Trim(),
}).SingleOrDefault();
SearchJob.Items.Add(obj.title.Trim());
Label2.Text = obj.title;
Is a nonsense! Let me tell you why:
Always check the data BEFORE you insert it into your database, not AFTER. You're creating a lot of unnecessary overhead this way, which could be avoided altogether. Trim the data before, never after.
Next thing - you need only a single string value, yet you create an anonymous object. WHY? Do this instead:
string title = (from o in db.infos
where o.location == "Lim"
select o.title).SingleOrDefault();
Use SingleOrDefault if you expect a single result or none. However, if you expect multiple results and want only the first, use FirstOrDefault.
As you can see, I'm using o instead of VAR. It's true it doesn't really matter that much, BUT, it's never a good idea to use a word that's very similar to one of the reserved words (var).
If you get an exception Object reference not set to an instance of an object., it means that your query returned a null and you're trying to access a non-existing object. If your query may return null at some point, always check for null when accessing a member!
EDIT
if ( obj.title != null )
is bad too, because you need to check for null the object itself!
if (obj != null)
if you really want to use your bad approach.
I think the error occurred in the query
In first query you have source db.infos
In second you have source db.jobinfos
The source is changed
If we assign empty text to Label it will show, It looks like that obj.title does not exist or you are getting error in your query due to wrong source.
The obj is not returning title field. Check obj by debugging.
Try to skip exception :)
public static class LINQException {
public static void TryExample() {
var LsWithEx = from p in Enumerable.Range(0, 10) select int.Parse("dsfksdj");
var LsSkipEx = (from p in Enumerable.Range(0, 10) select int.Parse("dsfksdj")).SkipExceptions();
}
public static IEnumerable<T> SkipExceptions<T>(this IEnumerable<T> values)
{
using (var enumerator = values.GetEnumerator())
{
bool next = true;
while (next)
{
try
{ next = enumerator.MoveNext();}
catch
{ continue;}
if (next) yield return enumerator.Current;
}
}
}
}
var obj = (from VAR in db.infos
where VAR.location.Trim() == "Lim"
select new
{
title = VAR.title.Trim(),
}).SkipExce.SingleOrDefault();
Example
Have a look at the following code:
private void DeDuplicateOrganisations()
{
var profileOrgs = _organisations.Where(o => o.ExistsInProfile).ToList();
var kvkOrgs = _organisations.Where(o => !o.ExistsInProfile).ToList();
profileOrgs.ForEach(o =>
{
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
_organisations = profileOrgs.Concat(kvkOrgs).OrderBy(o => o.Title).ToList();
}
In this example the property CompanyInfoOrganisation (simply a get; set; property) is copied when an organisation is considered a duplicate. This all works as expected, duplicates are nicely deduplicated.
Also this is true inside this message:
_organisations.First(o => o.ExistsInBoth).CompanyInfoOrganisation != null;
Problem
Now I bind the _organisations list to a listbox
lbxCompanies.DataSource = null;
lbxCompanies.DataSource = _organisations;
lbxCompanies.DisplayMember = "Title";
lbxCompanies.SelectedIndex = -1;
and later on get the selected value:
var org = lbxCompanies.SelectedValue as Organisation;
gbxCompanyInfo.Visible = org != null;
if (gbxCompanyInfo.Visible)
if (org.CompanyInfoOrganisation != null)
// NEVER GETS HERE (but gbxComanpyInfo is visible)
If I try to read the CompanyInfoOrganisation property I always get null while I know the property was set.
Question
What is happening here? How come the property reference is destroyed? How can I prevent this from happening?
The reference you're using only has immediate scope and as soon as the query ends it exits scope and your reference disappears. So when you bind later, the reference is exactly right -- null.
profileOrgs.ForEach(o =>
{
// Right here -- var duplicate has scope ONLY within your query.
// As soon as the query is executed it leaves scope and the reference
// pointer will be null
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
Because you're using a class, you need to perform a deep MemberwiseClone on it to get a NEW copy of the object:
o.CompanyInfoOrganisation = (YourInfoType)duplicate.CompanyInfoOrganisation.MemberwiseClone();
When you load the data, load the CompanyInfoOrganisation property along with the root entity; that way it will be already loaded into memory. If using LINQ to SQL, you load via DataLoadOptions, and pass this to the context. If using Entity Framework, you use the Include method in the LINQ query.
It might have to do with capturing of variables inside the lambda. Try substituting the .ForEach to a regular foreach().
Or maybe the CompanyInfoOrganisation in duplicate was null to begin with.
The problem was I used string.Join() to show the values, and the first value to join was null (which is really annoying), resulting in an empty string, leaving me thinking the property was null. However it turned out the property was not null, but has a perfectly valid reference to the object needed. Using the debugger with a little more care would have saved me an hour or so...
Sorry!
I've been trying to get the following method cleaned up using more sensible and lean syntax, but I'm striking serious headaches when it comes to aggregate clauses and filtering using L2S. Particularly, I feel I should be able to use a .Contains() method to filter out objects whose tags fit the string parameter passed in the method, but it hasn't worked.
public TagListViewModel GetTagModel(string Name)
{
var model = new TagListViewModel();
var repo = new SimpleRepository("Wishlist");
var ideas = repo.All<Idea>();
List<Idea> ideaList = new List<Idea>();
foreach (Idea i in ideas)
{
var query = from tag in repo.All<Tag>()
join ideatag in repo.All<IdeaTag>()
on tag.ID equals ideatag.TagId
where ideatag.IdeaId == i.ID
select tag;
i.Tags = query.ToList<Tag>();
ideaList.Add(i);
}
foreach (Idea i in ideaList)
{
var query = from vote in repo.All<IdeaVotes>()
where vote.IdeaId == i.ID
select vote;
i.Votes = query.ToList<IdeaVotes>();
}
// Here begins the problem area. I should be able to get a tag from the repo
// whose name matches the "Name" parameter and then call a .Contains() method to
// filter this list, shouldn't I?
List<Idea> filteredTagList = new List<Idea>();
foreach (Idea item in ideaList){
foreach(Tag t in item.Tags)
{
if (t.Name == Name)
filteredTagList.Add(item);
}
}
model.Ideas = filteredTagList;
return model;
}
It's ugly. I know it's ugly but after over 2 hours of playing with several preferred variations I still can't get it to filter the way it's supposed to. Where am I going wrong?
This should be equivalent assuming there are no duplicate tags on a single Idea.
model.Ideas = ideaList.Where(
idea => idea.Tags.Any(
tag => tag.Name == Name)).ToList();