Get property value of object via index - c#

Issue:
I have an object with properties that models the hours of the day. I would like to know if it's possible to index the properties of this object similar to how they are indexed in an array or dictionary and then get these values by their index.
Example:
internal class HoursOfDay
{
[JsonProperty("hour00", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour00 { get; set; }
[JsonProperty("hour01", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour01 { get; set; }
[JsonProperty("hour02", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour02 { get; set; }
[JsonProperty("hour03", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour03 { get; set; }
[JsonProperty("hour04", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour04 { get; set; }
[JsonProperty("hour05", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour05 { get; set; }
[JsonProperty("hour06", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour06 { get; set; }
[JsonProperty("hour07", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour07 { get; set; }
[JsonProperty("hour08", NullValueHandling = NullValueHandling.Ignore)]
public HourDetailsExample Hour08 { get; set; }
...
I would like to be able to access the properties of this object like this if possible:
var hoursOfDay = new HoursOfDay();
var h1 = hoursOfDay["0"] // or hoursOfDay[0] or hoursOfDay["hour00"]
I understand I could just turn this object into an array or a dictionary but I was just curious if indexing on the properties of an object is possible.
Edit I also understand this is something I can do with reflection. I want to know if I can do with indexing.
Edit2 Similarly, I also would like to know how to update the setter: example:
var h1[0] = new HourDetailsExample("exampleData");
** Edit3** I've learned this can be added by adjusting the below answers to add a setter like so:
set
{
switch (index)
{
case 0:
Hour00 = value; break;
case 1:
Hour01 = value; break;
...
default: break;
}
}

You can create an indexer in your class
public HourDetailsExample this[int index] => index switch {
0 => Hour00,
1 => Hour01,
2 => Hour02,
3 => Hour03,
4 => Hour04,
5 => Hour05,
6 => Hour06,
7 => Hour07,
8 => Hour08,
_ => throw new IndexOutOfRangeException()
};
and you can overload it with a string index variant:
public HourDetailsExample this[string index] => index.ToLowerInvariant() switch {
"0" or "hour00" => Hour00,
"1" or "hour01" => Hour01,
"2" or "hour02" => Hour02,
"3" or "hour03" => Hour03,
"4" or "hour04" => Hour04,
"5" or "hour05" => Hour05,
"6" or "hour06" => Hour06,
"7" or "hour07" => Hour07,
"8" or "hour08" => Hour08,
_ => throw new IndexOutOfRangeException()
};
Now, all these variants are possible:
var hoursOfDay = new HoursOfDay();
var h1 = hoursOfDay[0];
var h2 = hoursOfDay["1"];
var h3 = hoursOfDay["Hour02"];
var h4 = hoursOfDay["hour03"];
The indexer can also have a setter, if you need to assign values by indexing:
public HourDetailsExample this[int index]
{
get => index switch {
0 => Hour00,
...
_ => throw new IndexOutOfRangeException()
};
set {
switch (index) {
case 0: Hour00 = value; break;
case 1: Hour01 = value; break;
case 2: Hour02 = value; break;
case 3: Hour03 = value; break;
case 4: Hour04 = value; break;
case 5: Hour05 = value; break;
case 6: Hour06 = value; break;
case 7: Hour07 = value; break;
case 8: Hour08 = value; break;
default: throw new IndexOutOfRangeException();
}
}
}

You can always create indexer for the type:
internal class HoursOfDay
{
// ...
public HourDetailsExample this[int index]
{
get => index switch
{
0 => Hour00,
1 => Hour01,
// ...
_ => throw new ArgumentOutOfRangeException(nameof(index), index, null) // or return null
};
}
}
If you don't want to write everything by hand - you can use some reflection but there would be some performance hit (though it can be mostly negated by "caching" it - see this answer for some inspiration).

Related

Automapper help mapping a complex c# objects

I am trying to map from Vehicle object to the Motor object using Automapper
public class Range<T>
{
public T Min { get; set; }
public T Max { get; set; }
}
public Enum SpeedType
{
[Display(Name = "-")] Unknown = 0,
[Display(Name = "M")] Manual= 1,
[Display(Name = "A")] Automatic= 2
}
public class Vehicle
{
public Range<string> Speed { get; set; }
}
public class Motor
{
public Range<SpeedType?> Speed { get; set; }
}
I have tried using the MapFrom (reading the documentation) without any success. Can someone point me in the right direction. I am not even sure if this can be even mapped using Automapper. I have used automapper in the past for simple mappings.
This works for me:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<string, SpeedType?>().ConvertUsing(speed =>
{
switch (speed)
{
case "M": return SpeedType.Manual;
case "A": return SpeedType.Automatic;
default: return SpeedType.Unknown;
}
});
cfg.CreateMap<Range<string>, Range<SpeedType?>>();
cfg.CreateMap<Vehicle, Motor>();
});
var vehicle = new Vehicle
{
Speed = new Range<string>
{
Min = "M",
Max = "A"
}
};
var motor = Mapper.Map<Vehicle, Motor>(vehicle);
I came up with the following mapping to solve my problem. I wrote a GetEnum custom method
CreateMap<Vehicle,Motor>()
.ForMember(g => g.Speed, opt => opt.MapFrom(u=> new Range<SpeedType?>
{
Min = EnumHelper.GetEnum<SpeedType?>(u.Speed.Min),
Max = EnumHelper.GetEnum<SpeedType?>(u.Speed.Max),
}))

Issue with Binding a List of complex type object - Dotnet Core 2.1 Razor Pages

I'm using Razor pages for my project on dotnet core 2.1, and the application doesn't seem to bind my properties correctly, the simple types (int and string types) binds correctly but not the list of complex types, is there a work around for this?
my page handler looks like this:
public async Task<IActionResult> OnGetDTResponseAsync(DataTableOptions options) {// Some Code}
When I step through with my debugger all simple type properties for "DataTableOptions options" are well populated but the complex type returns null.
my model looks like this :
public class DataTableOptions
{
public string Draw { get; set; }
public int Start { get; set; }
public int Length { get; set; }
public List<DataTableColumnOrder> Order { get; set; }
public List<DataTableColumn> Columns { get; set; }
public DataTableColumnSearch Search { get; set; }
public List<string> Params { get; set; }
public DataTableOptions() { }
public class DataTableColumn
{
public string Data { get; set; }
public string Name { get; set; }
public bool Searchable { get; set; }
public bool Orderable { get; set; }
public DataTableColumnSearch Search { get; set; }
public DataTableColumn() { }
}
public class DataTableColumnSearch
{
public string Value { get; set; }
public bool Regex { get; set; }
public DataTableColumnSearch() { }
}
public class DataTableColumnOrder
{
public int Column { get; set; }
public string Dir { get; set; }
public DataTableColumnOrder() { }
}
}
While trying to solve this, I tried using
public async Task<IActionResult> OnGetDTResponseAsync(List<Dictionary<string, string>> columns)
in my page handler in place of the columns property of DataTableOptions so i could manually bind the properties to my class: I got a full list of my columns with it's properties binded to it, except for the DataTableColumn's DataTableColumnSearch property which is also a complex type that came out as null.
public async Task<IActionResult> OnGetDTResponseAsync(List<Dictionary<string, object>> columns)
doesn't work either.
This is what the request looks like in fiddler:
GET /CMS/Index?handler=DTResponse&draw=1&columns%5B0%5D%5Bdata%5D=id&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=false&columns%5B0%5D%5Borderable%5D=false&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=name&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=webPage.name&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=value&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=true&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B4%5D%5Bdata%5D=contentType.name&columns%5B4%5D%5Bname%5D=&columns%5B4%5D%5Bsearchable%5D=true&columns%5B4%5D%5Borderable%5D=true&columns%5B4%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B4%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B5%5D%5Bdata%5D=&columns%5B5%5D%5Bname%5D=&columns%5B5%5D%5Bsearchable%5D=false&columns%5B5%5D%5Borderable%5D=false&columns%5B5%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B5%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=2&order%5B0%5D%5Bdir%5D=asc&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1545122652329 HTTP/1.1
I had to build a custom model binding class to handle this scenario.
For some reason a Collection List of a complex object that has another complex object as part of it's properties can't be automatically binded correctly in core 2.1 -Razor pages.
See my solution below:
using Microsoft.AspNetCore.Mvc.ModelBinding;
using RestaurantDataModel.Data.Requests;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ExampleDataModel.Data
{
public class CustomDataTableEntityBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var allValues = bindingContext.HttpContext.Request.Query;
DataTableOptions DTOs = new DataTableOptions {
Draw = allValues.FirstOrDefault(a => a.Key == "draw").Value,
Start = Convert.ToInt32(allValues.FirstOrDefault(a => a.Key == "start").Value),
Length = Convert.ToInt32(allValues.FirstOrDefault(a => a.Key == "length").Value)
};
if (allValues.Any(a => a.Key.Length > 9 && a.Key.Substring(0, 9).Contains("columns")))
{
var myListIndex = 0;
var myListIndexComparer = 0;
var allColumns = allValues.Where(a => a.Key.Length > 9 && a.Key.Substring(0, 9).Contains("columns")).ToList();
DataTableColumn DTC = new DataTableColumn();
DataTableColumnSearch DTCS = new DataTableColumnSearch();
DTOs.columns = new List<DataTableColumn>();
foreach (var column in allColumns)
{
var perColumnArray = column.Key.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
var rawIndex = perColumnArray[1];
if (!int.TryParse(rawIndex, out myListIndex))
{
return Task.CompletedTask;
}
if (myListIndexComparer != myListIndex)
{
DTC.search = DTCS;
DTOs.columns.Add(DTC);
DTC = new DataTableColumn();
DTCS = new DataTableColumnSearch();
}
myListIndexComparer = myListIndex;
switch (perColumnArray[2])
{
case "data":
DTC.data = column.Value;
break;
case "name":
DTC.name = column.Value;
break;
case "searchable":
DTC.searchable = String.IsNullOrWhiteSpace(column.Value) ? false : Convert.ToBoolean(column.Value);
break;
case "orderable":
DTC.orderable = String.IsNullOrWhiteSpace(column.Value) ? false : Convert.ToBoolean(column.Value);
break;
case "search":
if (perColumnArray[3] == "regex")
{
DTCS.regex = String.IsNullOrWhiteSpace(column.Value) ? false : Convert.ToBoolean(column.Value);
}
if (perColumnArray[3] == "value")
{
DTCS.value = column.Value;
}
break;
}
if(allColumns.IndexOf(column) == allColumns.IndexOf(allColumns.Last()))
{
DTC.search = DTCS;
DTOs.columns.Add(DTC);
}
}
}
if (allValues.Any(a => a.Key.Length > 7 && a.Key.Substring(0, 7).Contains("order")))
{
var myListIndex = 0;
var myListIndexComparer = 0;
var allOrders = allValues.Where(a => a.Key.Length > 7 && a.Key.Substring(0, 7).Contains("order")).ToList();
DataTableColumnOrder DTCO = new DataTableColumnOrder();
DTOs.order = new List<DataTableColumnOrder>();
foreach (var order in allOrders)
{
var perColumnArray = order.Key.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
var rawIndex = perColumnArray[1];
if (!int.TryParse(rawIndex, out myListIndex))
{
return Task.CompletedTask;
}
if (myListIndexComparer != myListIndex)
{
DTOs.order.Add(DTCO);
DTCO = new DataTableColumnOrder();
}
myListIndexComparer = myListIndex;
switch (perColumnArray[2])
{
case "column":
DTCO.Column = Convert.ToInt32(order.Value);
break;
case "dir":
DTCO.Dir = order.Value;
break;
}
if(allOrders.IndexOf(order) == allOrders.IndexOf(allOrders.Last()))
{
DTOs.order.Add(DTCO);
}
}
}
if (allValues.Any(a => a.Key.Length > 7 && a.Key.Substring(0, 8).Contains("search")))
{
var allSearches = allValues.Where(a => a.Key.Length > 8 && a.Key.Substring(0, 8).Contains("search")).ToList();
DataTableColumnSearch DTCS = new DataTableColumnSearch();
DTOs.search = new DataTableColumnSearch();
foreach (var search in allSearches)
{
var perColumnArray = search.Key.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
switch (perColumnArray[1])
{
case "value":
DTCS.value = search.Value;
break;
case "regex":
DTCS.regex = String.IsNullOrWhiteSpace(search.Value) ? false : Convert.ToBoolean(search.Value);
break;
}
if(allSearches.IndexOf(search) == allSearches.IndexOf(allSearches.Last()))
{
DTOs.search = DTCS;
}
}
}
bindingContext.Result = ModelBindingResult.Success(DTOs);
return Task.CompletedTask;
}
}
}
And then I added this to the top of my model class:
[ModelBinder(BinderType = typeof(CustomDataTableEntityBinder))]

Iterate through any Property, nested objects and Lists C#

I get 2 objects of Type "Shipment" ("Shipment1" and "Shipment2") and must read each value of them. If the value of Shipment1 is NULL/empty I want to look into the same Value of Shipment2 and if the Value is not NULL/empty I have to copy it to Shipment1.
I tried to iterate through my objects with Reflection but the nested Objects "Consignor", "Consignee", "Invoices" let me fail. I hope you can help me.
I have the following simplified class structure:
public class Shipment
{
public int Id { get; set; }
public Address Consignor { get; set; }
public Address Consignee { get; set; }
public IEnumerable<Invoice> Invoices{ get; set; }
}
public class Address
{
public string Name { get; set; }
public string Street{ get; set; }
public string Zip { get; set; }
public string City{ get; set; }
public string Country{ get; set; }
}
public class Invoice
{
public IEnumerable<Item> Items{ get; set; }
}
public class Item
{
public string Description{ get; set; }
public int Amount { get; set; }
}
I tried it this way. It worked for the top level properties of shipment, but not for Consignor, Consignee, Invoices etc.
foreach (PropertyInfo info1 in shipment1.GetType().GetProperties())
{
var datatype = info1.PropertyType;
switch (datatype.Name.ToLower())
{
case "string":
if (!string.IsNullOrEmpty((string)info1.GetValue(shipment1)))
{
string value= (string)info1.GetValue(shipment1);
string name = info1.Name;
Type type = input.GetType();
PropertyInfo info2 = shipment2.GetType().GetProperty(name);
if (string.IsNullOrEmpty((string)info2.GetValue(shipment2)))
{
info2.SetValue(shipment2, value, null);
}
}
break;
case "integer":
// and so on
}
}
I ended up with this, having shipment1 with the same values that shipment2, even if shipment1 was null at the beginning.
Please note that it copies IEnumerable like pointers. After the copy, editing copy.Invoices will also edit source.Invoices, and vice versa.
// used for my test:
Shipment shipment1 = null;
Shipment shipment2 = new Shipment { Id = 42, Consignor = new Address { Name = "Foo1", Street = "Bar1", Zip = "Baz1", City = "Qux1", Country = "Quux1" }, Consignee = new Address { Name = "Foo2", Street = "Bar2", Zip = "Baz2", City = "Qux2", Country = "Quux2" }, Invoices = new Invoice[] { new Invoice { Items = new Item[] { new Item { Description = "FooBar1", Amount = 1 }, new Item { Description = "BazQux1", Amount = 1 } } }, new Invoice { Items = new Item[] { new Item { Description = "FooBar2", Amount = 2 }, new Item { Description = "BazQux2", Amount = 2 } } } } };
// kind of ugly but I didn't manage to do it prettier:
shipment1 = Work(shipment2, shipment1);
private T Work<T>(T source, T copy)
{
if (source == null)
return copy;
if (copy == null)
copy = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in typeof(T).GetProperties())
{
switch (prop.PropertyType.Name.ToLower())
{
case "string":
string str = (string)prop.GetValue(source);
if (!string.IsNullOrEmpty(str))
if (string.IsNullOrEmpty((string)prop.GetValue(copy)))
prop.SetValue(copy, str);
break;
case "int32":
int i = (int)prop.GetValue(source);
if (i != 0)
if ((int)prop.GetValue(copy) == 0)
prop.SetValue(copy, i);
break;
case "address":
prop.SetValue(copy, Work(prop.GetValue(source) as Address, prop.GetValue(copy) as Address));
break;
case "ienumerable`1":
switch (prop.PropertyType.GetGenericArguments()[0].Name.ToLower())
{
case "invoice":
IEnumerable<Invoice> invoices = (IEnumerable<Invoice>)prop.GetValue(source);
if (invoices != null && invoices.Count() > 0)
if ((IEnumerable<Invoice>)prop.GetValue(copy) == null)
prop.SetValue(copy, invoices);
break;
// edit: this is actually useless
/*
case "item":
IEnumerable<Item> items = (IEnumerable<Item>)prop.GetValue(source);
if (items != null && items.Count() > 0)
if ((IEnumerable<Item>)prop.GetValue(copy) == null)
prop.SetValue(copy, items);
break;
*/
}
break;
}
}
return copy;
}

Sorting and Updating a Generic List of Object based on a Sub Object

I have the following objects:
public class TestResult
{
public string SectionName { get; set; }
public int Score { get; set; }
public int MaxSectionScore { get; set; }
public bool IsPartialScore { get; set; }
public string Name { get; set; }
public int NumberOfAttempts { get; set; }
}
public class TestResultGroup
{
public TestResultGroup()
{
Results = new List<TestResult>();
Sections = new List<string>();
}
public List<TestResult> Results { get; set; }
public List<string> Sections { get; set; }
public string Name { get; set; }
public int Rank { get; set; }
}
So, a TestResultGroup can have any number of results of type TestResult. These test results only differ by their SectionName.
I have a List<TestResultGroup> which I need to sort into descending order based on a score in the Results property, but only when Results has an item whos SectionName = "MeanScore" (if it doesnt have this section we can assume a score of -1). How would I go about ordering the list? Ideally I would also like to apply the result of this ordering to the Rank property.
Many Thanks
List<TestResultGroup> groups = ...
// group test result groups by the same score and sort
var sameScoreGroups = groups.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(gr => gr.Key);
int rank = 1;
foreach (var sameScoreGroup in sameScoreGroups)
{
foreach (var group in sameScoreGroup)
{
group.Rank = rank;
}
rank++;
}
// to obtain sorted groups:
var sortedGroups = groups.OrderByDescending(gr => gr.Rank).ToArray();
Or even write one expression with a side effect:
List<TestResultGroup> groups = ...
int rank = 1;
var sortedGroups = groups
.GroupBy(
gr =>
{
var meanResult = gr.Results.FirstOrDefault(res => res.SectionName == "MeanScore");
return meanResult != null ? meanResult.Score : -1;
})
.OrderByDescending(grouping => grouping.Key)
.SelectMany(grouping =>
{
int groupRank = rank++;
foreach (var group in grouping)
{
group.Rank = groupRank;
}
return grouping;
})
.ToArray(); // or ToList

Add to list the first type missing objects from list the second type

I have two lists different types. The first list is type of below class:
public partial class PersonOneProductOne
{
public int PersonOneId { get; set; }
public int ProductOneId { get; set; }
}
The second list is type of below class:
public partial class PersonTwoProductTwo
{
public int PersonTwoId { get; set; }
public int ProductTwoId { get; set; }
}
I want to add to list type of PersonOneProductOne missing objects from list type of PersonTwoProductTwo but unfortunatelly it isn't so easy because I have also lists how to map PersonOneId and ProductOneId from PersonOneProductOne class to PersonTwoId and ProductTwoId in PersonTwoProductTwo class:
public class PersonOnePersonTwo
{
public int PersonOneId { get; set; }
public int PersonTwoId { get; set; }
}
public class ProductOneProductTwo
{
public int ProductOneId { get; set; }
public int ProductTwoId { get; set; }
}
I have written below code but it is slow, is better way??
// data:
List<PersonOneProductOne> personOneProductOneList = new List<PersonOneProductOne>();
List<PersonTwoProductTwo> personTwoProductTwoList = new List<PersonTwoProductTwo>();
// how to map:
List<PersonOnePersonTwo> personOnePersonTwoMapping = new List<PersonOnePersonTwo>();
List<ProductOneProductTwo> productOneProductTwoMapping = new List<ProductOneProductTwo>();
// my algorithm which is slow:
foreach (PersonTwoProductTwo personTwoProductTwo in personTwoProductTwoList)
{
PersonOnePersonTwo personOnePersonTwo = personOnePersonTwoMapping.FirstOrDefault(x => x.PersonTwoId == personTwoProductTwo.PersonTwoId);
ProductOneProductTwo productOneProductTwo = productOneProductTwoMapping.FirstOrDefault(x => x.ProductTwoId == personTwoProductTwo.ProductTwoId);
if (personOnePersonTwo != null && productOneProductTwo != null)
{
PersonOneProductOne personOneProductOne = personOneProductOneList.FirstOrDefault(x => x.PersonOneId == personOnePersonTwo.PersonOneId && x.ProductOneId == productOneProductTwo.ProductOneId);
if (personOneProductOne == null)
{
personOneProductOneList.Add(new PersonOneProductOne
{
PersonOneId = personOnePersonTwo.PersonOneId,
ProductOneId = productOneProductTwo.ProductOneId
});
}
}
}
The code fragment below is not going to speed up your query. It is a translation of your current logic into a single LINQ query. I included it, because it makes your current logic a lot easier to follow (i.e. it is more readable), which is always a good starting point when attempting an optimization.
var result =
from person2product2 in personTwoProductTwoList
from person1person2 in personOnePersonTwoMapping
from product1product2 in productOneProductTwoMapping
where person2product2.PersonTwoId == person1person2.PersonTwoId &&
person2product2.ProductTwoId == product1product2.ProductTwoId &&
!personOneProductOneList.Any(x =>
x.PersonOneId == person1person2.PersonOneId &&
x.ProductOneId == product1product2.ProductOneId)
select new PersonOneProductOne {
PersonOneId = person1person2.PersonOneId,
ProductOneId = product1product2.ProductOneId
};
personOneProductOneList.AddRange(result);
An obvious thing that could be optimized, is to use use dictionaries with O(1) lookup time instead of your "mapping" lists, that will have O(N) lookup time.
When using dictionaries and assuming each person id and product id has an entry, this could then be transformed into:
// Maps PersonTwoId (key) to PersonOneId (value)
Dictionary<int, int> personTwoPersonOneMapping;
// Maps ProductTwoId (key) to ProductOneId (value)
Dictionary<int, int> productTwoProductOneMapping;
var result =
from person2product2 in personTwoProductTwoList
where !personOneProductOneList.Any(x =>
x.PersonOneId == personTwoPersonOneMapping[person2product2.PersonTwoId] &&
x.ProductOneId == productTwoProductOneMapping[person2product2.ProductTwoId])
select new PersonOneProductOne {
PersonOneId = personTwoPersonOneMapping[person2product2.PersonTwoId],
ProductOneId = productTwoProductOneMapping[person2product2.ProductTwoId]
};

Categories

Resources