Unable to use use .Except on two List<String> - c#

I am working on an asp.net mvc-5 web applicatio. i have these two model classes:-
public class ScanInfo
{
public TMSServer TMSServer { set; get; }
public Resource Resource { set; get; }
public List<ScanInfoVM> VMList { set; get; }
}
public class ScanInfoVM
{
public TMSVirtualMachine TMSVM { set; get; }
public Resource Resource { set; get; }
}
and i have the following method:-
List<ScanInfo> scaninfo = new List<ScanInfo>();
List<String> CurrentresourcesNames = new List<String>();
for (int i = 0; i < results3.Count; i++)//loop through the returned vm names
{
var vmname = results3[i].BaseObject == null ? results3[i].Guest.HostName : results3[i].BaseObject.Guest.HostName;//get the name
if (!String.IsNullOrEmpty(vmname))
{
if (scaninfo.Any(a => a.VMList.Any(a2 => a2.Resource.RESOURCENAME.ToLower() == vmname.ToLower())))
{
CurrentresourcesNames.Add(vmname);
}
}
}
var allcurrentresourcename = scaninfo.Select(a => a.VMList.Select(a2 => a2.Resource.RESOURCENAME)).ToList();
var finallist = allcurrentresourcename.Except(CurrentresourcesNames).ToList();
now i want to get all the String that are inside the allcurrentrecoursename list but not inside the CurrentresourcesName ?
but that above code is raising the following exceptions :-
Error 4 'System.Collections.Generic.List>'
does not contain a definition for 'Except' and the best extension
method overload
'System.Linq.Queryable.Except(System.Linq.IQueryable,
System.Collections.Generic.IEnumerable)' has some invalid
arguments
Error 3 Instance argument: cannot convert from
'System.Collections.Generic.List>'
to 'System.Linq.IQueryable'

It looks to me like
var allcurrentresourcename = scaninfo.Select(a => a.VMList.Select(a2 => a2.Resource.RESOURCENAME)).ToList();
is not a list of strings at all like you seem to expect it to be. scaninfo is of type List<ScanInfo>, and the lambda expression
a => a.VMList.Select(a2 => a2.Resource.RESOURCENAME)
yields one IEnumerable<TSomething> for each ScanInfo object. So it would seem that allcurrentresourcename is not a List<string>, but rather a List<IEnumerable<TSomething>>, where TSomething is the type of RESOURCENAME (most likely string).
Edit: What you presumably want to use here is the SelectMany LINQ method (see #pquest's comment). It flattens the lists that you get to "one big list" of resource names, which you can then use Except on:
var allcurrentresourcename = scaninfo.SelectMany(a => a.VMList.Select(
b => b.Resource.RESOURCENAME));
You shouldn't even need the ToList() at the end of the line.

Related

C# MongoDB Driver filter subdocument list based on incoming array

I have the following C# model structure:
public class Box
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
[BsonElement("relatedJobs")]
public List<BoxJobs> RelatedJobs { get; init; } = default!;
//more properties
public class BoxJobs
{
[BsonElement("partnerId")]
public string PartnerId { get; init; } = null!;
//more properties
}
}
There is a need to filter all the boxes based in an incoming partnerIds array. In other words I want to retrieve every single box that has at least one relatedJob with its partnerId present in the incoming array.
I've tried the following:
var builder = Builders<Box>.Filter;
var filter = new FilterDefinitionBuilder<Box>().Empty;
filter &= !partnerIds.Any()
? new FilterDefinitionBuilder<Box>().Empty
: builder.AnyIn(box => box.RelatedJobs.Select(relatedJob => relatedJob.PartnerId), partnerIds);
var cursor = Collection.Find(filter);
This results in the following error:
Unable to determine the serialization information for box => box.RelatedJobs.Select(relatedJob => relatedJob.PartnerId).
I have also tried creating a PartnerIds property in Box which returns the same Select result, and use it in the AnyIn function but it didn't help either.
How can I do this filtering?
As I can see you need to check that RelatedJobs contains a job pith PartnerId from partnerIds list. Fields.ElemMatch method returns the first matching element in the array specified by name.
var builder = Builders<Box>.Filter;
var filter = new FilterDefinitionBuilder<Box>().Empty;
filter &= !partnerIds.Any()
? new FilterDefinitionBuilder<Box>().Empty
: builder.ElemMatch(x => x.RelatedJobs, j => partnerIds.Contains(j.PartnerId));
var cursor = Collection.Find(filter);

How to map a property in C# that is a list?

These are my classes:
public class Registration
{
public bool? IsRegistered { get; set; }
public List<RegistrationProcess> RegistrationProcess { get; set; }
}
public class RegistrationProcess
{
public bool? PaidInFull { get; set; }
public double PaymentAmount { get; set; }
public bool IdentityVerified { get; set; }
}
I have a method that is doing the object mapping like this:
public Registration Translate(Services.Registration source)
{
return new Registration
{
IsRegistered = source.IsRegistered,
RegistrationProcess = new List<RegistrationProcess>
{
new RegistrationProcess()
{
PaidInFull = source.RegistrationProcess.Select(o => o.HasPaid),
}
}
};
}
I am not sure how to set up the mapping for the RegistrationProcess.
I want to map PaidInFull within RegistrationProcess to the property HasPaid. They are both bools.
I am getting an error: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<bool?>' to 'bool?'
I feel like I need to add something to the end of the Select statement but I am not sure what. I did FirstOrDefault() and that made the error go away but I only got one value back and that is not what I want.
The problem with your approach is that you are only creating one instance of RegistrationProcess inside the list constructor. So by calling source.RegistrationProcess.Select(o => o.HasPaid) and assign it to your newly created RegistrationProcess you are creating a Collection of all bool values of your service registration process and try to assign it to a single registration process.
The Solution is to create multiple RegistrationProcess instances. In fact, one for each element in source.RegistrationProcess. To do this you can use the Select method on source.RegistrationProcess directly:
source.RegistrationProcesses.Select(x => new RegistrationProcess() { PaidInFull = x.HasPaid }).ToList()
As you can see, for every element in source.RegistrationProcesses a new RegistrationProcess is created. Or in other words: you select the elements of source.RegistrationProcesses as new RegistrationProcess() { PaidInFull = x.HasPaid } if that makes more sense to you.
The .ToList() converts the IEnumerable to a list.

How to yield multiple objects with respect to a multi-valued column in Dynamic Linq

Scenario:
I have to export an excel file which will contain list of Parts. We have enabled the user to select the columns and get only selected columns' data in the exported file. Since this is a dynamic report, I am not using any concrete class to map the report as this will result in exporting empty column headers in the report, which is unnecessary. I am using Dynamic Linq to deal with this scenario.
I have a list of dynamic objects fetched from dynamic linq.
[
{"CleanPartNo":"Test","Description":"test","AliasPartNo":["258","145","2313","12322"]},
{"CleanPartNo":"Test1","Description":"test1","AliasPartNo":[]}
]
How can I get 4 rows out of this json like
Please note that I cannot use a strongly typed object to deserialize/ Map it using JSON.Net
Update
Following is the code:
public class Part
{
public int Id { get; set; }
public string CleanPartNo { get; set; }
public string Description { get; set; }
public List<PartAlias> AliasPartNo { get; set; }
}
public class PartAlias
{
public int PartId { get; set; }
public int PartAliasId { get; set; }
public string AliasPartNo { get; set; }
}
var aliases = new List<PartAlias> {
new PartAlias{AliasPartNo="258" },
new PartAlias{AliasPartNo="145" },
new PartAlias{AliasPartNo="2313" },
new PartAlias{AliasPartNo="12322" }
};
List<Part> results = new List<Part> {
new Part{CleanPartNo="Test", Description= "test", PartAlias=aliases },
new Part{CleanPartNo="Test1", Description= "test1" }
};
var filters = "CleanPartNo,Description, PartAlias.Select(AliasPartNo) as AliasPartNo";
var dynamicObject = JsonConvert.SerializeObject(results.AsQueryable().Select($"new ({filters})"));
in the dynamicObject variable I get the json mentioned above
Disclaimer: The following relies on anonymous classes, which is not exactly the same as dynamic LINQ (not at all), but I figured that it may help anyway, depending on your needs, hence I decided to post it.
To flatten your list, you could go with a nested Select, followed by a SelectMany (Disclaimer: This assumes that every part has at least one alias, see below for the full code)
var flattenedResult = result.Select(part => part.AliasPartNumber.Select(alias => new
{
CleanPartNo = part.CleanPartNo,
Description = part.Description,
AliasPartNo = alias.AliasPartNo
})
.SelectMany(part => part);
You are first projecting your items from result (outer Select). The projection projects each item to an IEnumerable of an anonymous type in which each item corresponds to an alias part number. Since the outer Select will yield an IEnumerable<IEnumerable> (or omething alike), we are using SelectMany to get a single IEnumerable of all the items from your nested IEnumerables. You can now serialize this IEnumerable of instances of an anonymous class with JsonConvert
var json = sonConvert.SerializeObject(flatResults);
Handling parts without aliases
If there are no aliases, the inner select will yield an empty IEnumerable, hence we will have to introduce a special case
var selector = (Part part) => part.AliasPartNumber?.Any() == true
? part.AliasPartNumber.Select(alias => new
{
CleanPartNo = part.CleanPartNo,
Description = part.Description,
AliasPartNo = alias.AliasPartNo
})
: new[]
{
new
{
CleanPartNo = part.CleanPartNo,
Description = part.Description,
AliasPartNo = alias.AliasPartNo
}
};
var flattenedResult = result.Select(selector).SelectMany(item => item);
From json you provided you can get values grouped by their name in this way:
var array = JArray.Parse(json);
var lookup = array.SelectMany(x => x.Children<JProperty>()).ToLookup(x => x.Name, x => x.Value);
then this is just a manner of simple loop over the lookup to fill the excel columns.
However, I would suggest to do the flatenning before JSON. I tried for some time to make it happen even without knowing the names of the columns that are arrays, but I failed, and since it's your job, I won't try anymore :P
I think the best way here would be to implement custom converter that would just multiply objects for properties that are arrays. If you do it well, you would get infinite levels completely for free.

IEnumerable<T> GroupBy with Fluent Validation

So the problem I am trying to solve is that I am using a lot of generic classes with <T> that need to execute a .NET Async REST call to retrieve an IEnumerable<T> list of objects from an API. At runtime things are resolved fine with the T stuff because I have some concrete instances higher up the chain.
I have a worker class:
public class Worker<T> where T : class, new()
That has a REST client factory:
IBatchClientFactory batchClientFactory
where in that factory basically creates an instance of this:
public class BatchClient<T> where T : class, new()
That BatchClient has an important method:
public BaseBatchResponse<T> RetrieveManyAsync(int top = 100, int skip = 0)
so that the worker class's method does something like:
var batchClient = this.batchClientFactory.Create<T>(siteId);
var batchResponse = await batchClient.RetrieveManyAsync(top, skip);
Batch Response looks like:
public class BaseBatchResponse<T>
{
public List<T> Value { get; set; }
public BaseBatchResponse<T> Combine(BaseBatchResponse<T> baseBatchResponse)
{
return new BaseBatchResponse<T>
{
Value = this.Value.Concat(baseBatchResponse.Value).ToList()
};
}
}
Now at runtime things are ok because higher up the chain i will instantiate Worker into something like.. new Worker<Appointment>(); And the T's will all just work perfectly since everything down the chain is just doing generics.
My problem now is that I would like to evaluate my batchResponse and go through the List and run some validation against each element in the list. I saw this article on stack overflow that seems to let you split a list into 2 lists using GroupBy via a Dictionary where some SomeProp is the thing you're splitting around.. but can you do that GroupBy logic using a method call? And more importantly can I use FluentValidation as that method call? Ideally my code would look like:
var groups = allValues.GroupBy(val => validationService.Validate(val)).ToDictionary(g => g.Key, g => g.ToList());
List<T> valids = groups[true];
List<T> invalids= groups[false];
Where the result would be a List of my objects that are valid, and a second List of my objects that are invalid.
Ideally I would then just make a FluentValidation class that binds to my concreate Appointment class and has a rule inside it:
this.When(x => !string.IsNullOrWhiteSpace(x.Description), () =>
this.RuleFor(x => x.Description).Length(1, 4000));
Which will hook everything together and be used to determine if my object at runtime belongs in the valids or invalids list
I'm not sure fluent means, there is a approch to achieve that using LINQ:
using System.Collections.Generic;
using System.Linq;
namespace Investigate.Samples.Linq
{
class Program
{
public class SomeEntity
{
public string Description { get; set; }
}
static void Main(string[] args)
{
//Mock some entities
List<SomeEntity> someEntities = new List<SomeEntity>()
{
new SomeEntity() { Description = "" },
new SomeEntity() { Description = "1" },
new SomeEntity() { Description = "I am good" },
};
//Linq: Where to filter out invalids, then category to result with ToDictionary
Dictionary<bool, SomeEntity> filteredAndVlidated = someEntities.Where(p => !string.IsNullOrWhiteSpace(p.Description)).ToDictionary(p => (p.Description.Length > 1));
/* Output:
* False: new SomeEntity() { Description = "1" }
* True: new SomeEntity() { Description = "I am good" }
* */
}
}
}
Code segment:
Dictionary<bool, SomeEntity> filteredAndVlidated = someEntities.Where(p => !string.IsNullOrWhiteSpace(p.Description)).ToDictionary(p => (p.Description.Length > 1));

How to Cast List<T> To List<ClassName>

I am using generic method to fill my dropdown for all types
below is my code.
the entity type are as follow
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
public class DropDown
{
public string Id { get; set; }
public string Name { get; set; }
}
i am able to fetch data successfully at
var data = DataFetcher.FetchData<T>();
private static void Main( string[] args )
{
List<DropDown> cities = BLL.GetDataList<City>();
List<DropDown> states = BLL.GetDataList<State>();
List<DropDown> roles = BLL.GetDataList<Role>();
}
public static class BLL
{
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data as List<DropDown>;
}
}
I knew this cast data as List<DropDown> will fail,thats why its returning null back to calling method,
How can i cast Generic list to List of Known Type?
You have to ask yourself: how do I want to convert T to DropDown? If you can't answer this, the answer is: you can't.
I guess your DropDown class has an object Value property, that holds the dropdown value, and you wish to assign the data entity to that property.
Then you can project the list of data entities to DropDowns as such:
var data = DataFetcher.FetchData<T>();
return data.Select(d => new DropDown { Value = d }).ToList();
As for your edit: so you have at least one type, the displayed Role, that has an Id and Name property. But type T doesn't guarantee this, so you'd need to introduce an interface:
public interface INamedIdentifyableEntity
{
string Id { get; set; }
string Name { get; set; }
}
And apply this to your entities. Then introduce it as a generic constraint and do the mapping:
return data.Select(d => new DropDown
{
Id = d.Id,
Name = d.Name,
}).ToList();
But you don't want this, as here you are tying these two properties to dropdowns. Tomorrow you'll want an entity with Code instead of Id and Text instead of Name, so you'll have to add more interfaces, more overloads, and so on.
Instead you might want to use reflection, where you can specify the member names in the call:
List<DropDown> cities = BLL.GetDataList<City>(valueMember: c => c.CityCode, displayMember: c => c.FullCityname);
And use these member expressions to look up data's values and fill those into the DropDown.
However, you're then reinventing the wheel. Leave out your DropDown class entirely, and leave the dropdown generation to the front end, in this case MVC:
var cities = DataFetcher.FetchData<City>();
var selectList = new SelectList(cities.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCityId),
Text = c.FullCityName,
Value = c.CityCode,
});
Or:
var selectList = new SelectList(cities, "CityCode" , "FullCityName", selectedCityId);
One solution is to use AutoMapper.
First create a map between your models like this:
AutoMapper.Mapper.CreateMap<Role, DropDown>();
Do the same thing for City and State classes if you need to.
Then you can use AutpMapper to convert your objects to DropDown like this:
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data.Select(x => AutoMapper.Mapper.Map<DropDown>(x)).ToList();
}
If I understood the question correctly, you could use Linq as follows.
return data.Cast<DropDown>().ToList();

Categories

Resources