I'm trying to make a SQL Query with LinqToEntities which will be as efficient as possible so I would like to avoid iterating after every record I get in results to get a new corrected collection. I'm trying to create a ready collection to send as a result in API just with this one query.
I'm trying to get a discriminator value though with inherited models.
My models are somewhat like these:
public abstract class DbEntity
{
[NotMapped]
public string DiscriminatorValue
{
get
{
return ObjectContext.GetObjectType(GetType()).Name;
}
}
}
public class Foo : DbEntity {
}
public class Bar: DbEntity {
}
This model will create a table in database which will have a column called Discriminator. To get the discriminator value I used my custom DiscriminatorValue property but it won't work in queries like this:
var events = context.MyTable.Select(t => new {
Discriminator = t.DiscriminatorValue
}).ToList();
This below one will obviously work, but it will be much slower imho:
var events = context.MyTable.ToList().Select(t => new {
Discriminator = t.DiscriminatorValue
}).ToList();
Is it possible to get Discriminator value without having to write my custom SQL query myself (like real sql which I know is possible too).
Related
Following the guide lines from Domain Driven Design, I try to avoid having one aggregate referencing a different aggregate. Instead, an aggregate should reference another aggregate using the other aggregate's id, for example:
public class Addiction
{
private Addiction(){} //Needed for EF to populate non-simple types
//DrugType belongs to the aggregate,
//inflate when retrieving the Addiction from the db
//EF does not need DrugId for navigation
Drug Drug{get;set;}
//The supplier is not part of the aggregate,
//aggregates only reference eachother using Ids
int SupplierId{get;set;}
//Other properties
}
public class AddictionConfiguration : IEntityTypeConfiguration<Addiction>
{
builder.HasOne(addiction => addiction.Drug); //Works
builder.HasOne("SupplierId") //Does not work.
}
In this (not very realistic) example, Drug is part of the Addiction's aggregate. When loading this entity from the database using EF, it will also inflate the Drug property without me having to specify the DrugId as the foreign key.
However, now I need to get a list of all Addictions and their suppliers by mapping the relevant properties to a Dto. I try to achieve this by using AutoMapper's ProjectTo functionality, e.g.
_mapper.ProjectTo<AddictionDto>(_dbContext.Addictions.Where(x => x.Id > 1));
where AddictionDto is defined as
public class AddictionDto
{
DrugDto Drug {get;set;}
SupplierDto Supplier {get;set;}
//other properties
}
And
public class SupplierDto
{
public int Id {get;set;}
public string Name {get;set;}
}
Automapper correctly loads the Addiction and also the Drug, but I cannot get it to load the Supplier. I've tried all the options of the IEntityTypeConfiguration to tell EF that there is a navigation property, but I cannot get it to work. Does anyone know if is even possible to do what I described above?
I want to hydrate a collection of entities by passing in a comma delimited list of Ids using EF5 Code First.
I would previously have created a table function in t-sql, passed in the comma delimited list of Ids, I'd then join this table to the target table and return my collection of records.
What is the most performant way of using EF5 Code First to achieve the same?
Update: I want to avoid having the full set of entities in memory first.
Update2: I'd ideally like the order of the entities to match the delimited list.
I'd say to start out by converting the comma delimited list into a List<int> that contains all of the IDs that you would be going for. At that point, using your particular DbContext class you would do the following:
var entities = db.MyEntities.Where(e => myListOfIds.Contains(e.ID)).ToList();
Note: I only put the ToList at the end there because you were talking about hydrating the collection. Otherwise, with IEnumerable, there will be deferred execution of the query, and so it will not populate right away.
You could do it like this, where you restrict the set of Entity objects by checking if their IDs belong to your list of IDs:
// Dummy list of POCO 'entity' objects (i.e. the Code first objects) just for the sake of this snippet
var entities = new List<Entity>();
entities.Add(new Entity() { ID = 1, Title = "Ent1" });
entities.Add(new Entity() { ID = 2, Title = "Ent2" });
entities.Add(new Entity() { ID = 3, Title = "Ent3" });
// List of ids to match
var ids = new List<int>();
ids.Add(1);
ids.Add(2);
// LINQ:
var selected = (from e in entities where ids.Contains(e.ID) select e).ToList();
Just for completeness, this is the dummy class used above:
// POCO (Code first) object
private class Entity
{
public int ID { get; set; }
public string Title { get; set; }
}
Suppose I have a class Parent, with two subclasses, AChild and BChild. I have these mapped to a single table using Entity Framework 5.0.0 on .NET 4.5, using TPH.
public abstract class Parent {
public string Type { get; set; } // Column with this name in DB is discriminator.
public string Status { get; set; }
}
public class AChild : Parent {
// Other stuff.
}
public class BChild : Parent {
// Other stuff.
}
The code to configure the mapping:
class ParentConfiguration : EntityTypeConfiguration<Parent> {
Map((EntityMappingConfiguration<AChild> mapping) => mapping
.Requires("Type")
.HasValue("A"));
Map((EntityMappingConfiguration<BChild> mapping) => mapping
.Requires("Type")
.HasValue("B"));
}
I have a need to run a query that returns both AChild and BChild objects. However, it needs to filter ONLY the AChild rows by a second column, which in this example I will call Status.
Ideally I would want to do the following:
public IList<Parent> RunQuery() {
IQueryable<Parent> query =
this.context.Set<Parent>()
.Where((Parent parent) => !parent.Type.Equals("A") || parent.Status.Equals("Foo"))
.OrderBy((Parent parent) => parent.Number);
return query.ToList();
}
This doesn't work. It insisted on looking for a "Type1" column instead of just letting both the discriminator and a "Type" property be mapped to the same "Type" column.
I know of the "OfType" extension method that can be used to completely filter down to one type, but that's too broad a brush in this case.
I could possibly run multiple queries and combine the results, but the actual system I'm building is doing paging, so if I need to pull back 10 rows, it gets messy (and inefficient) to query since I'll either end up pulling back too many rows, or not pull back enough and have to run extra queries.
Does anyone have any other thoughts?
There are few problems. First of all you cannot have discriminator mapped as a property. That is the reason why it is looking for Type1 column - your Type property results in second column because the first one is already mapped to .NET types of your classes. The only way to filter derived types is through OfType.
The query you want to build will be probably quite complex because you need to query for all Bs and concatenate them with result of query for filtered As. It will most probably not allow you to concatenate instances of Bs with As so you will have to convert them back to parent type.
Can anyone explain the behavior I am seeing in the minimal code example below? It seems that for a given field or property, the same two instances of the Entry class are being reused in each iteration of the LINQ to SQL query, even though I use the new operator. The same problem does not show up for LINQ to objects queries. I created a C# console application project using .NET Framework 4 and connecting to a SQL Server 2005 Enterprise database.
public class Set
{
public Entry Field;
public Entry Property { get; set; }
}
public class Entry
{
public int ID;
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
var dc = new DataClasses1DataContext(); // just create a simple dbml with some table from some database
var resultQuery = (
from x in dc.SomeTable
select new Set()
{
Field = new Entry(),
Property = new Entry()
}
);
var sets = resultQuery.ToArray();
Test(sets);
var source = Enumerable.Range(0, 10);
var sourceQuery = (
from x in source
select new Set()
{
Field = new Entry(),
Property = new Entry()
}
);
var sets2 = sourceQuery.ToArray();
Test(sets2);
}
static void Test(Set[] sets)
{
var f = sets[0].Field;
Console.WriteLine(sets.All(x => object.Equals(x.Field, f)));
var p = sets[0].Property;
Console.WriteLine(sets.All(x => object.Equals(x.Property, p)));
Console.Writeline(sets.Length);
Console.WriteLine(object.Equals(f, p));
Console.WriteLine();
}
}
The output of Test() for the LINQ to SQL query is
True
True
1362
False
which indicates that for all of the Set objects produced, all the Field members point to the same single Entry instance and all the Property members point to the same single Entry instance. I.e., the same instance is reused for a respective member in every iteration of the query.
The output of Test() for the LINQ to objects query is
False
False
10
False
which indicates that a new distinct instance is created in each iteration of the query.
Is the LINQ to SQL behavior expected or a bug? Does anyone know if this happens with the Entity Framework?
I don't know if this is a bug or if and why this is expected in LINQ to SQL. I can only answer your last question if that also happens with Entity Framework.
The answer is: No.
With EF you have to use object initializer syntax though when you instantiate the Entry objects. Using the default constructor leads to an exception:
var resultQuery = (
from x in dc.SomeTable
select new Set
{
Field = new Entry { Name = "X" },
Property = new Entry { Name = "X" }
}
);
It doesn't matter how you initialize. Using the code above (and with 4 rows in a small test table) I get this output with your test program:
False
False
4
False
False
False
10
False
It looks that there is a big difference between LINQ to SQL and Entity Framework regarding object materialization during projections.
(I've tested with EF 4.1/DbContext.)
Edit
If I take the modified query in my code snippet above also for your LINQ to SQL query and watch the generated SQL I get the following:
SELECT NULL AS [EMPTY]
FROM [dbo].[SomeTable] AS [t0]
Whereas the same with LINQ to Entites creates this query:
SELECT
1 AS [C1],
N'X' AS [C2],
N'X' AS [C3]
FROM [dbo].[SomeTable] AS [Extent1]
My interpretation is that LINQ to SQL parses the projection code and queries only columns for property values which depend on the "row variable" x. All other properties are filled on the client when the objects get materialized. If an object does not depend on a column value at all, LINQ to SQL creates a single constant object and reuses it in the whole result collection.
In constrast to this Entity Framework also sends constant values (independent of x) to the database server. The values get send back to the client and EF treats those values as if they were column values and updates the properties of the objects in the projection.
This leads also to the big difference that something like this...
Random random = new Random();
var resultQuery = (
from x in dc.SomeTable
select new Set
{
Field = new Entry { ID = random.Next() },
Property = new Entry { Name = "X" }
}
);
...works in LINQ to SQL because apparently the random function value (which is independent of x) is evaluated on the client and then assigned to the property. But EF wants to translate the right side of the property assignment into SQL and send it as SQL fragment to the database server - which fails and leads to the infamous "...cannot translate into store expression..." exception.
Edit 2
BTW: The last code snippet above still creates only a single Field instance in the whole collection: random.Next() is only evaluated once (and also the constructor of Entry is only called once for the Field object). This now is indeed confusing because writing such code one would expect that you want to have a random value for each row returned from the database. It's not the case.
I'm trying to sort a list of entities using a GridView in ASP.NET, but I can't seem to get it working following examples. I have a property called Name on my entity, and I'm trying to sort by a specified column if given, or the Name column if the sortExpression is empty.
public static List<Product> GetProducts(int startRowIndex, int maximumRows, string sortExpression) {
using(var context = new ShopEntities()) {
var products = context.Products;
products.OrderBy("it."+(string.IsNullOrEmpty(sortExpression) ? "Name" : sortExpression))
.Skip(startRowIndex)
.Take(maximumRows);
return products.ToList();
}
}
I can't get it to sort though. The only other option seems to be doing a switch on the property name for every property in the entity and using a lambda.
OrderBy doesn't mutate the expression. It returns a new expression, which your code ignores. Change your code to:
products = products.OrderBy("it."+ //...