How to filter IQueryable whole tree? - c#

i have Contact query which contains Communication sub-query which contains CommunicationExtension sub-query
static class Program
{
public class Contact
{
public int ContactID { get; set; }
public List<Communication> Communications { get; set; }
public DateTime? DeletionDate { get; set; }
}
public class Communication
{
public int CommuncationID { get; set; }
public List<CommunicationExtension> CommunicationExtensions { get; set; }
public DateTime? DeletionDate { get; set; }
}
public class CommunicationExtension
{
public int CommunicationExtensionID { get; set; }
public int AreaCode { get; set; }
public DateTime? DeletionDate { get; set; }
}
static void Main(string[] args)
{
IQueryable<Contact> q = GenerateData();
IQueryable<Contact> result =
q.Where(c => c.DeletionDate == null &&
c.Communications.Where(co => co.DeletionDate == null &&
co.CommunicationExtensions.Where(ce => ce.DeletionDate == null));
}
private static IQueryable<Contact> GenerateData()
{
return new List<Contact>
{
new Contact
{
ContactID = 1,
DeletionDate = DateTime.Now,
Communications =
new List<Communication>
{
new Communication
{
CommuncationID = 1,
DeletionDate = DateTime.Now,
CommunicationExtensions =
new List<CommunicationExtension>
{
new CommunicationExtension
{
CommunicationExtensionID = 1,
AreaCode = 5,
DeletionDate = null
}
}
},
new Communication
{
CommuncationID = 2,
DeletionDate = null,
CommunicationExtensions =
new List<CommunicationExtension>
{
new CommunicationExtension
{
CommunicationExtensionID = 2,
AreaCode = 55,
DeletionDate = DateTime.Now
}
}
}
}
},
new Contact
{
ContactID = 2,
DeletionDate = null,
Communications =
new List<Communication>
{
new Communication
{
CommuncationID = 1,
DeletionDate = null,
CommunicationExtensions =
new List<CommunicationExtension>
{
new CommunicationExtension
{
CommunicationExtensionID = 3,
AreaCode = 54,
DeletionDate = null
}
}
},
new Communication
{
CommuncationID = 2,
DeletionDate = DateTime.Now,
CommunicationExtensions =
new List<CommunicationExtension>
{
new CommunicationExtension
{
CommunicationExtensionID = 4,
AreaCode = 5565,
DeletionDate = null
}
}
}
}
}
}.AsQueryable();
}
}
When i try to build it, i get the error:
Operator '&&' cannot be applied to operands of type 'bool' and 'System.Collections.Generic.IEnumerable'
on line:
IQueryable<Contact> result =
q.Where(c => c.DeletionDate == null &&
c.Communications.Where(co => co.DeletionDate == null &&
co.CommunicationExtensions.Where(ce => ce.DeletionDate == null)));
I need to filter all data that is not deleted (DeletionDate == null). In my scenario there is ~200 tables in database, each table contains nullable field DeletionDate

Here's the problem:
c.Communications.Where(co => co.DeletionDate == null &&
co.CommunicationExtensions.Where(ce => ce.DeletionDate == null)
Those two lines don't return a boolean, so your Where call doesn't know what to do with it. Are you wanting any Contact that has at least one non-deleted Communications and CommuincationExtensions? If so, then change the Where calls to Any and it should work. However, note that when actually dealing with your Contacts you'll still need to filter out any deleted Communications/CommunicationExtensions as the relationships will just get you the related records no matter what. In other words, contact.Communications will return all Communications related to that Contact, and comm.CommunicationExtensions will return all CommunicationExtensions, so you'll still need to filter out any deleted records whenever you deal with them.

Related

Mapping classes with reflection

I have 2 classes User and UserDto both have a Phones property, all the other properties I can map perfectly.
But the problem occurs with the Phones list of the User class, since being null, I cannot identify what type of list it is.
What I need is to be able to and create a list based on its type using reflection.
var list = new List<PhoneDto>();
list.Add(new PhoneDto { Code = 1, Number = 11111111 });
list.Add(new PhoneDto { Code = 2, Number = 11111112 });
list.Add(new PhoneDto { Code = 3, Number = 11111113 });
list.Add(new PhoneDto { Code = 4, Number = 11111114 });
list.Add(new PhoneDto { Code = 5, Number = 11111115 });
var userDto=new UserDto
{
Age=18,
IsData=true,
CreationDate=DateTime.Now,
Phones =list
};
var myMapper = new MyMapper();
var user= myMapper.Map<User>(userDto);
public class User
{
public string Name { get; set; }
public DateTime CreationDate { get; set; }
public int Age { get; set; }
public bool IsData { get; set; }
public ICollection.Phone. Phones { get; set; }
}
public class UserDto
{
public string Name { get; set; }
public DateTime CreationDate { get; set; }
public int Age { get; set; }
public bool IsData { get; set; }
public List.PhoneDto. Phones { get; set; }
}
namespace CustomMapping;
public class MyMapper
{
public T Map<T>(object sourceObject)
{
if (sourceObject == null) throw new Exception("Source object is null");
var instance = Activator.CreateInstance(typeof(T));
if (instance == null) throw new Exception("Can not create de typeof(T) Instance");
var sourceType = sourceObject.GetType();
var targetType = instance.GetType();
PropertyInfo[] sourceProperties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] targetProperties = targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var sourceProperty in sourceProperties)
{
if (sourceProperty == null) continue;
var targetProperty = targetProperties.FirstOrDefault(p => p.Name.ToLower() == sourceProperty.Name.ToLower() && p.CanWrite);
if (targetProperty == null) continue;
var sourceValue = sourceProperty?.GetValue(sourceObject, null);
if (sourceProperty?.PropertyType == targetProperty.PropertyType)
{
instance?.GetType()?.GetProperty(sourceProperty.Name)?.SetValue(instance, sourceValue);
continue;
}
var isEnumerable = IsEnumerable(targetProperty);
if (isEnumerable)
{
//---------Here I need to map the lists---------
}
}
return (T)instance;
}
private bool IsEnumerable(PropertyInfo propertyInfo)
{
return (propertyInfo.PropertyType.GetInterfaces().Any(p => p.Name == "IEnumerable`1") && propertyInfo.PropertyType.FullName.IndexOf("System.String") < 0);
}
}
I would appreciate any help.

Filter based on a string value in List<string> column in a table Entity Framework Core

I have a table with the following structure (code first approach using Entity Framework Core) in PostgreSQL
public class Product_Order
{
[Key]
public string product_number { get; set; }
public string customer_product_number { get; set; }
public List<string> product_statuses { get; set; }
public bool is_test { get; set; } = false;
public DateTime created_at { get; set; } = DateTime.UtcNow;
public DateTime updated_at { get; set; } = DateTime.UtcNow;
public string created_by { get; set; } = "system";
public string updated_by { get; set; } = "system";
}
Now, the product_statuses column usually contains of a list of statuses - ready, pickedup, scheduled, closed, cancelled.
I need to come up with a solution which returns me a list of product orders which DOES NOT CONTAIN orders which are closed or cancelled.
Here's the solution that I have at the moment which is not filtering as expected
_context.Product_Order.Where(t => t.is_test && !t.statuses.Contains("closed") && !t.statuses.Contains("cancelled")).ToList();
I think your code is ok for your data structure to find that information. I have created a dummy class and list to replicate your data and list. And I was able to find data by using you code. Sample Code given below what I have tested =>
void Test()
{
List<Product_Order> items = new List<Product_Order>();
var temp = new Product_Order() { product_number = "001", isTest = true };
temp.product_statuses = new List<string>();
temp.product_statuses.Add("good");
temp.product_statuses.Add("greate");
temp.product_statuses.Add("new");
items.Add(temp);
temp = new Product_Order() { product_number = "002", isTest = true };
temp.product_statuses = new List<string>();
temp.product_statuses.Add("good");
temp.product_statuses.Add("bad");
temp.product_statuses.Add("notnew");
items.Add(temp);
temp = new Product_Order() { product_number = "003", isTest = true };
temp.product_statuses = new List<string>();
temp.product_statuses.Add("n/a");
temp.product_statuses.Add("bad");
temp.product_statuses.Add("Closed");
items.Add(temp);
temp = new Product_Order() { product_number = "004", isTest = false };
temp.product_statuses = new List<string>();
temp.product_statuses.Add("n/a");
temp.product_statuses.Add("bad");
temp.product_statuses.Add("Cancelled");
items.Add(temp);
var finalOutput = items.Where(c => c.isTest == true && !c.product_statuses.Where(v => v.ToLower() == "closed").Any() && !c.product_statuses.Where(v => v.ToLower() == "cancelled").Any()).ToArray();
}
public class Product_Order
{
public string product_number { get; set; }
public bool isTest { get; set; }
public List<string> product_statuses { get; set; }
}
Finally , I think it is your data what not wright with you lambda expression. So, I modified for you a little bit.And that is
FINAL ANSWER:
var finalOutput = _context.Product_Order.Where(c => c.isTest == true && !c.product_statuses.Where(v => v.ToLower() == "closed").Any() && !c.product_statuses.Where(v => v.ToLower() == "cancelled").Any()).ToArray();
Please check my code and let me know.

Linq List problems checking null

Given the data below, I am trying to write a LINQ statement that will check if SubscriptionId is null by using (grp2.Select(y => y.SubscriptionId) == null). This must be failing because it will always go into the else section and return ,4 which is not what I am looking for. Is this the correct way to check if this value is null?
LINQPad Example
class Subscription
{
public int? SubscriptionId { get; set; }
public int ParentProductId { get; set; }
public string ParentProductName { get; set; }
public string ChildProductName { get; set; }
public int ChildProductId { get; set; }
public int GroupId { get; set; }
public DateTime? EndDate { get; set; }
}
class SubscriptionViewModel
{
public int SubscriptionId { get; set; }
public int ParentProductId { get; set; }
public string ParentProductName { get; set; }
public string SubscriptionIds { get; set; }
public int GroupId { get; set; }
}
class SubscriptionChildViewModel
{
public string ChildProductName { get; set; }
public int ChildProductId { get; set; }
}
void Main()
{
List<Subscription> ListOfSubscription = new List<Subscription>();
ListOfSubscription.Add(new Subscription() { SubscriptionId = null, ParentProductId = 4, ChildProductId = 4, ParentProductName = "Product 1", ChildProductName = "Product 1", GroupId = 362, });
ListOfSubscription.Add(new Subscription() { SubscriptionId = 2, ParentProductId = 114, ChildProductId = 1, ParentProductName = "Product 2", ChildProductName = "Product 3", GroupId = 1, });
ListOfSubscription.Add(new Subscription() { SubscriptionId = 3, ParentProductId = 114, ChildProductId = 2, ParentProductName = "Product 2", ChildProductName = "Product 4", GroupId = 1, });
var groupedSubscriptions = ListOfSubscription.GroupBy(u => u.GroupId);
var result = groupedSubscriptions.Select(grp1 => new
{
GroupId = grp1.Key,
Subscriptions = grp1.GroupBy(subscr => new
{
subscr.ParentProductId,
subscr.ParentProductName,
})
.Select(grp2 => new SubscriptionViewModel
{
GroupId = grp1.Key,
ParentProductId = grp2.Key.ParentProductId,
ParentProductName = grp2.Key.ParentProductName,
SubscriptionIds = (grp2.Select(y => y.SubscriptionId) == null) ? null : (string.Format("{0},{1}", string.Join(",", grp2.Select(y => y.SubscriptionId)), grp2.Key.ParentProductId))
})
});
var x = result.SelectMany((s => s.Subscriptions));
Console.Write(x);
}
Change your logic
(grp2.Select(y => y.SubscriptionId) == null)
to
grp2.Where(y => y.SubscriptionId == null).Count() > 0
Remove that bracket y.SubscriptionId)
This could work for you, to work with nulleables you can use "HasValue" instead of directly comparing with null.
SubscriptionIds = (grp2.Select( y => y.SubscriptionId.HasValue ) == null) ? null : (string.Format("{0},{1}", string.Join(",", grp2.Select(y => y.SubscriptionId.HasValue)), grp2.Key.ParentProductId))
Microsoft suggests using HasValue for nulleables types

C# Reactive Extensions (rx) FirstOrDefault enumerates entire collection

It seems that the expected behavior of FirstOrDefault is to complete after finding an item that matches the predicate and the expected behavior of concat is to evaluate lazily. However, the following example enumerates the entire collection even though the predicate matches the first item.
(Thanks for the friendlier code Shlomo)
void Main()
{
var entities = Observable.Defer(() => GetObservable().Concat());
Entity result = null;
var first = entities.FirstOrDefaultAsync(i => i.RowId == 1).Subscribe(i => result = i);
result.Dump();
buildCalled.Dump();
}
// Define other methods and classes here
public IEnumerable<IObservable<Entity>> GetObservable()
{
var rows = new List<EntityTableRow>
{
new EntityTableRow { Id = 1, StringVal = "One"},
new EntityTableRow { Id = 2, StringVal = "Two"},
};
return rows.Select(i => Observable.Return(BuildEntity(i)));
}
public int buildCalled = 0;
public Entity BuildEntity(EntityTableRow entityRow)
{
buildCalled++;
return new Entity { RowId = entityRow.Id, StringVal = entityRow.StringVal };
}
public class Entity
{
public int RowId { get; set; }
public string StringVal { get; set; }
}
public class EntityTableRow
{
public int Id { get; set; }
public string StringVal { get; set; }
}
Is this the expected behavior? Is there a way to defer the enumeration of the objects (specifically the building in this case) until truly needed?
The following is Linqpad-friendly code equivalent to what you have:
void Main()
{
var entities = Observable.Defer(() => GetObservable().Concat());
Entity result = null;
var first = entities.FirstOrDefaultAsync(i => i.RowId == 1).Subscribe(i => result = i);
result.Dump();
buildCalled.Dump();
}
// Define other methods and classes here
public IEnumerable<IObservable<Entity>> GetObservable()
{
var rows = new List<EntityTableRow>
{
new EntityTableRow { Id = 1, StringVal = "One"},
new EntityTableRow { Id = 2, StringVal = "Two"},
};
return rows.Select(i => Observable.Return(BuildEntity(i)));
}
public int buildCalled = 0;
public Entity BuildEntity(EntityTableRow entityRow)
{
buildCalled++;
return new Entity { RowId = entityRow.Id, StringVal = entityRow.StringVal };
}
public class Entity
{
public int RowId { get; set; }
public string StringVal { get; set; }
}
public class EntityTableRow
{
public int Id { get; set; }
public string StringVal { get; set; }
}
If you change GetObservable to the following, you'll get the desired result:
public IObservable<IObservable<Entity>> GetObservable()
{
var rows = new List<EntityTableRow>
{
new EntityTableRow { Id = 1, StringVal = "One"},
new EntityTableRow { Id = 2, StringVal = "Two"},
};
return rows.ToObservable().Select(i => Observable.Return(BuildEntity(i)));
}
It appears the implementation of Concat<TSource>(IEnumerable<IObservable<TSource>>) is eager in evaluating the enumerable, whereas the implementation of Concat<TSource>(IObservable<IObservable<TSource>>) and ToObservable<TSource>(IEnumerable<TSource>) maintain laziness appropriately. I can't say I know why.

How can I create set contain id record in first set equal id record in second set?

In database I have two tables:
public partial class PersonOne
{
public int id { get; set; }
public string name { get; set; }
public string surname { get; set; }
}
public partial class PersonTwo
{
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
}
I would like to fill my set:
public class PersonOnePersonTwo
{
public int PersonOneId { get; set; }
public int PersonTwoId { get; set; }
}
where PersonOne.name == PersonTwo.firstname && PersonOne.surname == PersonTwo.lastname but I have no idea how I can do that - because below code isn't efficient and is so slow:
List<PersonOne> personOneList = new List<PersonOne>();
List<PersonTwo> personTwoList = new List<PersonTwo>();
List<PersonOnePersonTwo> personOnePersonTwoList = new List<PersonOnePersonTwo>();
foreach (PersonOne personOne in personOneList)
{
foreach(PersonTwo personTwo in personTwoList.Where(x => x.firstname == personOne.name && x.lastname == personOne.surname).ToList())
{
personOnePersonTwoList.Add(new PersonOnePersonTwo
{
PersonOneId = personOne.id,
PersonTwoId = personTwo.id
});
}
};
Try this:
var result = personOneList.Join
(
personTwoList,
person1 => new { Key1 = person1.Name, Key2 = person1.Surname },
person2 => new { Key1 = person2.FirstName, Key2 = person2.LastName },
(person1, person2) => new PersonOnePersonTwo { PersonOneId = person1.Id, PersonTwoId = person2.Id }
).ToList();
I would go with:
var personOnePersonTwoList = new List<PersonOnePersonTwo>();
foreach (var personOne in personOneList)
{
personOnePersonTwoList = personTwoList.Where(x => x.firstname.Equals(personOne.name, StringComparison.OrdinalIgnoreCase) &&
x.lastname.Equals(personOne.surname, StringComparison.OrdinalIgnoreCase))
.Select(x => new PersonOnePersonTwo {PersonOneId = personOne.id, PersonTwoId = x.id}).ToList();
};
As a side note: it's more convinient to use Equals when comparing strings.

Categories

Resources