C# Table Per Hierarchy Projection Union - c#

I'm using table per hierarchy to project to model classes. When I use of type in Linq to Entities, all of my results are always the last type which was projected. What I mean is if I have these for domain models:
public interface IPerson { string Name {get;set;} }
public abstract class Person : IPerson { public string Name { get;set; } }
public class Employee : Person{ }
public class Manager : Person { }
And I have a Person table with a Name and type column, and use table per hierarchy to yield me specific Entities (which I'll refer to as EmployeeEntity and ManagerEntity).
When I use this code:
IQueryable<IPerson> people = entities.OfType<ManagerEntity>().Select(x => new Manager
{ Name = x.name }).Concat(entities.OfType<EmployeeEntity>().Select(y => new Employee
{ Name = y.name }));
var projectedPeople = people.ToList();
All of the types of objects in projectedPeople are Employee, even the the objects that should be of type Manager. If I remove the Concat call and run them individually, I get the correct type back (Manager or Employee).
What am I doing wrong here? I wrote some test code that removed Linq from the equation by making dummy lists of objects and calling AsQueryable on them, followed by a projection, and that worked as it should.

Related

Creating common class objects based on class name and use it

I have the same code logic used across different class objects.
For ex:
var matchingTypes = from matchType in order.Contacts
select matchType;
var matchingTypes = from matchType in customer.Contacts
select matchType;
Instead of writing duplicate lines of code, i would like to pass order, customer class names and get Contacts through it, so that the above code will look like (we are using LINQ in our code)
var matchingTypes = from matchType in objectElement.Contacts
select matchType;
The stuff i tried is passed an object parameter
GetData(object objectElement) // method consuming an object parameter.
var objectOrder= objectElement as Orders;
var objectCustomer= objectElement as Customers;
if(objectOrder!=null)
{
objectElement = (Orders) objectOrder; //type
}
if(objectCustomer !=null)
{
objectElement = (Customers) objectCustomer;
}
By doing so, i am repeating my code, which i would like to avoid, any suggestions/ideas? thanks.
I would like to use objectElement and assign only once, so that i can call like this as shown below
var matchingTypes = from matchType in objectElement.Contacts
select matchType;
An interface would be the preferred way to do this, but you could also use dynamic to duck type a method:
public IEnumerable<Contact> GetContacts(dynamic yourObject)
{
return yourObject.Contacts;
}
Note this will not give you a compile error if you call it with something that doesn't have a property called Contacts of type IEnumerable<Contact> but will instead give you a runtime error.
Or you don't even actually need a method, you could just do this:
var matchedTypes = ((dynamic)yourObject).Contacts as IEnumerable<Contact>;
Interfaces would be a safer bet, but are a little tricky with generate entity framework classes. But you can do them becuase they are generated as partial classes. So you can do something like this:
public interface IHaveContacts
{
public IEnumerable<Contact> Contacts { get; }
}
and then:
public partial class Orders : IHaveContacts
{
// should need to do anything since the auto-genrated Contacts property
// will satisfy the interface
}
public partial class Customers : IHaveContacts
{
// ditto
}
And now you can do:
var matchedTypes = ((IHaveContacts)yourObject).Contacts;
Or, if you really, really must (which you don't):
var matchedTypes = from matchType in ((IHaveContacts)yourObject).Contacts
select matchType;
Create an interface IContactsContainer:
public interface IContactsContainer
{
public YourContactType Contacts{get;set;}
}
Then your customer and order classes can implement it:
public class Customers : IContactsContainer
{
public YourContactType Contacts {get;set;}
....
}
public class Orders: IContactsContainer
{
public YourContactType Contacts {get;set;}
....
}
After that in your method you can use:
IContactsContainer objectElement = yourOrderObject;

Passing generic class name in dbcontext

My entities context file code:
public partial class MyDbContext : DbContext
{
//dbset 1
public DbSet<Customer> Customers { get; set; }
//dbset 2
public DbSet<Order> Orders { get; set; }
}
Class Shipments has a method GetCustomerName
public class Shipments
{
public string GetName(object caller, System.Data.Entity.DbSet objectContext)
{
//This one passes.
IQueryable<Customer> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
//This one fails
IQueryable<caller.GetType()> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
}
}
Question: I would like to remove the hard coding of class Customer there, instead call this method by passing the class name as parameter?
How can I implement this? In this case caller.GetType().Name didn't work for me.
GetName(Order, mydbContext);
GetName(Customer, mydbContext);
Both should work with the same code (trying it to make generic, I am not sure how to cast it generic). Any help will be great. Thanks.
Like so many people, including myself early on, you are not seeing the difference between a data type and an instance of the Type class. A data type is something that is known at compile time. When you call GetType, what you get back is an object whose data type is Type. It contains information about a data type but it is not a data type itself.
What you need to do is make your GetName method generic as well:
public string GetName<T>(T caller, System.Data.Entity.DbSet objectContext)
{
IQueryable<T> myEntity = from p in objectContext
select p where p.Id=1; //get customer name
}
To be able to do that though, the compiler must know that type T actually has an Id property. That means that T has to be constrained to be a specific base class or interface that declares an Id property. In the case of auto-generated EF classes, an interface is your only option.
There are still other issues with your code, but that covers what you are actually asking about.
I don't see the point of passing an object argument just for the sake of getting its type. You can use generics for that:
public string GetName<T>(DbSet objectContext) where T : BaseEntity {
IQueryable<T> myCustomer = from p in objectContext
select p where p.Id = 1;
//Get the name
//Return the name
}
Please note that I added a constraint for T to extend from a BaseEntity.
All your entities should then extend from this abstract class (which should contain the property Id and Name for getting the name).
You would then be able to retrieve the name of, not only Customer entities, but even Order entities:
string customerName = GetName<Customer>(context);
string orderName = GetName<Order>(context);
If only your Customer entity has the property Name, then don't use generics at all, you would be better defining the type explicitly:
public string GetName(context) {
IQueryable<Customer> customer = from p in context
select p where p.Id = 1;
//Get the name
//Return the name
}

Casting derived class to base class using LINQ to Entities

I have two database tables, one to hold completed items and another to hold incomplete items. Both tables are identical in structure. There are some cases where I would like to quest just one of these tables, but other cases where I would want to query the concatenation of both tables.
Classes
public abstract class SomeBase
{
public int Id {set;get;}
public string Item1 {set;get;}
public string Item2 {set;get;}
}
public class A : SomeBase
{
}
public class B : SomeBase
{
}
Mapping (Fluent API)
public class SomeDatabase : DbContext
{
public DbSet<A> As {set;get;}
public DbSet<B> Bs {set;get;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<A>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("ATable", "SomeSchema");
}
modelBuilder.Entity<B>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("BTable", "SomeSchema");
}
}
}
Using this setup, I can query both tables just fine individually like so
var db = new SomeDatabase();
var a = db.As;
var b = db.Bs;
When trying to combine the two, I can't quite figure out the syntax. One solution in the answer below involves casting it using .AsEnumerable but that isn't quite what I'm looking for since it evaluates immediately.
db.As.AsEnumerable().Concat<SomeBase>(db.Bs);
db.As.Cast<SomeBase>().Concat(db.Bs.Cast<SomeBase>());
How can I concatenate two derived classes that are identical on the database site?
EDIT:
At the lowest level, I am getting these errors
db.As.Cast<SomeBase>();
Unable to cast the type 'Test.Models.A' to type 'Test.Models.SomeBase'. LINQ to Entities only supports casting Entity Data Model primitive types.
db.As.OfType<SomeBase>();
'Test.Models.SomeBase' is not a valid metadata type for type filtering operations. Type filtering is only valid on entity types and complex types.
Related question: How to combine 2different IQueryable/List/Collection with same base class? LINQ Union and Covariance issues
Simply define a
DbSet<SomeBase> Bases { get; set;}
property to access all instances of the base class. The framework should combine the query the right way (union) to include the instances from both tables.
For more details check out e.g. this article: http://weblogs.asp.net/manavi/archive/2011/01/03/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete-type-tpc-and-choosing-strategy-guidelines.aspx
(You use the TPC inheritance strategy)
maybe there is some more elegant way, but union should do it i guess:
db.As.Select(x => new { x.Id, x.Item1, x.Item2 } )
.Union(db.Bs.Select(x => new { x.Id, x.Item1, x.Item2 }));
if you want to include some fields from As and some fields from Bs then it should look like:
db.As.Select(x => new { x.Id, x.Item1, x.Item2, x.Afield, Bfield = null } )
.Union(db.Bs.Select(x => new { x.Id, x.Item1, x.Item2, AField = null, x.Bfield }));
How about:
var concatenatedSet = db.As.Local.OfType<SomeBase>().Concat(db.Bs.Local.OfType<SomeBase());

Return entity type when using table per type inheritance

Lets say I have the following entities
public abstract class Animal
{
public int Id {get;set;}
}
public class Cat : Animal
{
}
public class Dog : Animal
{
}
Is it possible to determine the type of entity without creating an instance.
var id = 1;
var type = context.Animals.GetTypeOfAnimal(id)
public static Type GetTypeOfAnimal(this ObjectSet<Animal> source, int id)
{
// What shall I do here, I dont want to fetch the instance at this point...
var animal = source.First(a => a.Id == id);
return animal.GetType();
}
One solution I thought about using the following method...
public static Type GetTypeOfAnimal(this ObjectSet<Animal> source, int id)
{
var info = source.Where(a => a.Id == id).Select(a => new {IsDog = a is Dog, IsCat = a is Cat}).First();
if(info.IsDog) return typeof(Dog);
if(info.IdCat) return typeof(Cat);
return null;
}
There is no way to get this information without query to database. You are using TPT - it means that database contains Animal, Dog and Cat tables. The inheritance in database is modeled through one-to-one relation between Animal and Dog and between Animal and Cat. The minimum what you have to do is query both Animal and Dog tables for that Id (it can exist only in one of them). The first problem is that you cannot query these tables directly with EF because EF can work only with whole entities (not only with parts mapped to single table) - you must use direct SQL. The second problem is fragility of this solution. If you add new derived entity you must fix this query (same happens for your example).
The reason why TPT queries are slow is that EF must query all inheritance tree = in your case Animal joined with Dog concatenated with Animal joined with Cat. There are some performance improvements in .NET 4.5 for querying TPT inheritance tree but it will not affect your query because it simply has to query whole structure.

C# Linq Query With Interface

I have a number of LINQ classes that inherit from the same base class. Each Linq class has its own table, and then the base class is a sort of link to another table. I'll try and illustrate the relationship:
//Base Class
abstract class Element<T>
{
public string Name {get; set;}
public string Species {get; set;}
}
public partial class DogElement : Element<DogElement>
{
public int Legs {get; set;}
public bool hasHair {get; set;}
}
public partial class CatElement : Element<CatElement>
{
public bool hasWiskers {get; set}
}
So DogElement and CatElement are separate physical tables in the database. The Species class will read from the Species table. I basically want to have a generic function in Element that groups the T table with the Species table and returns the result as a T after having set .Species.
//Assume T is DogElement for this example
public IQueryable<T> Collection
{
using (DataBase db = new DataBase())
{
var k = from t in db.GetTable<T>()
where t.SomeID == SomeOtherID
select t;
k = { {Legs = 4, HasHair = True, Species = <null>, Name = <null>}, ...}
I basically want a query that will return an IQueryable<DogElement> with Name
and Species set from a join query. What is the best way to set the Name and
Species values?
return k;
}
Essentially you are asking SQL to handle inheritance (it doesn't do that)... and LINQ doesn't model inheritance either. A good object relational mapper (ORM) will help you piece something together more generically.
That said, here's a quick discussion.
First off, I think it is better to write one LINQ query for each species-animal pair.
You could write a union, switch in the LINQ select, create a new animal, and then cast that animal to a species in one go... then outside the LINQ statement, recast it to T on the way out. This kind of statement sounds messy though.
Although this is less efficient than one big union statement, have you considered this more maintainable option?
var a from speciies select ..
if(T is Dog){
additional linq statement}
else if(T is Cat){
additional linq statement}
that's two trips to the database (slower), but it's more maintainable, and often it is better not to over optimise your code on the first pass.

Categories