Generic method with multiple parameter - c#

I am trying to write a Generic method for the method CheckResult so that it could be used across different classes. For example, if I have a generic method then the only thing that is going to change is the classname. Here it is ClassA , another method could pass classB.
public bool CheckResult(Guid Id, List<ClassA> model,List<ClassA> existingEntities)
{
var ids = existingEntities?.Select(x => x.Id).Except(model.Select(x => x.Id)).ToList();
var check = existingEntities?.Where(o => ids.Any(c => c == o.Id && o.EffectiveTo >= DateTime.Today)).ToList();
check?.AddRange(model);
var dateModel = check.Select(x => new TimeInterval(x.EffectiveFrom, x.EffectiveTo)).ToList();
return true;
}
--------------This is what I was attempting to do---------------------
public static bool OpTest<T>(T model, T existingEntities, Guid t) where T : class
{
// var existingEntities = smRepository.GetStationMapping(t, StatusEnum.ALL); //smRepository.GetStationMapping(t, StatusEnum.ALL);
var ids = existingEntities?.Select(x => x.Id).Except(model.Select(x => x.Id)).ToList();
var check = existingEntities?.Where(o => ids.Any(c => c == o.Id && o.EffectiveTo >= DateTime.Today)).ToList();
check?.AddRange(model);
var dateModel = check.Select(x => new TimeInterval(x.EffectiveFrom, x.EffectiveTo)).ToList();
return true;
}
This is my first time writing generic any help is appreciated. The code is erroring out ?

I see a problem here. You're trying to access members on objects of type T, but T is an unknown type; it's not guaranteed to have the members you're trying to access that. C# doesn't like that.
I see two options here:
The robust option
Create an interface, IMyInterface, with the appropriate members. Then write this:
public static bool OpTest(IEnumerable<IMyInterface> model, IEnumerable<IMyInterface> existingEntities, Guid t)
{
List<SomeType> ids = existingEntities?.Select(x => x.Id).Except(model.Select(x => x.Id)).ToList();
List<IMyInterface> check = existingEntities?.Where(o => ids.Any(c => c == o.Id && o.EffectiveTo >= DateTime.Today)).ToList();
check?.AddRange(model);
List<TimeInterval> dateModel = check.Select(x => new TimeInterval(x.EffectiveFrom, x.EffectiveTo)).ToList();
return true;
}
The quick and dirty option
Use dynamic instead. Note that if you use this, your code will break at runtime if you make any mistakes. If you don't want it to break like that, then don't use this option.
public static bool OpTest(IEnumerable<dynamic> model, IEnumerable<dynamic> existingEntities, Guid t)
{
List<dynamic> ids = existingEntities?.Select(x => x.Id).Except(model.Select(x => x.Id)).ToList();
List<dynamic> check = existingEntities?.Where(o => ids.Any(c => c == o.Id && o.EffectiveTo >= DateTime.Today)).ToList();
check?.AddRange(model);
List<dynamic> dateModel = check.Select(x => new TimeInterval(x.EffectiveFrom, x.EffectiveTo)).ToList();
return true;
}

public static bool OpTest<T>(List<T> model, List<T> existingEntities, Guid t) where T : class
{
// var existingEntities = smRepository.GetStationMapping(t, StatusEnum.ALL); //smRepository.GetStationMapping(t, StatusEnum.ALL);
var ids = existingEntities?.Select(x => x.Id).Except(model.Select(x => x.Id)).ToList();
var check = existingEntities?.Where(o => ids.Any(c => c == o.Id && o.EffectiveTo >= DateTime.Today)).ToList();
check?.AddRange(model);
var dateModel = check.Select(x => new TimeInterval(x.EffectiveFrom, x.EffectiveTo)).ToList();
return true;
}
keep your items as list or you cant use linq on it.

Related

How to use Conditionals in NEST Queries Elasticsearch

I want to build a NEST query for Elasticsearch depending on the user input with an If Else statement. At the moment it only accepts one condition in the must part and the other one isn't added to the query.
The following code compiles to this http request:
{"from":0,"query":{"bool":{"must":[{"nested":{"path":"customer","query":{"term":{"customer.customerId":{"value":1}}}}}]}},"size":10}
as you can see the SearchPersonId isn't added into the must condition.
The search method:
private ISearchResponse<Country> GetFilteredResults(ElasticClient client, Query query)
{
var searchRequest = new SearchDescriptor<Country>();
searchRequest.From(0).Size(10).Query(q =>
{
q.Bool(b => b
.Must(mc =>
{
if (query.CustomerId != 0) mc.SearchCustomerId(query.CustomerId);
if (query.PersonId != 0) mc.SearchPersonId(query.PersonId);
return mc;
})
);
return q;
});
return client.Search<Country>(searchRequest);
}
The query methods:
public static class Helpers
{
public static QueryContainer SearchPersonId(this QueryContainerDescriptor<Country> container, string personId)
{
return container
.Nested(n => n
.Path(p => p.Person)
.Query(q => q
.Term(t => t
.Field(f => f.Person.PersonId).Value(personId))));
}
public static QueryContainer SearchCustomerId(this QueryContainerDescriptor<Country> container, string customerId)
{
return container
.Nested(n => n
.Path(p => p.Customer)
.Query(q => q
.Term(t => t
.Field(f => f.Customer.CustomerId).Value(customerId))));
}
}
One of the overloads of Must method accepts array of QueryContainer which can help you implement conditional logic
ISearchResponse<Country> GetFilteredResults(ElasticClient client, Query query)
{
var queryContainers = new List<QueryContainer>();
var descriptor = new QueryContainerDescriptor<Country>();
if (query.CustomerId != 0) queryContainers.Add(descriptor.SearchCustomerId(query.CustomerId));
if (query.PersonId != 0) queryContainers.Add(descriptor.SearchPersonId(query.PersonId));
var searchRequest = new SearchDescriptor<Country>();
searchRequest.From(0).Size(10).Query(q => q.Bool(b => b.Must(queryContainers.ToArray())));
return client.Search<Country>(searchRequest);
}

How do you call a function to an Linq List query that uses the exist function

I have this method with a linq statement below. I'm not a fan of multiple if statement and I'm trying to find what is the best way to not have these if statement and have a private method.
My field values is being set as such:
var fieldValues = await GetFields // then it's being passed to my method.
public static AppraisalContactBorrower BuildCoBorrower(List<LoanFieldValue> fieldValues) {
var coborrower = new AppraisalContactBorrower();
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.COBORRNAME")) {
coborrower.Name = fieldValues.First(v => v.FieldId == "CX.OS.AO.COBORRNAME").Value;
}
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.BORRCONTACTZIP")) {
borrower.Zip = fieldValues.First(v => v.FieldId == "CX.OS.AO.BORRCONTACTZIP").Value;
}
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.BORRCONTACTZIP")) {
borrower.Zip = fieldValues.First(v => v.FieldId == "CX.OS.AO.BORRCONTACTZIP").Value;
}
What I'm trying to do is instead of this:
coborrower.Name = fieldValues.First(v => v.FieldId == "CX.OS.AO.COBORRNAME").Value;
Is having something similar to this.
if (fieldValues.Exists(f => f.FieldId == "CX.OS.AO.BORRCONTACTZIP")) {
coborrower.Name = SETVALUE("CX.OS.AO.BORRCONTACTZIP")}
First, try using Enumerable.ToDictionary to have the field values grouped by FieldId, then use IDictionary.TryGetValue to get the existing values:
public static AppraisalContactBorrower BuildCoBorrower(List<LoanFieldValue> fieldValues) {
var groupedFieldValues = fieldValues.ToDictionary(f => f.FieldId)
var coborrower = new AppraisalContactBorrower();
if (groupedFieldValues.TryGetValue("CX.OS.AO.COBORRNAME", out var name)) {
coborrower.Name = name.Value;
}
if (groupedFieldValues.TryGetValue("CX.OS.AO.BORRCONTACTZIP", out var zip)) {
borrower.Zip = zip.Value;
}
}
Using Dictionary makes it faster to check the appropriate field existence as it is O(1) and with TryGetValue you combine two operations into one (existence check + obtaining the value).
Your two last statements are almost identitical. The equivalent of :
if (groupedFieldValues.TryGetValue("CX.OS.AO.COBORRNAME", out var name)) {
coborrower.Name = name.Value;
}
is:
coborrower.Name = fieldValues.FirstOrDefault(v => v.FieldId == "CX.OS.AO.COBORRNAME")
?? coborrower.Name;
In the original code, coborrower.Name is not updated if the field doesn't exist in the list.

List Custom Sort

I have a list of items which need to be sorted in a very particular way.
Take this example:
public class PayStubDetailItem
{
public string Code { get; set; }
}
void Main()
{
var bonus = new PayStubDetailItem() { Code = "Bonus" };
var ot = new PayStubDetailItem() { Code = "OT"};
var reg = new PayStubDetailItem() { Code = "Reg"};
var otPrem = new PayStubDetailItem() { Code = "OTPrem"};
var tps = new PayStubDetailItem() { Code = "3ps"};
var list = new List<PayStubDetailItem> {
bonus, ot, reg, otPrem, tps
};
}
My requirement states that sorting should be as follows:
Reg, OT, OTPrem, Alpha-sort by code.
Explained in words, if list contains 'Reg' code it should come first, if it also contains 'OT' it should come after Reg, etc.. All items which codes are different from those specific three should be alphabetically sorted.
In my example the sorted list should look like this:
Reg, OT, OTPrem, 3ps, Bonus
What would be the most elegant way to accomplish that? Perhaps, using LINQ or a custom comparer.
This is what I have attempted so far but it's to verbose:
var subList = list.Where(i => i.Code != "OT" && i.Code != "Reg" && i.Code != "OTPrem");
subList = subList.OrderBy(l => l.Code);
var newList = new List<PayStubDetailItem>();
if (list.Select(c => c.Code).Contains("Reg"))
{
newList.Add(list.Where(i => i.Code == "Reg").FirstOrDefault());
}
if (list.Select(c => c.Code).Contains("OT"))
{
newList.Add(list.Where(i => i.Code == "OT").FirstOrDefault());
}
if (list.Select(c => c.Code).Contains("OTPrem"))
{
newList.Add(list.Where(i => i.Code == "OTPrem").FirstOrDefault());
}
newList.AddRange(subList);
newList.Dump();
Thanks
You can use Linq like this:
var result = list.
.OrderBy(c => c.Code == "Reg" ? 0 : c.Code == "OT" ? 1 : c.Code == "OTPrem" ? 2 : 3)
.ThenBy(c => c.Code)
.ToList();
The OrderBy expression will give you the required priority order, while the ThenBy will do the alphabetical part.
As your sorting logic is quite unique to your problem, I would suggest an implementation the IComparer(T) interface and then calling Sort(IComparer(T)) on your list.
class MyComparer : IComparer<PayStubDetailItem>
{
public int Compare(PayStubDetailItem x, PayStubDetailItem y)
{
//Your implementation
}
}

linq-to-sql group by with count and custom object model

I'm looking to fill an object model with the count of a linq-to-sql query that groups by its key.
The object model looks somewhat like this:
public class MyCountModel()
{
int CountSomeByte1 { get; set; }
int CountSomeByte2 { get; set; }
int CountSomeByte3 { get; set; }
int CountSomeByte4 { get; set; }
int CountSomeByte5 { get; set; }
int CountSomeByte6 { get; set; }
}
This is what I have for the query:
var TheQuery = from x in MyDC.TheTable
where ListOfRecordIDs.Contains(x.RecordID) && x.SomeByte < 7
group x by x.SomeByte into TheCount
select new MyCountModel()
{
CountSomeByte1 = TheCount.Where(TheCount => TheCount.Key == 1)
.Select(TheCount).Count(),
CountSomeByte2 = TheCount.Where(TheCount => TheCount.Key == 2)
.Select(TheCount).Count(),
.....
CountSomeByte6 = TheCount.Where(TheCount => TheCount.Key == 6)
.Select(TheCount).Count(),
}.Single();
ListOfRecordIDs is list of longs that's passed in as a parameter. All the CountSomeByteN are underlined red. How do you do a count of grouped elements with the group's key mapped to an object model?
Thanks for your suggestions.
The select is taking each element of your group and projecting them to identical newly created MyCountModels, and you're only using one of them. Here's how I'd do it:
var dict = MyDC.TheTable
.Where(x => ListOfRecordIDs.Contains(x.RecordID) && x.SomeByte < 7)
.GroupBy(x => x.SomeByte)
.ToDictionary(grp => grp.Key, grp => grp.Count());
var result = new MyCountModel()
{
CountSomeByte1 = dict[1];
CountSomeByte2 = dict[2];
CountSomeByte3 = dict[3];
CountSomeByte4 = dict[4];
CountSomeByte5 = dict[5];
CountSomeByte6 = dict[6];
}
EDIT: Here's one way to do it in one statement. It uses an extension method called Into, which basically works as x.Into(f) == f(x). In this context, it can be viewed as like a Select that works on the whole enumerable rather than on its members. I find it handy for eliminating temporary variables in this sort of situation, and if I were to write this in one statement, it's probably how I'd do it:
public static U Into<T, U>(this T self, Func<T, U> func)
{
return func(self);
}
var result = MyDC.TheTable
.Where(x => ListOfRecordIDs.Contains(x.RecordID) && x.SomeByte < 7)
.GroupBy(x => x.SomeByte)
.ToDictionary(grp => grp.Key, grp => grp.Count())
.Into(dict => new MyCountModel()
{
CountSomeByte1 = dict[1];
CountSomeByte2 = dict[2];
CountSomeByte3 = dict[3];
CountSomeByte4 = dict[4];
CountSomeByte5 = dict[5];
CountSomeByte6 = dict[6];
});
Your range variable is not correct in the subqueries:
CountSomeByte6 = TheCount.Where(TheCount => TheCount.Key == 6)
.Select(TheCount).Count(),
In method notation you don't need the extra select:
CountSomeByte6 = TheCount.Where(theCount => theCount.Key == 6).Count(),
If you want to use it anyway:
CountSomeByte6 = TheCount.Where(theCount => theCount.Key == 6).Select(theCount => theCount).Count(),

Create Order by programming Linq/Lambda

I have this code :
public void CreateOrdering(string field, string direction)
{
//direction : ASC/DESC
var result = context.MyTable
.Where(x => x.Code > 5)
.OrderBy()
.Skip(10)
.Take(5)
.ToList<MyTable>();
}
I rephrase, I have a method, this method receive as string field name for ordering and the direction ("ASC", "DESC")
I'd like create a Order with the field and the direction received in argument. I have to be able to :
I'd like in this Query be able to do an ascending and descending
Set the ordering field by programming, here Code may be later Id or other ...
The ordering must be done on the SQL Server side not on the list returned
Thanks,
You may use reflection in an extension method which allows for linq syntax:
public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string field, string direction)
{
string orderByMethod = (direction == "ASC") ? "OrderBy" : (direction == "DESC" ? "OrderByDescending" : null);
if(orderByMethod == null) throw new ArgumentException();
var propertyInfo = typeof (TSource).GetProperty(field);
var entityParam = Expression.Parameter(typeof(TSource), "e");
Expression columnExpr = Expression.Property(entityParam, propertyInfo);
LambdaExpression columnLambda = Expression.Lambda(columnExpr, entityParam);
MethodInfo orderByGeneric = typeof (Queryable).GetMethods().Single(m => m.Name == orderByMethod
&& m.GetParameters().Count() == 2
&& m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
&& m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>));
MethodInfo orderBy = orderByGeneric.MakeGenericMethod(new [] {typeof(TSource), propertyInfo.PropertyType});
return (IQueryable<TSource>) orderBy.Invoke(null, new object[] { source, columnLambda });
}
Sample use:
internal class SomeType
{
public string StringValue { get; set; }
}
IQueryable<SomeType> l = new List<SomeType>
{
new SomeType {StringValue = "bbbbb"},
new SomeType {StringValue = "cccc"},
new SomeType {StringValue = "aaaa"},
new SomeType {StringValue = "eeee"},
}.AsQueryable();
var asc = l.OrderBy("StringValue", "ASC");
var desc = l.OrderBy("StringValue", "DESC");
Or for your example:
context.MyTable
.Where(x => x.Code > 5)
.OrderBy(field, direction)
.Skip(10)
.Take(5)
.ToList<MyTable>();
I may have misunderstood your question, but can't you just do:
Ascending
.OrderBy(x => x.Property)
Descending
.OrderByDescending(x => x.Property)
Update
What you need is Dynamic LINQ. However, what you are trying to do it could get quite complicated. As a simple workaround you could do something like:
var result = context.MyTable
.Where(x => x.Code > 15);
if (direction == "ASC")
{
result = result.OrderBy(field);
}
else
{
result = result.OrderByDescending(field);
}
result = result.Skip(10)
.Take(5)
.ToList<MyTable>();
void Main() {
// Ascending by some other property
CreateOrdering(item => item.SomeProperty, SortDirection.Ascending).Dump("Ascending order for SomeClass.SomeProperty");
// Descending by some other property
CreateOrdering(item => item.SomeProperty, SortDirection.Descending).Dump("Descending order for SomeClass.SomeProperty");
// Ascending by the Code property
CreateOrdering(item => item.Code, SortDirection.Ascending).Dump("Ascending order for SomeClass.Code");
// Descending by the Code property
CreateOrdering(item => item.Code, SortDirection.Descending).Dump("Descending order for SomeClass.Code");
}
// I reccomend not using bare strings, and instead use an enum
public enum SortDirection {
Ascending = 0,
Descending = 1
}
// Define other methods and classes here
public List<SomeClass> CreateOrdering<T>(Expression<Func<SomeClass, T>> field, SortDirection direction) {
// query does not get executed yet, because we have not enumerated it.
var query = context.MyTable
.Where(x => x.Code > 5);
if (direction.Equals(SortDirection.Ascending)) {
query = query.OrderBy (field);
} else {
query = query.OrderByDescending (field);
}
// query gets executed when the call ToList is made.
return query.Skip(10)
.Take(5)
.ToList();
}
public static class context {
private static List<SomeClass> _MyTable = new List<SomeClass>() {
new SomeClass("A", 4), new SomeClass("B", 5), new SomeClass("C", 6),
new SomeClass("D", 7), new SomeClass("E", 8), new SomeClass("F", 9),
new SomeClass("G", 10), new SomeClass("H", 11), new SomeClass("I", 12),
new SomeClass("J", 13), new SomeClass("K", 14), new SomeClass("L", 15),
new SomeClass("M", 16), new SomeClass("N", 17), new SomeClass("O", 18)
};
public static IQueryable<SomeClass> MyTable {
get {
return _MyTable.AsQueryable();
}
}
}
public class SomeClass {
public SomeClass(string property, int code) {
this.SomeProperty = property;
this.Code = code;
}
public string SomeProperty { get; set; }
public int Code { get; set; }
}
normally you would do this:
.OrderBy(x => x.yourField)
or
.OrderByDescending(x => x.yourField)
if you need your field to be dynamic, check this answer
If the field is passed as a string (for instance when using an ObjectDataSource), you can map it using a switch:
var qry = context
.MyTable
.Where(x => x.Code > 5);
switch(orderBy) {
case "MyField": qry = qry.OrderBy(r => r.MyField); break;
case "MyField DESC": qry = qry.OrderByDescending(r => r.MyField); break;
}
// By the way, ToList can infer the generic type if you don't
// want to state it explicity
var result = qry.Skip(10).Take(5).ToList();
The query is not executed before the ToList, and at least with EF it is executed on the SQL Server. I admit the switch is quite a lot of boilerplate, but it did turn out to be quite reliable and fast.

Categories

Resources