Using FindAll in a List with instances from two derived classes - linq

I wonder if the following is possible to accomplish with the FindAll-method:
I have a base class called Animal. I have two derived classes called Fish and Dog.
Animal have:
public string Type { get; set; }
public int Height { get; set; }
public int Length { get; set; }
Dog have:
public string Color { get; set; }
Fish have: nothing extra.
I want to do something like this:
Console.Write("\nEnter keyword to search the farm: ");
string keyword = Console.ReadLine().Trim();
var mySearch = myAnimals.FindAll(p => p.Type.Contains(keyword) || p.Color.Contains(keyword));
myAnimals is a List of type Animal, containing instances of Fish and Dog.
List<Animal> myAnimals = new List<Animal>();
The problem is that Color cant be accessed from there. Only what's in the base class Animal, i.e. Type, Height and Length, are possible targets.

You could use the following code:
animals.FindAll(animal => animal.Type.Contains(keyword) || (((animal as Dog)?.Color.Contains(keyword)) ?? false));
So what does the part (((animal as Dog)?.Color?.Contains(keyword)) ?? false) actually do?
Let's take it into parts:
(animal as Dog) tries to cast animal to the Type Dog. If this fails there is no exception thrown, instead it returns null
As we cannot access the property Color of null (this would lead to a NullReferenceException) we need to use the ?. Operator aka Null-conditional Operator. It will only try to access Color if (animal as Dog) is not null.
If (animal as Dog) is null the whole expression becomes null.
The same with Color?.Contains() as Color itself could be null.
As we need a boolean expression after || and null would not be boolean we also use the ?? Operator aka Null-coalescing Operator. If the expression on the left is null use the value on the right. Otherwise use the value on the left.
This means if animal is a Dog which has a Color property and this property is not null use the return value of Contains(keyword) which is true or false.
Otherwise if animal is not a Dog or Color is null default to false.
Type.Contains(keyword)|| false means whatever Type.Contains(keyword) returns.
But this becomes unflexible if you add more types. What could be another approach?
E.g. using a custom Attibute:
class Animal
{
[SearchableProperty]
public string Type { get; set;}
public string ImNotSearchable { get; set;}
}
class Fish : Animal { }
class Dog : Animal
{
[SearchableProperty]
public string Color { get; set;}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
class SearchablePropertyAttribute : Attribute
{
}
animals.FindAll(animal => animal.GetType().GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(SearchablePropertyAttribute))).Any(p => p.GetValue(animal)?.ToString()?.Contains(keyword) ?? false))
But be aware that these kind of things can be inefficient for larger datasets.

Related

C#9 + .NET5, nullable enabled: is it possible to use the object initialiser with non nullable properties without using null!?

I'm testing c# 9.0 with NET 5.0 and there is something I don't understand. I enabled <nullable>enable</nullable>.
If I write a class
public class Potato {
public string Colour { get; set; }
public string Flavour { get; set; }
}
I get the warning CS8618: Non-nullable propery 'Colour' must contain a non-null value when existing constructor. Consider declaring the propery as nullable. I do NOT want to declare it nullable, that's why I enabled the tag...
Then, I declare the constructor:
public class Potato
{
public Potato(string colour, string flavour)
{
this.Colour = colour;
this.Flavour = flavour;
}
public string Colour { get; set; }
public string Flavour { get; set; }
}
So far so good. Now I want to create a Potato object using the object initialiser:
var sweetPotato = new Potato {
Colour = "orange",
Flavour = "tasty",
};
And now I get the complain CS7036: There is no argument given that corresponds to the required formal parameter 'colour' of 'Potato.Potato(string, object). So, it tries to use the constructor I explicitly declared but I didn't introduce the parameters. I should do:
var sweetPotato = new Potato(
colour: "orange",
flavour: "tasty");
but here I am not using the object initialiser.
Another option would be declaring the class as
public class Potato
{
public string Colour { get; set; } = null!;
public string Flavour { get; set; } = null!;
}
with the null! elements, but this allows some weird things like:
var sweetPotato = new Potato()
{
Colour = "orange",
};
which I think it is extremely weird. I declared 2 non nullable properties and thanks to the null! I can load the object with one property but not loading the second one. That breaks my nullable tag!
Then my question is: is it possible to use the object initialiser with non nullable properties if the nullable tag is enabled?
If it is not, I think that for classes with non nullable properties, without any null! involved, the default constructor should not be the empty one but the one with all arguments, and it should have to be possible to init an object with the object initialiser form, as long as all non-nullable properties are initialised.
I know this is an old Question but here's the comprehensive answer:
by adding the <nullable>true<nullable> tag you make it so that all nullables must be identified with ?
because of 1., and since you didn't add ? to their types, neither Colour nor Flavour can be NULL
because of 2., you must supply a constructor that initializes values for both Colour and Flavour - the default parameterless constructor won't work as it does nothing by itself and would return an object with NULL in both Colour and Flavour, as that's the default for their types, which you requested the compiler not to allow
The snippet below:
var sweetPotato = new Potato {
Colour = "orange",
Flavour = "tasty",
};
uses the default parameterless constructor and so cannot be used. It is the shorthand equivalent to:
var sweetPotato = new Potato(); // sweetPotato has Colour and Flavour NULL
sweetPotato.Colour = "orange"; // sweetPotato still has Flavour NULL
sweetPotato.Flavour = "tasty";
which as you can see would create the object first (with default values) and assign values later. But by your not allowing the default value of NULL, it fails
And this is why if you won't allow them to be nullable, you must add a constructor that initializes them, and you must use that constructor
This wouldn't apply if their types were of a primitive that has a different default - like an int or a double. In that situation, the default parameterless constructor would work. But by using types with a default value of NULL, (like string or any object derived type), you can either make them nullable by adding ? or you must use a constructor that initializes them to something other than NULL.
Late, Late Edit
You can use the object initializer still if you introduce a parameterless constructor that defaults those two properties:
public Potato()
{
Colour = "blue";
Flavor = "bitter";
}
Then you can do:
var bitterPotato = new Potato(); //blue and bitter potato
var sweetPotato = new Potato
{
Color = "orange",
Flavour = "tasty"
}; // orange and tasty potato
The default value for string is null. As your property declarations do not define a default value, they will default to default, which happens to be null in the case of string. The problem is that you have not declared the type as nullable.
The solution is to assign a default value, such as String.Empty:
public class Potato
{
public string Colour { get; set; } = String.Empty;
public string Flavour { get; set; } = String.Empty;
}

Expression.Property(param, field) is "trolling" me [System.ArgumentException] = {"Instance property 'B.Name' is not defined for type A"}

Once again, I am facing an issue, this time with LINQ Expression builder and this time I am even struggling to find the reason why it's not working. I have a Database-First EF project with quite a few tables. For this specific case, I have to use 2 of them - DocHead and Contragent. MyService.metadata.cs looks like this:
[MetadataTypeAttribute(typeof(DocHead.DocHeadMetadata))]
public partial class DocHead
{
// This class allows you to attach custom attributes to properties
// of the DocHead class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class DocHeadMetadata
{
// Metadata classes are not meant to be instantiated.
private DocHeadMetadata()
{
}
public string doc_Code { get; set; }
public string doc_Name { get; set; }
public string doc_ContrCode { get; set; }
//...
[Include]
public Contragent Contragent { get; set; }
}
}
[MetadataTypeAttribute(typeof(Contragent.ContragentMetadata))]
public partial class Contragent
{
// This class allows you to attach custom attributes to properties
// of the Contragent class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class ContragentMetadata
{
// Metadata classes are not meant to be instantiated.
private ContragentMetadata()
{
}
public string Code { get; set; }
public string Name { get; set; }
//...
I take some docHeads like this:
IQueryable<DocHead> docHeads = new MyEntities().DocHead;
Then I try to sort them like this:
docHeads = docHeads.OrderByDescending(x => x.Contragent.Name);
It is all working like I want it. I get those docHeads sorted by the name of the joined Contragent. My problem is that I will have to sort them by a field, given as a string parameter. I need to be able to write something like this:
string field = "Contragent.Name";
string linq = "docHeads = docHeads.OrderByDescending(x => x." + field + ")";
IQueryable<DocHead> result = TheBestLinqLibraryInTheWorld.PrepareLinqQueryable(linq);
Unfortunately, TheBestLinqLibraryInTheWorld does not exist (for now). So, I have set up a method as a workaround.
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(param, SortField); // normally returns x.sortField
var exp = Expression.Lambda(prop, param); // normally returns x => x.sortField
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); // normally returns sth similar to q.OrderBy(x => x.sortField)
return q.Provider.CreateQuery<T>(mce);
}
Normally... yes, when it comes to own properties of the class DocHead - those prefixed with doc_. The disaster strikes when I call this method like this:
docHeads = docHeads.OrderByField<DocHead>("Contragent.Name", true); // true - let it be Ascending order
To be more specific, the exception in the title is thrown on line 2 of the method OrderByField():
var prop = Expression.Property(param, SortField);
In My.edmx (the model), the tables DocHead and Contragent have got a relation already set up for me, which is the following: 0..1 to *.
Once again, I have no problem writing "static" queries at all. I have no problem creating "dynamic" ones via the method OrderByField(), but only when it comes to properties of the class DocHead. When I try to order by a prop of the joined Contragent class - the disaster strikes. Any help will be greatly appretiated, thank you!
The problem is that Expression.Property method does not support nested properties. It does exactly what it says - creates expression that represents a property denoted by propertyName parameter of the object denoted by the expression parameter.
Luckily it can easily be extended. You can use the following simple Split / Aggregate trick anytime you need to create a nested property access expression:
var prop = SortField.Split('.').Aggregate((Expression)param, Expression.Property);

Combining Linq Expressions for Dto Selector

We have a lot of Dto classes in our project and on various occasions SELECT them using Expressions from the entity framework context. This has the benefit, that EF can parse our request, and build a nice SQL statement out of it.
Unfortunatly, this has led to very big Expressions, because we have no way of combining them.
So if you have a class DtoA with 3 properties, and one of them is of class DtoB with 5 properties, and again one of those is of class DtoC with 10 properties, you would have to write one big selector.
public static Expression<Func<ClassA, DtoA>> ToDto =
from => new DtoA
{
Id = from.Id,
Name = from.Name,
Size = from.Size,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
Also, they cannot be reused. When you have DtoD, which also has a propertiy of class DtoB, you would have to paste in the desired code of DtoB and DtoC again.
public static Expression<Func<ClassD, DtoD>> ToDto =
from => new DtoD
{
Id = from.Id,
Length = from.Length,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
So this will escalate pretty fast. Please note that the mentioned code is just an example, but you get the idea.
I would like to define an expression for each class and then combine them as required, as well as EF still be able to parse it and generate the SQL statement so to not lose the performance improvement.
How can i achieve this?
Have you thought about using Automapper ? You can define your Dtos and create a mapping between the original entity and the Dto and/or vice versa, and using the projection, you don't need any select statements as Automapper will do it for you automatically and it will project only the dto's properties into SQL query.
for example, if you have a Person table with the following structure:
public class Person
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
public string Initial { get; set; }
public string PreferredName { get; set; }
public string FormerTitle { get; set; }
public string FormerFamilyName { get; set; }
public string FormerGivenName { get; set; }
}
and your dto was like this :
public class PersonDto
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
}
You can create a mapping between Person and PersonDto like this
Mapper.CreateMap<Person, PersonDto>()
and when you query the database using Entity Framework (for example), you can use something like this to get PersonDto columns only:
ctx.People.Where(p=> p.FamilyName.Contains("John"))
.Project()
.To<PersonDto>()
.ToList();
which will return a list of PersonDtos that has a family name contains "John", and if you run a sql profiler for example you will see that only the PersonDto columns were selected.
Automapper also supports hierachy, if your Person for example has an Address linked to it that you want to return AddressDto for it.
I think it worth to have a look and check it, it cleans a lot of the mess that manual mapping requires.
I thought about it a little, and I didn't come up with any "awesome" solution.
Essentially you have two general choices here,
Use placeholder and rewrite expression tree entirely.
Something like this,
public static Expression<Func<ClassA, DtoA>> DtoExpression{
get{
Expression<Func<ClassA, DtoA>> dtoExpression = classA => new DtoA(){
BDto = Magic.Swap(ClassB.DtoExpression),
};
// todo; here you have access to dtoExpression,
// you need to use expression transformers
// in order to find & replace the Magic.Swap(..) call with the
// actual Expression code(NewExpression),
// Rewriting the expression tree is no easy task,
// but EF will be able to understand it this way.
// the code will be quite tricky, but can be solved
// within ~50-100 lines of code, I expect.
// For that, see ExpressionVisitor.
// As ExpressionVisitor detects the usage of Magic.Swap,
// it has to check the actual expression(ClassB.DtoExpression),
// and rebuild it as MemberInitExpression & NewExpression,
// and the bindings have to be mapped to correct places.
return Magic.Rebuild(dtoExpression);
}
The other way is to start using only Expression class(ditching the LINQ). This way you can write the queries from zero, and reusability will be nice, however, things get harder & you lose type safety. Microsoft has nice reference about dynamic expressions. If you structure everything that way, you can reuse a lot of the functionality. Eg, you define NewExpression and then you can later reuse it, if needed.
The third way is to basically use lambda syntax: .Where, .Select etc.. This gives you definitely better "reusability" rate. It doesn't solve your problem 100%, but it can help you to compose queries a bit better. For example: from.MyCList.Select(dtoCSelector)

Can I query for and retrieve members of a collection property in RavenDB using full text search?

I'm using the term "child documents" loosely relating to objects stored in a collection property. Given those two classes:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Bar> Bars { get; set; }
}
public class Bar
{
public string Name { get; set; }
public string Description { get; set; }
public string SomeString { get; set; }
public int SomeInt { get; set; }
}
I would like to query for Bars which have the term "roses" in either their Name or Description properties. Please note that Bars are stored within Foo.
This is a two fold question:
Can a query be used over a collection of type Foo to return Bars ("child document")? I want to get a collection of Bars which have "roses" in their Name, other Bars should be skipped. I don't want Foos (aggregate root) returned.
If so, how to do it with full text search?
Regarding #1, I know something like that is impossible in MongoDB, where I would either have to store Foo and Bar separately or query for Foos where any of their Bars has "roses" in the Name and then do something about it on the client. But RavenDB has Live Projections / TransformResults, so I thought it would be possible perhaps.
You can store a copy of each Bar in field storage with the index, so yes - it can be done. But you should make sure you understand the impact of doing this.
Normally when you query from raven, the index is only used to determine which documents get sent back. The documents themselves don't come from the index, but from the document store. The document store has ACID guarantees - meaning you will always get the most current copy of the document regardless of the state of the index. If you project from index entries or index field storage, then the values you get back are as stale as the index itself.
Say you are constantly updating the Bars, and you search before the index has caught up to your last update. You could get back a Bar that has old data. Therefore, you need to weigh in the potential staleness of data into your results, possibly using one of the WaitForNonStaleResultsAsOf... customizations - which will slow the speed your search results return if you have lots of writes going on.
public class Foos_BarsByName
: AbstractIndexCreationTask<Foo, Foos_BarsByName.Result>
{
public class Result
{
public string Name { get; set; }
public Bar Bar { get; set; }
}
public Foos_BarsByName()
{
Map = foos => from foo in foos
from bar in foo.Bars
select new
{
bar.Name,
Bar = bar
};
Index(x => x.Name, FieldIndexing.Analyzed);
Index(x => x.Bar, FieldIndexing.No);
Store(x => x.Bar, FieldStorage.Yes);
}
}
var results = session.Query<Foos_BarsByName.Result, Foos_BarsByName>()
.Customize(x => x.WaitForNonStaleResultsAsOfNow())
.Search(x => x.Name, "roses")
.Select(x => x.Bar);
Another way to handle it might be to let all Foos come back, and then pull out the Bars you are interested in on the client side. At least then, everything comes from the document store:
public class Foos_BarsByName
: AbstractIndexCreationTask<Foo, Foos_BarsByName.Result>
{
public class Result
{
public string Name { get; set; }
}
public Foos_BarsByName()
{
Map = foos => from foo in foos
from bar in foo.Bars
select new
{
bar.Name
};
Index(x => x.Name, FieldIndexing.Analyzed);
}
}
var results = session.Query<Foos_BarsByName.Result, Foos_BarsByName>()
.Search(x => x.Name, "roses")
.As<Foo>()
.AsEnumerable()
.SelectMany(x => x.Bars)
.Where(x => x.Name.IndexOf("roses",
StringComparison.CurrentCultureIgnoreCase)
!= -1)
.AsEnumerable() will force the linq-to-raven query to execute, making everything that follows happen in linq-to-objects on the client side.
Of course, if you are doing a more advanced search than can be expressed with c# string functions, then you won't be able to take this second approach.

Using abstract view model in MVC 3

I have an input form containing several input fields. Each input field has a ElementModel which has properties basically for the label and the value. The input fields to display are specified in a XML document, so I have kind of a dynamic view with only a list of elements.
The problem is, that each element should be either displayed as a decimal or as percentage value. And of course, if it's a percentage value, the user shoud be able to input something like "45%" and the value in the model should then be 0.45.
My first thought when I found this article was to use an abstract view model class with an abstract property for the value and to define a PercentageElementModel deriving from my base ElementModelclass that makes use of a custom model binder. Unfortunately, if I use that abstract base class in my view, the data annotations made in the PercentageElementModelare ignored.
Do you have any idea of how I can solve this? I don't want to use strings in my view model and do the parsing by myself as this will break the MVC pattern. Are there some other ways to achieve my goal?
Here are some code snippets:
public abstract class ElementModel
{
public string ElementName { get; set; }
public ElementType ElementType { get; set; }
public abstract double? ElementValue { get; set; }
}
public class PercentageElementModel : ElementModel
{
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:P2}")]
public override double? ElementValue { get; set; }
}
I came up with another solution as my problem is more a matter of display formatting than of validation: I wrote a custom ModelBinder which checks the input string of the text box. If it ends with a trailing '%' sign, I will devide the value by 100. Here's the code.
public class DoubleModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var modelState = new ModelState { Value = valueResult };
double? actualValue = null;
try
{
// cut trailing '%'
if (valueResult.AttemptedValue.EndsWith("%"))
{
var strValue = valueResult.AttemptedValue.Substring(0, valueResult.AttemptedValue.Length - 1);
actualValue = double.Parse(strValue, valueResult.Culture) / 100;
}
else
{
actualValue = Convert.ToDouble(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
The rest happens in the view itself. To display the correct format, I use a simple ToString() method with a percentage format string assigned if needed. And if the user enters a numeric value without '%', a jQuery's blur() event will append the '%' sign to the user's input. Perfectly works for me although this is not the best answer for my own question.

Resources