Applying expression functions - linq

I need to implement filter function with expression parameter.
So i can't apply filtered query to entity.
Entity :
[XmlRoot(ElementName = "Zip")]
public class Zip
{
[XmlAttribute(AttributeName = "code")]
public string Code { get; set; }
}
[XmlRoot(ElementName = "District")]
public class District
{
[XmlElement(ElementName = "Zip")]
public List<Zip> Zip { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
}
[XmlRoot(ElementName = "City")]
public class City
{
[XmlElement(ElementName = "District")]
public List<District> District { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "code")]
public string Code { get; set; }
}
[XmlRoot(ElementName = "AddressInfo")]
public class AddressInfo
{
[XmlElement(ElementName = "City")]
public List<City> City { get; set; }
}
Test case filtered by City name "Berlin". How can apply predicate with function.
public IConverter<T> Filter(Expression<Func<T, bool>> predicate)
{
// ???
return this;
}

I presume you need to filter a collection with a given predicate.
You can define a Filter extension method that takes a predicate as an argument (or simply rely on the already existing collection.Where extension method)
public static class Extensions
{
public static IEnumerable<T> Filter<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
{
return collection.Where(predicate);
}
}
define predicates based on your needs
// Filter by city Berlin
Func<City, bool> berlin = city => city.Name == "Berlin";
// Filter by district Spandau
Func<City, bool> spandau = city => city.Districts.Any(d => d.Name == "Spandau");
// Filter by zip 10115
Func<City, bool> zipcode = city =>
{
var predicate = from district in city.Districts
from zip in district.Zips
where zip.Code == "10115"
select zip;
return predicate.Any();
};
filter the data based on given predicate
var query = from address in addresses
from city in address.Cities.Filter(berlin)
select city;

Related

LINQ how to order on a referenced List

I have an object as defined like this
public class Person {
public Person() {
this.NewsItems = new List<NewsItem>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age {get;set;}
public virtual IList<NewsItem> NewsItems { get; set; }
}
The NewsItem object has a property called DisplayOrder and it is of type int. I then have LINQ as following:
return _repo.GetAll<Person>().Where(p => p.age >60).ToList();
My objective is have a sorted List<Person> based on DisplayOrder.
I am going to use this List<Person> in controller. In this controller I do have access to Person.NewsItems. I need to have List<Person> to be sorted in order or DisplayOrder which is inside the referenced list NewsItems.
Basically I have 1:m relationship between Person and NewsItem.
You can implement a custom comparer that you can use with .OrderBy. This could be against the NewsItems property. So you can do something like:
var sortedPersons = persons
.Where(p => p.Age > 60)
.OrderBy(x => x.NewsItems, new NewsItemsComparer());
Where your NewsItemComparer could take the form of:
public class NewsItemsComparer : IComparer<IList<NewsItem>>
{
public int Compare(IList<NewsItem> x, IList<NewsItem> y)
{
if (<logic where xDisplayOrders takes precedence over yDisplay Orders>)
{
return 1;
}
if (<logic where yDisplayOrders takes precedence over xDisplay Orders>)
{
return -1;
}
else
return 0;
}
}
Here's a sample implementation in which Persons are sorted according to which NewsItems has the lowest DisplayOrder number: https://dotnetfiddle.net/MTtwHU

Convert collections of database entities to collections of view models

I am working on a .NET Core Web API
So far I used to return anonymous types in my controllers but now I want to start using the full power of swagger with auto documentation of the return types.
Which lead me to start using view models.
But I am struggling with converting between the auto-generated database model classes
and the auto-generated swagger view model classes.
It works for a single instance (see GetPerson method in the controller below) but fails when I want to return lists.
So my questions:
How do I cast/convert collections/lists of objects between view models and database models
Is the code in the controller correct? Are there easier/shorter/better ways to do the conversion? (I read about using the implicit operator)
Error message I get:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
It gives me an InvalidCastException if I cast them explicitly like
List result = (List)_dbContext.Person....
there seems to be a problem with generics in the display of stackoverflow
Assume I used the generic lists with giving a type PersonView
My code looks like:
Database models
public partial class Person
{
public Person()
{
}
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public int? MainAdressId { get; set; }
public virtual Adress MainAdress { get; set; }
}
public partial class Adress
{
public Adress()
{
Person = new HashSet();
}
public int Id { get; set; }
public string CityName { get; set; }
public int CityPostalCode { get; set; }
public string StreetName { get; set; }
public string HouseNumber { get; set; }
public string FloorNumber { get; set; }
public string DoorNumber { get; set; }
public virtual ICollection Person { get; set; }
}
View models
public class City
{
public string Name { get; set; }
public int PostalCode { get; set; }
}
public class Street
{
public string Name { get; set; }
public string HouseNumber { get; set; }
public string FloorNumber { get; set; }
public string DoorNumber { get; set; }
}
public class AdressView
{
public Street Street { get; set; }
public City City { get; set; }
}
public class PersonView
{
public string FirstName { get; set; }
public string Lastname { get; set; }
public AdressView Adress { get; set; }
}
The controller class which is working for a single instance but not for lists
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Swashbuckle.SwaggerGen.Annotations;
using PersonExample.ModelsPersonDB;
using PersonExample.ModelsViewPerson;
namespace PersonExample.Controllers
{
[Route("api/[controller]")]
public class PersonViewTestController : Controller
{
private readonly PersonDBContext _dbContext;
private readonly ILogger _logger;
public PersonViewTestController(PersonDBContext dbContext, ILogger logger)
{
_dbContext = dbContext;
_logger = logger;
_logger.LogDebug("{0} > new instance created", GetType().Name);
}
[HttpGet("{id:int}", Name = "GetPerson")]
[ProducesResponseType(typeof(PersonView), 200)]
[SwaggerOperation("GetPerson")]
public virtual IActionResult GetPerson([FromRoute]int id)
{
PersonView result = _dbContext.Person
.Include(p => p.MainAdress)
.Where(p => p.Id == id)
.Select(p => new PersonView()
{
FirstName = p.Firstname,
Lastname = p.Lastname,
Adress = (p.MainAdress == null) ? null :
new AdressView()
{
Street = new Street()
{
Name = p.MainAdress.StreetName,
HouseNumber = p.MainAdress.HouseNumber,
FloorNumber = p.MainAdress.FloorNumber,
DoorNumber = p.MainAdress.DoorNumber
},
City = new City()
{
Name = p.MainAdress.CityName,
PostalCode = p.MainAdress.CityPostalCode
}
}
}
)
.FirstOrDefault();
return new ObjectResult(result);
}
[HttpGet(Name = "GetPersonList")]
[ProducesResponseType(typeof(List), 200)]
[SwaggerOperation("GetPersonList")]
public virtual IActionResult GetPersonList()
{
List result = _dbContext.Person
.Include(p => p.MainAdress)
.Select(p => new PersonView()
{
FirstName = p.Firstname,
Lastname = p.Lastname,
Adress = (p.MainAdress == null) ? null :
new AdressView()
{
Street = new Street()
{
Name = p.MainAdress.StreetName,
HouseNumber = p.MainAdress.HouseNumber,
FloorNumber = p.MainAdress.FloorNumber,
DoorNumber = p.MainAdress.DoorNumber
},
City = new City()
{
Name = p.MainAdress.CityName,
PostalCode = p.MainAdress.CityPostalCode
}
}
}
);
return new ObjectResult(result);
}
}
}
you can use AutoMapper https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
here some examples: Simple Automapper Example
example with EF core and ASP.NET WebApi: https://github.com/chsakell/aspnet5-angular2-typescript
I missed the .ToList() at the end of the query.
The full controller know looks like:
[HttpGet(Name = "GetPersonList")]
[ProducesResponseType(typeof(List), 200)]
[SwaggerOperation("GetPersonList")]
public virtual IActionResult GetPersonList()
{
List result = _dbContext.Person
.Include(p => p.MainAdress)
.Select(p => new PersonView()
{
FirstName = p.Firstname,
Lastname = p.Lastname,
Adress = (p.MainAdress == null) ? null :
new AdressView()
{
Street = new Street()
{
Name = p.MainAdress.StreetName,
HouseNumber = p.MainAdress.HouseNumber,
FloorNumber = p.MainAdress.FloorNumber,
DoorNumber = p.MainAdress.DoorNumber
},
City = new City()
{
Name = p.MainAdress.CityName,
PostalCode = p.MainAdress.CityPostalCode
}
}
}
).ToList(); //missed that line
return new ObjectResult(result);
}

How to keep the value of the source when using InjectFrom

By injecting values ​​into my domain object, I would keep the values ​​of some properties.
Example:
Domain model
public class Person
{
public string Name { get; set; }
public Guid ID { get; set; }
public DateTime CreateAt { get; set; }
public string Notes { get; set; }
public IList<string> Tags { get; set; }
}
View Model
public class PersonViewMode
{
public string Name { get; set; }
public Guid ID { get; set; }
public DateTime CreateAt { get; set; }
public string Notes { get; set; }
public IList<string> Tags { get; set; }
public PersonViewMode() { ID = Guid.NewGuid(); } //You should use this value when it is the Target
}
Sample
var p = new Person
{
ID = Guid.NewGuid() //Should be ignored!
,
Name = "Riderman"
,
CreateAt = DateTime.Now
,
Notes = "teste de nota"
,
Tags = new[] {"Tag1", "Tag2", "Tag3"}
};
var pvm = new PersonViewMode();
pvm.InjectFrom(p); //Should use the ID value generated in the class constructor PersonViewMode
if you delete the set; from from the ViewModel's ID then it won't be set;
otherwise you could save the value of ID in a separate variable and put it back after injecting,
or you can create a custom valueinjection that would ignore "ID" or would receive a list of properties to ignore as a parameter
here's the example for a custom injection that receives a list of property names to ignore:
public class MyInj : ConventionInjection
{
private readonly string[] ignores = new string[] { };
public MyInj(params string[] ignores)
{
this.ignores = ignores;
}
protected override bool Match(ConventionInfo c)
{
if (ignores.Contains(c.SourceProp.Name)) return false;
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Type == c.TargetProp.Type;
}
}
and use it like this:
pvm.InjectFrom(new MyInj("ID"), p);
if you need to ignore more, you can do like this:
pvm.InjectFrom(new MyInj("ID","Prop2","Prop3"), p);

LinqKit PredicateBuilder with EF 4 CPT 5 table relationships?

I'm using LinqKit PredicateBuilder (http://www.albahari.com/nutshell/predicatebuilder.aspx) for a method that do searching. This is how the relationships are built (Entity Framework 4 CPT 5 POCO):
public class MusicSheet
{
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string Key { get; set; }
public virtual ICollection<Author> Authors { get; set; }
}
public class Author
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public string Bio { get; set; }
public virtual ICollection<MusicSheet> MusicSheets { get; set; }
}
I need to be able to build a predicate that checks for MusicSheet (the Title contains a specific search term) as well as the Name or Bio of the Author who might also contain that search term. Here is what I currently have:
var predicate = PredicateBuilder.False<MusicSheet>();
foreach (var term in terms)
{
string keyword = term;
predicate = predicate
.Or(s => s.Title.Contains(keyword));
// TODO Check for Author Name & Bio
}
Any suggestions? Thank you very much.
Have you tried this:
var predicate = PredicateBuilder.False<MusicSheet>();
foreach (var term in terms)
{
string keyword = term;
predicate = predicate
.Or(s => s.Title.Contains(keyword) ||
s.Authors.Any (a => a.Name.Contains(keyword) || a.Bio.Contains(keyword)));
}

AutoMapper strings to enum descriptions

Given the requirement:
Take an object graph, set all enum type properties based on the processed value of a second string property. Convention dictates that the name of the source string property will be that of the enum property with a postfix of "Raw".
By processed we mean we'll need to strip specified characters e.t.c.
I've looked at custom formatters, value resolvers and type converters, none of which seems like a solution for this?
We want to use AutoMapper as opposed to our own reflection routine for two reasons, a) it's used extensively throughout the rest of the project and b) it gives you recursive traversal ootb.
-- Example --
Given the (simple) structure below, and this:
var tmp = new SimpleClass
{
CountryRaw = "United States",
Person = new Person { GenderRaw="Male" }
};
var tmp2 = new SimpleClass();
Mapper.Map(tmp, tmp2);
we'd expect tmp2's MappedCountry enum to be Country.UnitedStates and the Person property to have a gender of Gender.Male.
public class SimpleClass1
{
public string CountryRaw {get;set;}
public Country MappedCountry {get;set;}
public Person Person {get;set;}
}
public class Person
{
public string GenderRaw {get;set;}
public Gender Gender {get;set;}
public string Surname {get;set;}
}
public enum Country
{
UnitedStates = 1,
NewZealand = 2
}
public enum Gender
{
Male,
Female,
Unknown
}
Thanks
I did it with the ValueInjecter,
here is the whole thing:
I've added one more prop to the SimpleClass just to show you how it works
public class SixFootUnderTest
{
[Test]
public void Test()
{
var o = new SimpleClass1
{
CountryRaw = "United States",
GenderRaw = "Female",
Person = new Person { GenderRaw = "Male" }
};
var oo = new SimpleClass1();
oo.InjectFrom(o)
.InjectFrom<StrRawToEnum>(o);
oo.Person.InjectFrom<StrRawToEnum>(o.Person);
oo.Country.IsEqualTo(Country.UnitedStates);
oo.Gender.IsEqualTo(Gender.Female);
oo.Person.Gender.IsEqualTo(Gender.Male);
}
public class SimpleClass1
{
public string CountryRaw { get; set; }
public Country Country { get; set; }
public string GenderRaw { get; set; }
public Gender Gender { get; set; }
public Person Person { get; set; }
}
public class Person
{
public string GenderRaw { get; set; }
public Gender Gender { get; set; }
public string Surname { get; set; }
}
public class StrRawToEnum : LoopValueInjection
{
protected override bool UseSourceProp(string sourcePropName)
{
return sourcePropName.EndsWith("Raw");
}
protected override string TargetPropName(string sourcePropName)
{
return sourcePropName.RemoveSuffix("Raw");
}
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType == typeof(string) && targetType.IsEnum;
}
protected override object SetValue(object sourcePropertyValue)
{
return Enum.Parse(TargetPropType, sourcePropertyValue.ToString().Replace(" ", ""), true);
}
}
public enum Country
{
UnitedStates = 1,
NewZealand = 2
}
public enum Gender
{
Male,
Female,
Unknown
}
}
also in case you need to do it from CountryRaw to MappedCountry
you could do it like this:
oo.InjectFrom(new StrRawToEnum().TargetPrefix("Mapped"), o);

Resources