How to replace IF statements for dictionary? (C#, Linq) - c#

I have this equality comparer of a SampleObject:
public bool Equals(SampleObject x, SampleObject y)
{
if (x == null)
{
return y == null;
}
if (y == null)
{
return false;
}
if (!string.Equals(x.SomeId, y.SomeId))
{
return false;
}
if (x.EventsList == null)
{
return y.EventsList == null;
}
if (y.EventsList == null)
{
return false;
}
return x.EventsList.OrderBy(e => e)
.SequenceEqual(y.EventsList.OrderBy(e => e));
}
What I would like to know is if there is a way to replace all those IF clauses for a dictionary?

It is not possible with a dictionary, but with a list. A dictionary has no order, hence you can't guarantee that your checks are performed in the right order. I used a list of tuples, where the first item is the condition, and the second item is the return value. Your code will be the following:
public bool Equals(SampleObject x, SampleObject y)
{
var checks = new List<(Func<bool>,Func<bool>)>
{
(() => x == null, () => y == null),
(() => y == null, () => false),
(() => !string.Equals(x.SomeId, y.SomeId), () => false),
(() => x.EventsList == null, () => y.EventsList == null),
(() => y.EventsList == null, () => false)
};
foreach(var entry in checks)
{
if(entry.Item1.Invoke())
{
return entry.Item2.Invoke();
}
}
return x.EventsList.OrderBy(e => e)
.SequenceEqual(y.EventsList.OrderBy(e => e));
}
But I strongly recommend to stay with your original version, because from my point of view the readability strongly decreases with this approach. Sometimes a classic sequence of if statements is much more appropriate then any fancy LINQ or whatever stuff.

Well, I doubt if Dictionary is of any help here, but you can slightly simplify the routine into
public bool Equals(SampleObject x, SampleObject y) {
if (ReferenceEquals(x, y))
return true;
else if (x == null || y == null)
return false;
// From now on, both x and y are not null
//TODO: to avoid such constructions, do not let collections be null, but empty
if (x.EventList == null || y.EventList == null)
return x.EventList == y.EventList;
// From now on, both x.EventList and y.EventList are not null
return string.Equals(x.SomeId, y.SomeId) &&
x.EventList.OrderBy(e => e).SequenceEquals(y.EventList.OrderBy(e => e));
}

I don't think a dictionary is of any help here. You can replace all the if-statements by a simple expression:
return
x == null && y == null ||
x != null && y != null &&
String.Equals(x.SomeId, y.SomeId) &&
(x.EventsList == null && y.EventsList == null ||
x.EventsList != null && y.EventsList != null &&
x.EventsList.OrderBy(e => e)
.SequenceEqual(y.EventsList.OrderBy(e => e));
Note that because of C#'s short-circuit evaluation, the expression has to be evaluated only partially in most cases.

Related

i updated my project from dotnet 2.1 to 6 and some base repository function return 500

error in debugger
im trying to do sothing like this but need help with how
upgrate my project from dotnet 2.1 to 6
my base function now return 500
using vscode
i need to check if IsActive property is exist in that model from base repo that im using generic model
public virtual async Task<IEnumerable<TPickerDto>> GetForPickerAsync([Optional] List<string> fieldsForInclude, [Optional] Dictionary<string, ItemsPermissions> permissions)
{
IQueryable<TModel> listFromDb = this._context.Set<TModel>()
.Where(r => (r.GetType().GetProperty("IsActive") != null &&
r.GetType().GetProperty("IsActive").GetValue(r) != null &&
(bool)r.GetType().GetProperty("IsActive").GetValue(r) == true) ||
(r.GetType().GetProperty("IsActive") == null) ||
(r.GetType().GetProperty("IsActive").GetValue(r) == null));
if (permissions != null)
{
if (permissions.ContainsKey("allowedUnits") && permissions.ContainsKey("allowedSites"))
{
if (permissions.GetValueOrDefault("allowedUnits").All && !permissions.GetValueOrDefault("allowedSites").All)
{
listFromDb = listFromDb.Where(r => r.GetType().GetProperty("SiteId").GetValue(r) == null
|| permissions.GetValueOrDefault("allowedSites").Indexes.Contains((int)r.GetType().GetProperty("SiteId").GetValue(r)));
}
else
{
if (!permissions.GetValueOrDefault("allowedUnits").All && permissions.GetValueOrDefault("allowedSites").All)
{
listFromDb = listFromDb.Where(r => r.GetType().GetProperty("UnitId").GetValue(r) == null
|| permissions.GetValueOrDefault("allowedUnits").Indexes.Contains((int)r.GetType().GetProperty("UnitId").GetValue(r)));
}
else
{
if (!permissions.GetValueOrDefault("allowedUnits").All && !permissions.GetValueOrDefault("allowedSites").All)
{
listFromDb = listFromDb.Where(r => (r.GetType().GetProperty("UnitId").GetValue(r) == null
|| permissions.GetValueOrDefault("allowedUnits").Indexes.Contains((int)r.GetType().GetProperty("UnitId").GetValue(r)))
&& (r.GetType().GetProperty("SiteId").GetValue(r) == null
|| permissions.GetValueOrDefault("allowedSites").Indexes.Contains((int)r.GetType().GetProperty("SiteId").GetValue(r))));
}
}
}
}
else
{
if (permissions.ContainsKey("allowedUnits"))
{
listFromDb = listFromDb
.Where(r => permissions.GetValueOrDefault("allowedUnits").All
|| r.GetType().GetProperty("UnitId").GetValue(r) == null
|| permissions.GetValueOrDefault("allowedUnits").Indexes.Contains((int)r.GetType().GetProperty("UnitId").GetValue(r)));
}
if (permissions.ContainsKey("allowedSites"))
{
listFromDb = listFromDb
.Where(r => permissions.GetValueOrDefault("allowedSites").All
|| r.GetType().GetProperty("SiteId").GetValue(r) == null
|| permissions.GetValueOrDefault("allowedSites").Indexes.Contains((int)r.GetType().GetProperty("SiteId").GetValue(r)));
}
}
}
// Import all the navigation properties
if (fieldsForInclude != null)
{
listFromDb = _JoinNavigationProperties(listFromDb, fieldsForInclude);
}
IEnumerable<TPickerDto> mappingList = _mapper.Map<IEnumerable<TPickerDto>>(listFromDb);
return mappingList;
}
Starting from dotnet core 3, Ef will throw an exception if a Linq query couldn't be translated to SQL and results in Client-side evaluation. In earlier versions you would just receive a warning. You will need to improve your Linq query so that it can be evaluated on client side.
Refer to this link for details,
https://learn.microsoft.com/en-us/ef/core/querying/client-eval
You are using reflection to do the query, why not use the property directly? If your boolean can be be NULL then declare the type as bool? IsActive. That way you can check for null in the where,
this._context.Set<TModel>().Where(r => (r.IsActive==true))
In case you are trying to create a repository then try declaring an interface that has IsActive as a field.
interface ISoftDeleteTarget
{
public IsActive{get;}
}

C# Find best matching element / Simplify query to List

I ask myself how I can simplify something like this
var myList = new List<MyObject>
pulic MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value)
{
MyObject item = null;
item = myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value && x.Prop3 == Prop3Value);
if(item != null)
{
return item;
}
item = myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value);
if(item != null)
{
return item;
}
item = myList.Find(x => x.Prop1 == Prop1Value);
// Doesn't matter if its null
return item;
}
I'm sure LINQ offers a solution, but I'm not able to find it :)
Thank you.
Technically, you can simplify the current code into
pulic MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value) {
return
myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value && x.Prop3 == Prop3Value)
?? myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value)
?? myList.Find(x => x.Prop1 == Prop1Value);
}
But doing Find (scaning the entire list) can be a costly operation, if it's your case you can find the best match in one loop only:
public MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value) {
MyObject result1 = null;
MyObject result2 = null;
foreach (MyObject item in myList) {
if (item.Prop1 == Prop1Value) {
result1 = item;
if (item.Prop2 == Prop2Value) {
result2 = item;
if (item.Prop3 == Prop3Value)
return item;
}
}
}
return result2 ?? result1;
}
Try this:
public MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value)
{
return myList.FirstOrDefault(x => (x.Prop1 == Prop1Value && x.Prop2 == Prop2Value && x.Prop3 == Prop3Value)
|| (x.Prop1 == Prop1Value && x.Prop2 == Prop2Value)
|| (x => x.Prop1 == Prop1Value));
}

Looping though collection inside other collection and LINQ lambda expression

Here I'm selecting descriptions from all FactoryOption with an Header of "TRANSMISSION"
tOptions = _vDetails.fOptions
.Where(x => (x.header != null && x.header.Value.ToUpper() == "TRANSMISSION"))
.Select(x => x.description)
.SelectMany(x => x);
If the header is null I would like to search for the header in ambiguous options which matches to "TRANSMISSION"
Something like the following :
foreach (var fOptions in _vDetails.fOptions)
{
if (fOptions.header != null && fOptions.header.Value.ToUpper() == "TRANSMISSION")
{
tOptions = fOptions.description;
}
else if (fOptions.ambiguousOption != null)
{
foreach (var ambiguousOption in fOptions.ambiguousOption)
{
if (ambiguousOption.header != null && ambiguousOption.header.Value.ToUpper() == "TRANSMISSION")
{
newseq = tOptions.Concat(ambiguousOption.description);
}
}
}
}
I am trying to change existing LINQ lambda expression for iterating through fOptions.ambiguousOption could someone please suggest.
If I'm understanding correctly I think you just want to do something like this:
var result = options.SelectMany(o => IsTransmissionHeader(o.header) ? o.description :
o.ambigousOptions == null || !o.ambigousOptions.Any(x => IsTransmissionHeader(x.header)) ? new string[] { } :
o.ambigousOptions.First(x => IsTransmissionHeader(x.header)).description)
.Where(d => d.Any());
I added a static method to check the header:
public static bool IsTransmissionHeader(Header header)
{
return header != null && header.Value != null && header.Value.ToUpper() == "TRANSMISSION"
}
This will return IEnumerable<string>. If you want a IEnumerable<IEnumerable<string>> change the SelectMany to Select.
EDIT:
To get all Transmission description values from ambigousOptions you need to change the last line so it looks like this:
var result = options.SelectMany(o => IsTransmissionHeader(o.header) ? o.description :
o.ambigousOptions == null || !o.ambigousOptions.Any(x => IsTransmissionHeader(x.header)) ? new string[] { } :
o.ambigousOptions.Where(x => IsTransmissionHeader(x.header)).SelectMany(x => x.description));
This should do what you want. Here I concatenate the descriptions from FactoryOption that have a matching header with the descriptions from AmbiguousOption that have a matching header.
var descriptions = details.SelectMany(d => d.FactoryOptions.Where(f => f.Header == "TRANSMISSION").Select(f => f.Description)
.Concat(d.FactoryOptions.SelectMany(f => f.AmbiguousOptions.Where(a => a.Header == "TRANSMISSION").Select(a => a.Description))));
Update
Answer above is for when you start with a collection of Detail. If you start with a single Detail then you can do:
_vehicleDetails.FactoryOptions.Where(f => f.Header == "TRANSMISSION").Select(f => f.Description)
.Concat(_vehicleDetails.FactoryOptions.SelectMany(f => f.AmbiguousOptions.Where(a => a.Header == "TRANSMISSION").Select(a => a.Description)));

Dynamic EF Where Clause raising ArgumentNullException

I'm trying to code a method that, in it's class given the values of some of the attributes, returns a filtered DbSet. The code, so far, is:
public IEnumerable<Pesquisa> Pesquisas {
get {
PrometheusDBContext db = new PrometheusDBContext();
var temp = db.Pesquisas;
if ((this.Filtro.Nome != null) && (this.Filtro.Nome.Trim() != ""))
{
temp = (temp.Where(p => SqlFunctions.PatIndex(this.Filtro.Nome, p.Nome) > 0) as DbSet<Pesquisa>);
}
if ((this.Filtro.CodTipoPesquisa != null) && (this.Filtro.CodTipoPesquisa.Trim() != ""))
{
temp = (temp.Where(p => p.CodTipoPesquisa == this.Filtro.CodTipoPesquisa.Trim()) as DbSet<Pesquisa>);
}
if ((this.Filtro.IDStatusPesquisa != null) && (this.Filtro.IDStatusPesquisa > 0))
{
temp = (temp.Where(p => p.IDStatusPesquisa == this.Filtro.IDStatusPesquisa) as DbSet<Pesquisa>);
}
if ((this.Filtro.DataCriacao_Inicial != null) && (this.Filtro.DataCriacao_Final != null))
{
temp = (temp.Where(p => (p.DataCriacao >= this.Filtro.DataCriacao_Inicial) && (p.DataCriacao <= this.Filtro.DataCriacao_Final)) as DbSet<Pesquisa>);
}
else
{
if (this.Filtro.DataCriacao_Inicial != null)
{
temp = (temp.Where(p => p.DataCriacao >= this.Filtro.DataCriacao_Inicial) as DbSet<Pesquisa>);
}
if (this.Filtro.DataCriacao_Final != null)
{
temp = (temp.Where(p => p.DataCriacao <= this.Filtro.DataCriacao_Final) as DbSet<Pesquisa>);
}
}
return temp
.Include(p => p.Usuario)
.Include(p => p.StatusPesquisa)
.Include(p => p.TipoPesquisa)
.Include(p => p.ModeloTermoAdesao)
.Include(p => p.Pacientes)
.ToList();
}
Problem is: everytime one of the attributes is filled with some value (i.e.: this.Filtro.Nome = "test" ), the ToList() raises an ArgumentNullExcpetion. Any ideas?
You shouldn't cast to DbSet at the end of each line.
Also, declare
IQueryable<Pesquisa> temp = db.Pesuisas;
// your code follows.
The reason behind it is that although you start with a DbSet, applying operators changes its type. Your dynamic cast returns null then.

Query Collection using composite key where key parts can match 'any'

Is there an appropriate collection or algorithm that would allow me to get a value using a composite key where when querying parts of the key could be null to mean match any value?
For example, if I have the class:
class Key
{
string p1{ get; }
string p2{ get; }
string p3{ get; }
public Key(string p1, string p2 , string p3)
{ this.p1 = p1; this.p2 = p2; this.p3=p3; }
}
If I then created three keys e.g.
new Key( "a","b","c")
new Key( "d","b","c")
new Key( "e","f","c")
I would like a collection or algorithm with out iterating to allow for the following key
new Key( null, "b","c") to return the values mapped to the first two keys,
new key( null,null,"c") to return the values mapped to all of the keys.
Is there any way to do this?
Probably this would do for lookup by any combination of three key components. Note that key for pair lookup (A+B) is created by simple concat for simplicity. Real key should be Tuple.
var keys = new[] { new Key("a", "b", c"), ... };
class Map
{
// ... skip members declaration here
public Map(IEnumerable<Keys> keys)
{
all = keys;
mapA = keys.ToLookup(k => k.A);
mapB = keys.ToLookup(k => k.B);
mapC = keys.ToLookup(k => k.C);
// should be keys.ToLookup(k => Tuple.Create(k.A, k.B))
mapAB = keys.ToLookup(k => k.A + k.B);
mapAC = keys.ToLookup(k => k.A + k.C);
mapBC = keys.ToLookup(k => k.B + k.C);
mapABC = keys.ToLookup(k => k.A + k.B + k.C);
}
public IEnumerable<Key> Find(Key k)
{
if(k.A == null && k.B == null && k.C == null) return all;
if(k.A != null && k.B == null && k.C == null) return mapA[k.A];
if(k.A == null && k.B != null && k.C == null) return mapB[k.B];
if(k.A == null && k.B == null && k.C != null) return mapC[k.C];
if(k.A != null && k.B != null && k.C == null) return mapAB[k.A+k.B];
if(k.A != null && k.B == null && k.C != null) return mapAC[k.A+k.C];
if(k.A == null && k.B != null && k.C != null) return mapBC[k.B+k.C];
return mapABC[k.A+k.B+k.C];
}
}

Categories

Resources