EF Core: filter on converted column [duplicate] - entity-framework-core

Suppose I want to enhance a model like
public class Person
{
public string Name { get; set; }
public string Address { get; set; }
}
so that I use a complex object for Address:
public class Person
{
public string Name { get; set; }
public Address Address { get; set; }
}
EF Core is quite nice in allowing this with the HasConversion feature:
modelBuilder.Entity<Person>
.Property(p => p.Address)
.HasConversion(addr => addr.ToString(), str => Address.FromString(str));
I even tested and this works in queries with == operator: The following will successfully convert to SQL
var whiteHouse = Address.Parse("1600 Pennsylvania Avenue NW");
var matches = from person in people
where person.Address == whiteHouse
select person;
However, suppose I want to string.Contains on the string version of Address, something like
var search = "1600";
var matches = from person in people
where person.Address.ToString().Contains(search)
select person;
This will fail to convert. Is there any feature of EF Core to map the ToString() method or otherwise map a complex object that converts to a string / VARCHAR so that I can write a query like this?

The problem with EF Core value converters and LINQ queries is that the LINQ query is against the CLR entity property, hence the CLR type rather than the provider type. This is partially mentioned under currently Limitations of the value conversion system section of the EF Core documentation:
Use of value conversions may impact the ability of EF Core to translate expressions to SQL. A warning will be logged for such cases. Removal of these limitations is being considered for a future release.
So having query expression against the CLR type combined with the inability to translate custom methods is causing your issue. Technically it's possible to add custom method/property translation, but it's quite complicated because requires a lot of non user friendly infrastructure plumbing code, which makes practically unusable in real life application development.
In this particular case though, you know that the provider type is string, and the database table values are generated by ToString method. So you just need to let the query use the provider type. And you can do that by using cast operator.
Normally C# compiler won't allow you to cast known object type to another known object type if there is no conversion between them. But you can trick it by using the "double cast" technique by first casting to object and then to the desired type. Fortunately EF Core translator supports such casts and properly (sort of) translates them to SQL. By sort of I mean it emits unnecessary (redundant) CAST inside the query, but at least it translates and executes server side.
With that being said, the solution for your example is
where ((string)(object)person.Address).Contains(search)

As a default behavior, EF Core use Server-side evaluation, EF Core try to translate your expression to standard DB provider T-SQL code (based on selected DB provider)
you expression can't translate to T-SQL code and the DB provider can't handle it (Because the logic you write in the overridden version of ToString() is in your C# code and is unknown to the database provider)
You should force EF Core to use client-side evaluation by fetching all data to memory and then query on the loaded entities, something like this:
var search = "1600";
var matches = from person in people.ToList()
where person.Address.ToString().Contains(search)
select person;
Note that fetching all data to memory in huge databases have performance impacts and use client-side evaluation carefully.

Related

Attempting to use EF/Linq to Entities for dynamic querying and CRUD operations

(as advised re-posting this question here... originally posted in msdn forum)
I am striving to write a "generic" routine for some simple CRUD operations using EF/Linq to Entities. I'm working in ASP.NET (C# or VB).
I have looked at:
Getting a reference to a dynamically selected table with "GetObjectByKey" (But I don't want anything from cache. I want data from database. Seems like not what this function is intended for).
CRM Dynamic Entities (here you can pass a tablename string to query) looked like the approach I am looking for but I don't get the idea that this CRM effort is necessarily staying current (?) and/or has much assurance for the future??
I looked at various ways of drilling thru Namespaces/Objects to get to where I could pass a TableName parameter into the oft used query syntax var query = (from c in context.C_Contacts select c); (for example) where somehow I could swap out the "C_Contacts" TEntity depending on which table I want to work with. But not finding a way to do this ??
Slightly over-simplyfing, I just want to be able to pass a tablename parameter and in some cases some associated fieldnames and values (perhaps in a generic object?) to my routine and then let that routine dynamically plug into LINQ to Entity data context/model and do some standard "select all" operations for parameter table or do a delete to parameter table based on a generic record id. I'm trying to avoid calling the various different automatically generated L2E methods based on tablename etc...instead just trying to drill into the data context and ultimately the L2E query syntax for dynamically passed table/field names.
Has anyone found any successful/efficient approaches for doing this? Any ideas, links, examples?
The DbContext object has a generic Set() method. This will give you
from c in context.Set<Contact>() select c
Here's method when starting from a string:
public void Test()
{
dynamic entity = null;
Type type = Type.GetType("Contract");
entity = Activator.CreateInstance(type);
ProcessType(entity);
}
public void ProcessType<TEntity>(TEntity instance)
where TEntity : class
{
var result =
from item in this.Set<TEntity>()
select item;
//do stuff with the result
//passing back to the caller can get more complicated
//but passing it on will be fine ...
}

Filtering an EF object graph from query based on a common interface

If I have a common interface implemented by all entities, for example:
public interface IEntity {
int Id { get; set; }
DateTime ValidFrom { get; set; }
DateTime? ValidTo { get; set; }
}
Is it at all possible to filter out objects returned as part of a query if ValidTo is null - only return still valid objects.
Where I get this idea from is that Julia Lerman and Rowan Miller use something similar in PEF:DbContext to set state on disconnected entities, however they do it via the state manager which allows them to iterate through all entities which implement a particular interface - I want to do something similar, but on a query which involves sub-entities.
I am using SQL Server 2012, EF5 and .Net 4.5.
The example you refer to relies on the ChangeTracker.Entries<T>() method that accepts an interface as generic type parameter. Of course this all happens in memory, there is no translation to SQL involved.
When it comes to translating linq to SQL, entity framework does not (yet) support interfaces, basically because DbSet<T> must be based on a concrete class.
Your only chance is to create a base type in stead of an interface and have your entities derive from that.

Combine columns in entity framework into one column with the edmx designer

I'm using EntityFramework 5 EDMX designer and would like to combine the first & last name of a person into a single field value (name, for instance) on the entity.
I thought in previous versions there was a way to do this, but I don't see anything available to do what I need to do.
Is this still possible?
Unless I'm not understanding your question, I believe I've done that with a partial class that resembles something like the following:
public partial class person
{
public string name {
get
{
return firstname + " " + lastname;
}
set{ }
}
}
No it is not possible. You can create model defined function and use it in queries but it will still not be part of your entity. If your entity is read only you can create database view with combined column and map it instead of the table - it shows also main reason why combining columns into single property is not such easy task. Automatic concatenating during reading is easy but automatic decomposing to save correct value into correct column is hard and error prone.
If you need combined property for anything else than querying you can simply create another partial part of your entity class and add your own computed property. If you need the combined property for querying use the model defined function.
The way I do this is through a Computed Column as explained here:
How to make a computed column nullable in SQL Server
If you use a computed column you'll be able to use such a column in your LINQ queries. For example:
var users = Database.Users.Where(u => u.FullName.ToLower().Contains("string"));
You won't get errors like "not supported in LINQ to Entities" because this property is really a part of your model object. All the heavy lifting occurs on the database side.
Of course you could place a FullName property in a partial class and use it.
public string FullName
{
get { return string.Format("{0} {1}", FirstName, LastName); }
}
In this case, you'll have to call .ToList() first ( Database.Users.ToList(); ) to be able to use this property in LINQ queries. .ToList() will hydrate/bring all your Users to memory. This is not desirable!
You can also try the FullName property implementation that's described here: Calculated Columns in Entity Framework Code First Migrations

How to tell entity framework how to save instances of a custom type (that can be stored as a scalar)

One of my entity classes would be possible to store in a sql server
database as a BIGINT. My question is: How do I get a Entity Framework
context to know how to store and retrieve instances of my entity class?
More detail. I'm using Noda Time, which can represent a (much) wider range of
dates than can SQL or .NET datetime (AND it's a dessert topping). My Entity Class, Happening, is a wrapper around NodaTime's
Instant class. I can set a Happening from a long, and get a long from
a happening with methods like .SetFromLong(long instant) and .ToLong().
Currently I have my model working, saving classes that contain
properties of the dot net DateTime type. If instead I want to use properties
of my custom type "Happening", how do I tell Entity Framework how to save those?
If I'm reading this article about Modeling and Mapping am I on the
right track or missing something simpler?
http://msdn.microsoft.com/en-us/library/bb896343.aspx
I'm using entity framework 4.
What i recommend doing is adding 2 properties on your entity a NodaTime and a long, and exclude your NodaTime property using [NotMapped] in your EF model, then in your getter/setter update the long.
ie
public class MyEntity{
public long TimeAsLong{get;set;}
[NotMapped]
public Happening {
get{
return new Happening().SetFromLong(TimeAsLong);
}
set {
TimeAsLong = value.ToLong();
}
}
}
The effect of this will be that the long is stored in the db but you can access it on the class via NodaTime

How do I get Entity Framework to use interfaces on the generated classes?

I have a project where the client is using Entity Framework, and I'm trying to abstract away the generated classes from the rest of the application.
One generated class is Category and it has say Type as a property.
I've created an interface that I want Category to implement, like this:
public interface ICategory
{
string Type { get; set;}
}
I have done this in LINQ to SQL before and it works fine. I create a partial class in a separate file and have it implement the interface:
public partial class Category: ICategory
//implement interface
However, with EF whenever I try to build a query with EF it says it doesn't support OfType<>().
Example:
var query = from c in DataContext.Category
where Type == "some type"
select c;
var resultsList = query.OfType<ICategory>(); //error here (not supported)
What am I doing wrong here?
Other things to note: I'm developing this in a silverlight application and the data context is actually being pulled from a service, so there's a client server relationship going on here as well.
As a general rule, LINQ to Entities can only understand things which are part of your entity model (EDMX). So while you are free to extend your entity types be a partial classes, you cannot use properties, methods, and interface references you add there in LINQ to Entities queries, except for certain, very specific features.
However, in this case the following query should give you the result you want:
var resultsList = query.Select<ICategory>(c => c);