Check if string list contains any enum string value - c#

enum KnownError
{
[StringValue("CODE-001")]
CODE001,
[StringValue("CODE-002")]
CODE002,
[StringValue("CODE-003")]
CODE003,
[StringValue("CODE-004")]
CODE004,
[StringValue("CODE-005")]
CODE005
}
List<string> errors = {"ahah", "eheh", "CODE-005", "uhuh"};
Let's say i have a list of string errors.
How can I check if any error is "known"?
bool ContainsKnownError(List<string> error)
{
return errors.Where(error => Enum.IsDefined(typeof(KnownError), error) == true).Count() > 0;
}
This doesn't seem to work.
How can I access StringValue inside the linq query without having to compare each string?
EDIT
I tried #AK_ solution, using Intersect, but I'm getting this compilation error:
The type arguments for method 'System.Linq.Enumerable.Intersect<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Collections.Generic.IEnumerable<TSource>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
The real scenario is an Error object with a string field with the code like this
class Error { string code; }
List<Error> errors = GetErrors();
var knownErrors = Enum.GetValues(typeof(KnownError));
bool exists = errors.Select(error => error.code).Intersect(knownErrors).Any();

var knownErrors = Enum.GetValues(typeof(KnownError));
return errors.Contains(error => knownErrors.Contains(error));
//or cooler:
return errors.Intersect(knownErrors).Count() > 0;
Zack's comment is correct:return errors.Intersect(knownErrors).Any is better...
+1 him :-)

Enum.IsDefined expects an object-wrapped instance of the enum. Passing a string name does not produce the desired result.
This should work:
KnownError ignore;
var res = errors.Any(errorCode => Enum.TryParse<KnownError>(errorCode, out ignore));
Note the use of LINQ's Any in place of comparing Count() to zero: this approach is more efficient, because it stops as soon as it finds the first match.

Related

C# Converting List to Enumerable and Selecting With LINQ

I have a question related to an Enumerable and list of integers. I have the below code that is throwing an error stating "'int' does not contain a definition for 'Field' and has some invalid arguments." I'm sure this is something easy, but was wondering if anyone could help out. Thanks!
public static IList<Site> GetSiteFromSites(DataTable data)
{
var linqRegions = Enumerable.Empty<int>();
IList<Site> sites = data.AsEnumerable().Select(r =>
{
return new Site()
{
id = r.Field<string>("id"),
name = r.Field<string>("name"),
address_line1 = r.Field<string>("address_line1"),
address_line2 = r.Field<string>("address_line2"),
post_code = r.Field<string>("post_code"),
county = r.Field<string>("county"),
city = r.Field<string>("city"),
phone_number = r.Field<string>("phone_number"),
regions = linqRegions.Where(u => u.Field<int>("regions") == r.Field<int>("regions")).ToList().Select(z => z.Field<int>("regions")).ToList()
//error is thrown for above line of code "regions"
};
}).ToList();
return sites;
}
The variable linqRegions is an empty enumerable of ints.
In the line where the error is thrown, you are calling the Where() method where you try to get the Field property of each element. Because each element is an int, it throws the 'int' does not contain a definition for 'Field' error.
Change your line with the error to this:
regions = linqRegions.Where(u => u == r.Field<int>("regions")).ToList()
This will always return zero elements (because linqRegions is empty), so don't forget to populate it. And even then, the line doesn't make any sense because it will always return a list of the same values (values equal to r.Field<int>("regions")).

Extract arguments of method calls with Mono.Cecil

I am trying to extract all arguments passed to a specific method with Mono.Cecil.
In a post-processing script that runs right after the project being build, I am able to find all method calls, and even extract the type of the argument passed to the function. However, I am not able to get the actual value... So is this even possible with Mono.Cecil, and if yes, what value do I need to look at?
Here is my current code :
List<MethodDefinition> methods = new List<MethodDefinition> ();
foreach (ModuleDefinition _module in assembly.Modules) {
foreach (TypeDefinition _type in _module.Types) {
methods.AddRange (_type.Methods);
}
}
var uiExtensionMethod = methods
.Where(m => m.DeclaringType.Name == "SetCustomText").FirstOrDefault();
var instructions = uiExtensionMethod.Body.Instructions;
var count = instructions.Count;
var instructionArray = instructions.ToArray();
for (int i = 0; i < count; i++) {
if (instructionArray [i] != null) {
if (instructionArray [i].Operand != null) {
var paramType = instructionArray [i].Operand.ToString ();
// So here I have the type of the parameter, but I cannot get the value...
}
}
}
Okay, so I found the solution to this.
The problem was, that Mono.Cecil did indeed find my method calls, but it processed them inside the file, where the argument was already written into a variable, and therefor unable to be converted to a string.
So my solution is, parsing ALL methods that have a string as an Operand, and then detecting their NEXT operation. If the next operation is my function of choice, THEN I found the string I am looking for :)

C# Linq Filter IEnumerable dynamic property and value

public class Sample
{
public int Id { get; set; }
public string Description { get; set; }
public DateTime EffectiveDate { get; set; }
}
IEnumerable<Sample> sampleList;
//Populate the list
Now I want to filter the list some times by "Id" property, sometimes "Description" property. Just want to pass the property name (filterColumn) and property value (filterValue) both as strings.
I tried the following:
IEnumerable<Sample> result = sampleList.Where(x => x.GetType().GetProperty(filterColumn).Name == filterValue);
and
string whereQuery = string.Format(" {0} = \"{1}\"", filterColumn, filterValue);
IEnumerable<Sample> result = sampleList.AsQueryable().Where(whereQuery);
The second option works, if i pass the filterColumn as "Description", but throws incompatible '=' operator between string and int error when "Id" is passed as filterColumn and some filterValue like "1".
Appreciate any help. Thanks
Your first approach can work. Expanding on Jon Skeet's comment, here's the adjusted statement.
IEnumerable<Sample> result = sampleList.Where(
x => x.GetType().GetProperty(filterColumn).GetValue(x, null).Equals(filterValue)
);
To put a little context around this, you would have to allow for the differing data types. You can do this at least two ways: use a generic method or use the object data type. For illustrative purposes, I'll use the object approach.
public IEnumerable<Sample> GetFiltered(
IEnumerable<Sample> samples, string filtercolumn, object filtervalue
{
return samples.Where(
x => x.GetType().GetProperty(filtercolumn).GetValue(x, null).Equals(filtervalue)
);
}
IEnumerable<Sample> sampleList;
var byId = GetFiltered(sampleList, "Id", 100);
var byDescription = GetFiltered(sampleList, "Description", "Some Value");
This example is not really safe as there is no type checking to ensure that the property value will be of the same data type that you are passing in. For example, there is nothing stopping you from passing "Description" and 100 as parameters. You can't do a meaningful comparison between an integer and a string so you will always come up with an empty result. The Equals method does not throw an exception, it just sees that the two objects are different. As Jon pointed out, you'll always want to use Equals in this case rather than the "==" operator. The Equals method is intended to compare content while "==" compares references. Example:
Console.WriteLine(12 == 12);
// True
object a = 12;
object b = 12;
Console.WriteLine(a == b);
// False - because, due to boxing, a and b are separate objects
// that happen to contain the same value. (Check out "boxing"
// if this doesn't make sense.)
Console.WriteLine(a.Equals(b));
// True - because the Equals method compares content (value)
Also, note that strings have some special behaviors when using the "==" operator. The important thing to remember is that there is a difference between references (containers) and content. You want to compare content and that means Equals. (I have noticed that the Immediate window in Visual Studio is inconsistent in its results regarding strings when using "==". I suspect this is because string references can be, but are not always, optimized in that window.)
You state that your second approach works. I have not seen this type of filter string in a standard IEnumerable.Where method. So I am guessing that you are using some extensions. Your example does not work as shown. The DataTable class uses filter strings that match your usage. In general, a filter string has to be constructed in different ways based on data type. For example, a string requires the quotes (which you have) but an integer value does not use quotes.
Another option that you have is to set up a dictionary with the required operations.
public IEnumerable<Sample> GetFiltered(
IEnumerable<Sample> samples, string property, string value)
{
var map = new Dictionary<string, Func<string, Func<Sample, bool>>>()
{
{ "Description", v => s => s.Description == v },
{ "Id", v => s => s.Id == int.Parse(v) },
};
return samples.Where(map[property](value));
}
The advantage here is that you can perform a more complex comparison, such as adding custom filters by ranges of values, or those containing more than one property.

Scope of string inside lambda

I have an interesting scenario in which I've built a validation checking system that maintains a series of requirements in the form List<Tuple<Func<bool>, string>> where the Func should return true if validation failed and false otherwise. The string is a corresponding rejection description that describes the condition should the test fail.
In more simple tests like the following the validation system is quite simple:
validationChecks.Add(Tuple.Create<Func<bool>, string>(() =>
value1 == requiredValue, "value 1 did not have the required value"));
I'm struggling to understand the scope of variables within the lambda for the Func in a more advanced scenario in which the rejection string is calculated in a call to another part of the system. The scenario looks something like this:
string rejectionString = null;
validationChecks.Add(Tuple.Create<Func<bool>, string>(() => {
rejectionString = CallToAnotherMethodThatReturnsString(parameter);
if (rejectionString != null) {
return true;
} else {
return false;
}
}, rejectionString));
EDIT: As observed through testing, when this check fails the rejectionString from the Tuple is still null. I want the rejectionString that was generated by the CallToAnotherMethod to be used instead, is there any way I can do this using ref or otherwise? I need the Func's code to be able to affect the value of the string inside Item2 of the Tuple.
The problem is that the code inside of CallToAnotherMethodThatReturnsString might be code that I only want executed ONCE. However should the check fail I want to use the string that would have been returned from it as my rejection description in the Tuple. I'm unable to tell at this point what the effect of my use of rejectionString in this second example will accomplish? Will rejectionString inside the Tuple always be null? Or if CallToAnotherMethodThatReturnsString returns a different value will it be updated?
Just an idea that might work. I think if you change your second Tuple parameter to a Func that returns string instead of string value, you could come close to what you need.
string rejectionString = null;
validationChecks.Add(Tuple.Create<Func<bool>, Func<string>>(() =>
{
rejectionString = CallToAnotherMethodThatReturnsString(parameter);
if (rejectionString != null) {
return true;
} else {return false;}
},
()=>rejectionString));
In the first case your validation check would be set as
validationChecks.Add(Tuple.Create<Func<bool>, Func<string>>(() => value1 == requiredValue, ()=>"value 1 did not have the required value"));
And your validation is logic is then
if(validationChecks[0].Item1()==false)
var error = validationChecks[0].Item2();

Linq To SQL Method with .Contains fails

I have the below Linq To SQL Method. When I step through the code spcCodeIDs contains the seven entries I am expecting. However I get a run-time exception of
Method 'Boolean Contains(System.String)' has no supported translation to SQL.
What am I missing?
public static DataTable GetSPCCodeList()
{
using (var context = ProviderDataContext.Create())
{
IQueryable<tblProviderAdminSPCCode> tSPCCode = context.GetTable<tblProviderAdminSPCCode>();
IList<string> spcCodeIDs = BLLCmo.FCApprovedSPCsForGreenSheet();
return (tSPCCode
.Where(spcCode => spcCode.Inactive == null && spcCodeIDs.Contains(spcCode.SPCCodeID))
.OrderBy(spcCode => spcCode.SPCCodeID)
.Select(spcCode => new { spcCode.SPCCodeID, spcCode.SPCDescription, spcCode.SPCCategoryID }))
.CopyLinqToDataTable();
}
}
LINQ to SQL can only support Contains translations form a concrete list and not the IList interface.. try changing your line from
IList<string> spcCodeIDs = BLLCmo.FCApprovedSPCsForGreenSheet();
to
List<string> spcCodeIDs = BLLCmo.FCApprovedSPCsForGreenSheet().ToList();
You need to pass a string as a parameter to Contains. So trying passing spcCode.SPCCodeID.ToString()

Categories

Resources