I know that in sql you do something like this
WHERE 'val' IN (field1, field2, field3, field4, ...)
I was wondering if there is a way doing something similar using Linq to entities? The only thing I can think of right now is just to create a giant "or" statement of the fields I want to search over like the following
.where(m =>
m.field1.Contains('val') ||
m.field2.Contains('val') ||
m.field3.Contains('val') ||
m.field4.Contains('val'));
Is there a cleaner way of writing this search or is what I have as good as it gets?
You are not using Contains() properly as pointed out by Theodor Zoulias, because IN on SQL checks equality, while contains would be LIKE on SQL. also you are enclosing your string val with ' instead of ", ' only works for a single character.
Assuming you are trying to retrive "m" where any property has a certain value, you're going to have to use reflection:
First create a method to loop trough an object and match the desired value
public bool FieldSearch(object a, string b)
{
//Get the type of your object, to loop through its properties
Type t = a.GetType();
//loop and check (the loop stops once the first property that matches has been found!)
foreach(PropertyInfo p in t.GetProperties())
{
if(p.GetValue(a).ToString()==b)
{
return true;
}
}
return false;
}
Be careful with GetProperties(), you might want to add BidingAttributes because it retrieves every(public) property.
Now just use your new bool method on your linq: (not a good idea regarding performance depending on the context)
.where(m => FieldSearch(m,"val"))
Although all of this is possible, you probably have an architecture problem, because you are going to lose reference very quick, since this linq query returns any object that has that value on any field; without specifying which field.
There are probably better ways to do what you are trying to do..
You can do
.Where(f => new string[] { f.field1, f.field2, f.field3 }.Any(s => s.Contains("val")));
which have the behavior of the code you posted, or
.Where(f => new string[] { f.field1, f.field2, f.field3 }.Contains("val"));
which check for equality.
But I can't say if it's a good idea regarding performance.
Here is an example of the code:
public class ClassWithFields
{
public int Id { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 {get;set;}
}
public class Program
{
public static void Main()
{
var listFields = new List<ClassWithFields>()
{
new ClassWithFields { Id = 1, Field1 = "val", Field2 = "qewr", Field3 = "asdqw" },
new ClassWithFields { Id = 2, Field1 = "asdf", Field2 = "asdd", Field3 = "asdqw" },
new ClassWithFields { Id = 3, Field1 = "asdf", Field2 = "qewr", Field3 = "qwvaleqwe" }
};
var containsVal = listFields.Where(f => new string[] { f.Field1, f.Field2, f.Field3 }.Any(s => s.Contains("val")));
var equalsVal = listFields.Where(f => new string[] { f.Field1, f.Field2, f.Field3 }.Contains("val"));
}
}
You can run it at https://dotnetfiddle.net/lXSoB4
Related
Here I've selected the record which a want to get into a new variable. and after that, when I'm changing the value of 'itm.note' it's automatically modifying the original list (Obj.Testlist) as well. How can this be avoided? Only want to change 'itm' object and Obj.Testlist list should be keep as it is.
var itm = Obj.Testlist.Where(x => x.id == 1).SingleOrDefault();
itm.note = "text";
You could clone the fetched item, but that wouldn't be efficient.
How about this:
var changedFetchedItem = Obj.Testlist.Where(x => x.id == 1)
.Select(x => new
{
Id = 1,
Note = "text",
// copy the other properties that you plan to use
// don't copy the ones that you don't use.
}
.SingleOrDefault();
Since you will be fetching at utmost one item, this is very efficient.
I chose to create an object with anonymous type. If you need a specific type, you can add the type after the keyword new
Since I have large model, decided to use cloning for this (Sample code),
public class TestClone : ICloneable
{
public bool IsSuccess { get; set; }
public string Note { get; set; }
public string ErrorDetail { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
static void Main(string[] args)
{
var test1 = new TestClone() { IsSuccess = true, Note= "text", ErrorDetail = "DTL1" };
var test2 = (TestClone) test1.Clone();
test2.Note= "new text";
}
Say I have the following class structures
public class EmailActivity {
public IEnumerable<MemberActivity> Activity { get; set; }
public string EmailAddress { get; set; }
}
public class MemberActivity {
public EmailAction? Action { get; set; }
public string Type { get; set; }
}
public enum EmailAction {
None = 0,
Open = 1,
Click = 2,
Bounce = 3
}
I wish to filter a list of EmailActivity objects based on the presence of a MemberActivity with a non-null EmailAction matching a provided list of EmailAction matches. I want to return just the EmailAddress property as a List<string>.
This is as far as I've got
List<EmailAction> activityTypes; // [ EmailAction.Open, EmailAction.Bounce ]
List<string> activityEmailAddresses =
emailActivity.Where(
member => member.Activity.Where(
activity => activityTypes.Contains(activity.Action)
)
)
.Select(member => member.EmailAddress)
.ToList();
However I get an error message "CS1503 Argument 1: cannot convert from 'EmailAction?' to 'EmailAction'"
If then modify activityTypes to allow null values List<EmailAction?> I get the following "CS1662 Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type".
The issue is the nested .Where it's returning a list, but the parent .Where requires a bool result. How would I tackle this problem?
I realise I could do with with nested loops however I'm trying to brush up my C# skills!
Using List.Contains is not ideal in terms of performance, HashSet is a better option, also if you want to select the email address as soon as it contains one of the searched actions, you can use Any:
var activityTypes = new HashSet<EmailAction>() { EmailAction.Open, EmailAction.Bounce };
List<string> activityEmailAddresses =
emailActivity.Where(
member => member.Activity.Any(
activity => activity.Action.HasValue &&
activityTypes.Contains(activity.Action.Value)
)
)
.Select(activity => activity.EmailAddress)
.ToList();
You want to use All or Any depends if you want each or at least one match...
HashSet<EmailAction> activityTypes = new HashSet<EmailAction> { EmailAction.None };
var emailActivity = new List<EmailActivity>
{
new EmailActivity { Activity = new List<MemberActivity>{ new MemberActivity { Action = EmailAction.None } }, EmailAddress = "a" },
new EmailActivity { Activity = new List<MemberActivity>{ new MemberActivity { Action = EmailAction.Click } }, EmailAddress = "b" }
};
// Example with Any but All can be used as well
var activityEmailAddresses = emailActivity
.Where(x => x.Activity.Any(_ => _.Action.HasValue && activityTypes.Contains(_.Action.Value)))
.Select(x => x.EmailAddress)
.ToArray();
// Result is [ "a" ]
I have two lists classes
public class class1{
public Int Id { get; set; }
public Bool Flag{ get; set; }
}
public class class2{
public Int Id { get; set; }
}
Now i have List<class1> and List<class2>,
Now i have to update Flag property to true in List<class1> for only those Ids which match with the Id's present in List<class2> using lambda expression c#.Don't want to use foreach.
using lambda expression. Don't want to use foreach.
That's usually a silly requirement and a hallmark that you're not really familiar with C#, Linq or performance analysis. You have a collection whose elements you want to modify, so you should use foreach().
If you're trying out functional programming, then you should treat the list elements as immutable and project into a new collection.
The first part of your problem, looking up which list elements to modify based on a presence of one of their properties in another collection's elements' properties, is trivial:
var elementsToModify = list1.Where(l1 => list2.Any(l2 => l2.Id == l1.Id));
Now with a foreach(), this'll be simple:
foreach (var l1 in elementsToModify)
{
l1.Flag = true;
}
Or, even denser (not that less code equals more performance):
foreach (var l1 in list1.Where(l1 => list2.Any(l2 => l2.Id == l1.Id)))
{
l1.Flag = true;
}
So, there's your code. But you didn't want to use foreach(). Then you need to project into a new collection:
var newList1 = list1.Where(l1 => list2.Any(l2 => l2.Id == l1.Id))
.Select(l1 => new Class1
{
Id = l1.Id,
Flag = true,
})
.ToList();
There you have it, a List<Class1> with only flagged items. Optionally you could use this list in a foreach() to update the original list1. Oh, wait.
The below solution does not use the classical "for each", but is compiled to one under the hood. If that's not what you meant, then please explain what you are trying to achieve. Using for each in this example is a good approach. One could also use while or for loops, but is it really what's being asked here?
Object definition:
public class MyObject
{
public int Id { get; set; }
public bool Flag { get; set; }
}
List initialization:
var list = new List<MyObject>()
{
new MyObject() { Id= 1 },
new MyObject() { Id= 2 },
new MyObject() { Id= 3 },
new MyObject() { Id= 4 }
};
var list2 = new List<MyObject>()
{
new MyObject() { Id= 2 },
new MyObject() { Id= 4 }
};
Code:
list.ForEach(el => el.Flag = list2.Any(el2 => el2.Id == el.Id));
EDIT:
An example with a while loop (a bit nasty to do it this way):
int i = -1;
int numberOfElements = list.Count;
while (++i < numberOfElements)
{
list[i].Flag = list2.Any(el => el.Id == list[i].Id);
}
I guess you can write a for loop yourself...
I am trying to figure out the best way to organise a bunch of my data classes, given I need to be able to access some metrics on them all at some point.
Here's a snippet of my OR class:
public enum status { CLOSED, OPEN }
public class OR
{
public string reference { get; set; }
public string title { get; set; }
public status status { get; set; }
}
Not every OR I initialise will have values for all properties. I want to be able to 'collect' thousands of these together in such a way that I can easily obtain a count of how many OR objects had a value set. For example:
OR a = new OR() { reference = "a" }
OR b = new OR() { reference = "b", title = "test" }
OR c = new OR() { reference = "c", title = "test", status = status.CLOSED }
Now these are somehow collected in such a way I can do (pseudo):
int titleCount = ORCollection.titleCount;
titleCount = 2
I would also want to be able gather metrics for the enum type properties, for example retrieve a Dictionary from the collection that looks like:
Dictionary<string, int> statusCounts = { "CLOSED", 1 }
The reason for wanting access to these metrics is that I am building two collections of ORs and comparing them side-by-side for any differences (they should be identical). I want to be able to compare their metrics at this higher level first, then break-down where precisely they differ.
Thanks for any light that can be shed on how to accomplish this. :-)
... to 'collect' thousands of these
Thousands is not a huge number. Just use a List<OR> and you can get all your metrics with Linq queries.
For example:
List<OR> orList = ...;
int titleCount = orList
.Where(o => ! string.IsNullOrEmpty(o.title))
.Count();
Dictionary<status, int> statusCounts = orList
.GroupBy(o => o.status)
.ToDictionary(g => g.Key, g => g.Count());
The existing answers using Linq are absolutely great and really elegant, so the idea presented below is just for posterity.
Here is a (very rough) reflection-based program that will alow you to count the "valid" properties in any collection of objects.
The validators are defined by you in the Validators dictionary so that you can easily change what is a valid/invalid value for each property. You may find it useful as a concept if you end up with objects having tons of properties and don't want to have to write inline linq metrics on the actual collection itself for every single property.
You could weaponise this as a function and then run it against both collections, giving you a basis to report on the exact differences between both since it records the references to the individual objects in the final dictionary.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace reftest1
{
public enum status { CLOSED, OPEN }
public class OR
{
public string reference { get; set; }
public string title { get; set; }
public status status { get; set; }
public int foo { get; set; }
}
//creates a dictionary by property of objects whereby that property is a valid value
class Program
{
//create dictionary containing what constitues an invalid value here
static Dictionary<string,Func<object,bool>> Validators = new Dictionary<string, Func<object,bool>>
{
{"reference",
(r)=> { if (r ==null) return false;
return !String.IsNullOrEmpty(r.ToString());}
},
{"title",
(t)=> { if (t ==null) return false;
return !String.IsNullOrEmpty(t.ToString());}
},
{"status", (s) =>
{
if (s == null) return false;
return !String.IsNullOrEmpty(s.ToString());
}},
{"foo",
(f) =>{if (f == null) return false;
return !(Convert.ToInt32(f.ToString()) == 0);}
}
};
static void Main(string[] args)
{
var collection = new List<OR>();
collection.Add(new OR() {reference = "a",foo=1,});
collection.Add(new OR(){reference = "b", title = "test"});
collection.Add(new OR(){reference = "c", title = "test", status = status.CLOSED});
Type T = typeof (OR);
var PropertyMetrics = new Dictionary<string, List<OR>>();
foreach (var pi in GetProperties(T))
{
PropertyMetrics.Add(pi.Name,new List<OR>());
foreach (var item in collection)
{
//execute validator if defined
if (Validators.ContainsKey(pi.Name))
{
//get actual property value and compare to valid value
var value = pi.GetValue(item, null);
//if the value is valid, record the object into the dictionary
if (Validators[pi.Name](value))
{
var lookup = PropertyMetrics[pi.Name];
lookup.Add(item);
}
}//end trygetvalue
}
}//end foreach pi
foreach (var metric in PropertyMetrics)
{
Console.WriteLine("Property '{0}' is set in {1} objects in collection",metric.Key,metric.Value.Count);
}
Console.ReadLine();
}
private static List<PropertyInfo> GetProperties(Type T)
{
return T.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
}
}
}
You can get the title count using this linq query:
int titleCount = ORCollection
.Where(x => !string.IsNullOrWhiteSpace(x.title))
.Count();
You could get the count of closed like this:
int closedCount = ORCollection
.Where(x => x.status == status.CLOSED)
.Count();
If you were going to have larger collections or you access the values a lot it might be worth creating a custom collection implementation that stores the field counts, it could then increment/decrement these values as you add and remove items. You could also store a dictionary of status counts in this custom collection that gets updated as you add and remove items.
There is a model of a database that contains two entities related to foreign keys, such as
class Item
{
public int ID;
public string Name;
public int StatusID;
}
and
class Status
{
public int ID;
public string Name;
}
There is my own class
class MyClassFormat
{
public int ID;
public string Name;
public string Status;
}
Now I would like to create a method that returns a class of its own, something like that
MyClassFormat getTtem(this Item item)
{
return new MyClassFormat{ID = item.ID, Name = item.Name, Status = item.Statuses.Name}
}
The problem is that when I use this method, I get an exception that my method has no supported translation to SQL
Is it possible to create such a method which would be unwrapped its contents instead of calling this method?
Solved: Both answers are suitable for solving my problem, but the second one is the one I was looking for, thank you both
Either:
query
.Select(item => new
{
ID = item.ID,
Name = item.Name,
Status = item.Statuses.Name
})
.ToArray()
.Select(item => new MyClassFormat
{
ID = item.ID,
Name = item.Name,
Status = item.Status
});
or:
query
.ToArray()
.Select(item => getTtem(item));
The first way is longer to write, but it loads less data from database.
Anyway, you need to materialize query results (ToArray method in sample).
If your goal is to write code to reuse in multiple queries, you could factor out an expression like this:
static Expression<Func<Item, MyClassFormat>> ItemToClassFormat()
{
return item => new MyClassFormat
{
ID = item.ID,
Name = item.Name,
Status = item.Statuses.Name
}
}
This can be used similar to:
IQueryable<Item> query = ...;
var selector = ItemToClassFormat();
var results = query.Select(selector).ToList();