Generic table loading EntityFramework - c#

There are two tables: Teachers and Students both of them can be derived from a base class Human.
May load a DbSet<Human> from Teacher and Student class? and do a generic function, e.g. generic inserting
pseudo code:
class Student : Human
{
//
//
//
DbSet<Human> GenericLoading(...)
{
//
}
}
usage of generic DbSet:
void Insert<T>(DbSet<T> entities, string name, int age) where T: new(),Human
{
entities.Add(new T{ Name = name, Age = age });
}
any help would be greatly appreciated.

Sounds like you're looking for Table-Per-Concrete-Type (TPC) inheritance.
Derive the two entities from the same base class Human and configure each entity class use its own table, then declare a DbSet<Human> in your DbContext

You could use IQueryable.Concat to create a union Query, if it is about loading the data:
var teacherQuery = db.Teacher.Select(e => new Person {...})
var studentQuery = db.Student.Select(e => new Person {...})
var combinedQuery = teacherQuery.Concat(studentQuery);
I am not sure how you could do an insert, if you don`t know the exact properties.

Related

How to query using TableQuery via IEnumerable?

I am exposing my Table via the following method:
public static class TableCacheService
{
public static IEnumerable<Myentity> Read()
{
var acc = new CloudStorageAccount(
new StorageCredentials("account", "mypass"), false);
var tableClient = acc.CreateCloudTableClient();
var table = tableClient.GetTableReference("Myentity");
return table.ExecuteQuery(new TableQuery<Myentity>());
}
}
Here's a usage example:
var nodeHasValue = TableCacheService.Read()
.Where(x => note.ToLower().Contains(x.RowKey.ToLower()))
.Any();
But supposedly x.RowKey is null!
It looks like there are 2 members both called RowKey. One of them is a TableEntity:
How do I access the RowKey that is non-null? Am I incorrectly querying the table?
It seems you have hiding properties RowKey and PartitionKey inside your Myentity class.
I guess your Myentity class has something like it:
public class Myentity: TableEntity
{
public new string RowKey { get; set; }
...
}
Pay attention to keyword new.
If you have this construction you need to delete this property.
More information about hiding and overriding you can find here.
Also please review work example with ATS (Azure Table Storage) is here.

How to query objects by entity when more than 1 entity derive from a common entity and saved in the same table

Let's say I have a parent class with 2 sub-classes, with the following configuration:
modelBuilder.Entity<ParentType>(entity =>
{
entity.HasDiscriminator()
.HasValue<ChildA>("ChildA")
.HasValue<ChildB>("ChildB");
}
So how do I pull the data based on the child type?
var result = context.ParentTypes.
.Where(x => ...);
In the table, I see a column called Discriminator with values, such as ChildA and ChildB. However, there's no such property on x.Discriminator.
When writing queries against a TPH (Table per Hierarchy) Entity Framework configuration, you can use the OfType<T> LinQ method to filter the types. This also lets you access the properties in that derived class. For example if we had a ChildA class like this:
public class ChildA : ParentType
{
public string FavouriteFood { get; set; }
}
We could query like this:
var childAWhoLikeCheese = context.ParentTypes
.OfType<ChildA>()
.Where(x => x.FavouriteFood == "Cheese");
This will actually create a query something like this:
SELECT ...
FROM ParentTypes
WHERE Discriminator = "ChildA"
AND FavouriteFood = "Cheese"

Inheritance in DbSet<T> EntityFramework for similar tables

I want to do Inheritance for similar tables.
For example suppose we have two tables: Teachers and Students both of them can be derived from a base class Human.
Is it possible to write a common task and avoid repeat code using EntityFramework? a function like this which works for DbSet<Student> and DbSet<Teacher>:
void IncreaseAge(DbSet<Human> humans, int id)
{
//
}
and more important, a generic add function to db, pseudo code:
void AddHuman({...}, string name, int age)
{
// add a human to related table
}
any help would be greatly appreciated.
Use Extension with a Generic parameter:
void IncreaseAge<T>(DbSet<T> entities, int id) where T: Human
{
var current = entities.Find(id);
current.Age++;
// SaveChanges() in the context
}
If your Student is inheriting Age property from Human class. This code should work perfectly.
Edited
Also, you can apply the same technique for the add
void Insert<T>(DbSet<T> entities, string name, int age) where T: new(), Human
{
entities.Add(new T{ Name = name, Age = age });
// SaveChanges() in the context
}
Hope this help!
A DbSet represents all the entities. You'd normally not pass it to a function. Instead consider a method like this in your DbContext:
public void IncreaseAge(IQueryable<Human> humans)
{
foreach( var h in humans)
{
h.Age++;
}
SaveChanges();
}
Then you can pass in an query that specifies a set of teachers or students you'd like to operate on. EG:
db.IncreaseAge(db.Teachers.Where(t => t.Age == 47));
I guess you have read about the various strategies when using inheritance in Entity Framework. The nice thing about entity framework is that it hides the used inheritance strategy.
As the three inheritance strategies are described in the link, there is no need to describe the fluent API needed for this. I'll only write the classes you'll end up with and how to do queries on Students, Teachers and the common base class Persons.
Furthermore I'll describe some considerations needed to properly select the correct inheritance strategy.
So you have Student and Teachers, both derived from Person.
abstract class Person
{
public string Name {get; set}
public Gender Gender {get; set;}
public DateTime Birthday {get; set;}
}
public class Teacher : Person
{
public int Id {get; set;}
...
}
public class Student : Person
{
public int Id {get; set;}
...
}
And the DbContext:
public class MyDbContext : DbContext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
}
Now whenever you have an IQueryable of Teachers or Students you can also use all properties of Person, without doing something special.
var maleTeachers = myDbContext.Teachers
.Where(teacher => teacher.Gender == Gender.Male);
var youngStudents = myDbcontext.Students
.Where(student => student.Birthday > new Datetime(2000, 1, 1);
If you have to query all Persons, you'll have to Concatenate the Students and the Teachers. In baby steps:
IQueryable<Person> teachers = myDbcontext.Teachers.Cast<Person>();
IQueryable<Person> students = myDbContext.Students.Cast<Person>();
IQueryable<Person> allPersons = teachers.Concat(students);
var result = allPersons.Where(person => ...)
.Select(person => ...)
... etc
Of course this can be done in one statement.
Decide which inheritance strategy to use
When deciding on the inheritance strategy, keep in mind what kind of queries you'll do most:
Query Teachers who ... or Query Students who ...
Query Persons that ...
If you do the first most often, then consider Table per concrete class (TPC). You'll have two tables, one for students and one for teachers. The Person properties are in the same table. So table Students will have Person columns; table Teachers will also have Person columns.
The advantage is that if you ask for "Students who ...", only one table is involved. a join won't be necessary.
The disadvantage is that if you ask for "Persons that ...", the Teachers and Students tables need to be concatenated.
If you will be querying Persons more often, consider creating Table per Type (TPT). The result will be three tables with foreign keys: Teachers, Persons, Students. When asking for Persons, only one table is involved. However, when asking for Teachers we always need to join two tables.
Even if you opt for the best inheritance strategy because those are the kind of queries you perform most often you might sometimes have to do the other type of queries.
TPC: Table per concrete class
You choose this if you mostly ask for Teachers that ... or Students who ... You'll end up with two tables: one for Students, and one for Teachers. There will be no Persons table.
If you have to do an occasional Person query you'll have to concatenate the two sequences. This is in baby steps:
IQueryable<Person> teachers = myDbcontext.Teachers.Cast<Person>();
IQueryable<Person> students = myDbContext.Students.Cast<Person>();
IQueryable<Person> allPersons = teachers.Concat(students);
var result = allPersons.Where(person => ...)
.Select(person => ...)
... etc
Of course this can be done in one statement.
If you have to do this more often consider adding a prperty to your DbContext class that does this for you:
class MyDbcontext : Dbcontext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
public IQueryable<Person> Persons
{
get
{
return this.Teachers.Cast<Person>()
.Concat(this.Students.Cast<Person>());
}
}
}
Usage will be:
using (var myDbContext = new MyDbContext(...))
{
IQueryable<Person> females = myDbcontext.Persons
.Where(person => person.Gender == Gender.Female);
}
If you don't want to pollute your Dbcontext, consider creating an extension function that does the same. See extension functions demystified
static class MyDbContextExtensions
{
IQueryable<Person> Persons(this MyDbContext dbContext)
{
return dbContext.Teachers.Cast<Person>()
.Concat(dbContext.Students.Cast<Person>());
}
}
TPT: table per type
You'll end up with three tables, Students, Teachers, Persons. Students and Teachers will have a foreign key to the Persons they are.
You can query Persons directly using the DbSet. On the other hand, if you want only Teachers who..., you'll just access the DbSet. As soon as you use one of the inherited Person properties, Entity framework will automatically do the join for you. You won't see this in the code. So even though you don't do a join, internally two tables are involved, which might be less efficient.
So be careful which inheritance strategy you choose.

Can I define a filter on a property within a relationship in Entity Framework 6?

Before I get started, note that I've simplified the data structure for what I'm trying to do and in the real world it isn't as awful of an implementation as you might think. Or maybe it is. Regardless, I can't change the way the data is structured so I'm not looking for suggestions on how to better structure the data. I'm just hoping to see if there is a way I can do what I'm asking in Entity Framework 6.
Consider I have the following tables:
Person:
ID FirstName LastName
1 John Smith
2 Jane Doe
Job:
ID Name
1 Developer
ExtendedData:
ID TableName RowID FieldName FieldValue
1 Person 1 MiddleInitial A
2 Person 1 Gender M
3 Person 2 MiddleInitial B
4 Person 2 Gender F
5 Job 1 Description Develop Stuff
The purpose of this ExtendedData table is to allow for additional data to be stored when there isn't a column for the data.
For example, here "MiddleInitial" is not a column in the Person table, but in the ExtendedData table we can add a row to store that data.
In my "Person" class I can add the following code to add an ExtendedData property:
public virtual ICollection<ExtendedData> ExtendedData { get; set; }
Then I can create a relationship in Entity Framework with this:
modelBuilder.Entity<Person>()
.HasMany(e => e.ExtendedData)
.WithRequired(e => e.Person)
.HasForeignKey(e => e.RowID);
The concern I have, is if I call...
john = Persons.Where(a => a.ID == 1);
john.ExtendedData...
... I'll get back all Extended Data rows where RowID = 1, including the row for the "Job" table.
Obviously, I could do something like...
john.ExtendedData.Where(a => a.TableName == "Person")...
... but this is a little dangerous because what if I (or some other developer) forget to specify that extra filter in the code?
I tried doing something like this...
modelBuilder.Entity<Person>()
.HasMany(e => (ICollection<ExtendedData>))e.ExtendedData.Where(a => a.TableName == "Person"))
.WithRequired(e => e.Person)
.HasForeignKey(e => e.RowID);
... but received an error at run time stating...
The expression 'e => Convert(e.ExtendedData.Where(a => (a.TableName ==
"Person")))' is not a valid property expression. The expression should
represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t)
t.MyProperty'.
The sense I make of it is it wants me to specify a property from "e" and not try to do any further wizardry.
Is there anywhere I can modify the Entity Framework model such that when I call
person.ExtendedData it will only return to me ExtendedData records where the TableName = "Person"? Or must I remember to always include that extra filter when trying to pull data from the ExtendedData table?
What you really want here is Table Per Hierarchy. Define a base entity for your table, then define an inherited entity for each variation, and customize the discriminator.
Public abstract class ExtendedData
{
public int Id {get;set;}
public string FieldName {get;set;}
public string FieldValue {get;set;}
}
public class ExtendedPersonData : ExtendedData
{}
public class ExtendedJobData : ExtendedData
{}
public class Person
{
....
public virtual ICollection<ExtendedPersonData> ExtendedData {get;set}
}
public class Job
{
....
public virtual ICollection<ExtendedJobData> ExtendedData {get;set;}
}
public class Context : DbContext
{
....
public DbSet<ExtendedData> ExtendedData {get;set;}
}
modelBuilder.Entity<ExtendedData>()
.Map<ExtendedPersonData>(m => m.Requires("TableName").HasValue("Person"))
.Map<ExtendedJobData>(m => m.Requires("TableName").HasValue("Job"));
with the inherited classes, you can now do Non-Polymorphic Queries against the table data, i.e.
IQueryable<ExtendedPersonData> query = from e in context.ExtendedData
.OfType<ExtendedPersonData>()
select e;
This will generate a SQL query which only returns records where the discriminator column matches ("TableName" == "Person" in this case). Returning a Person and querying it's ExtendedData would automatically create a Non-Polymorphic Query, due to the collection type defined on the entity.
Note that in this scenario, you can mix shared columns and columns unique to each variation. Any columns specific to a unique variation would automatically become nullable in the table, and would only be filled by the entities that provide it. In your case, however, you have no unique columns, so the Inherited classes are just implementation stubs.
http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph

NHibernate load the nested complex object entity in automapper queryable extension

I am having class employee which contain complex property Department.
public Class Employee
{
public int Id { get; set;}
public Department {get; set;}
}
public Class Department
{
public int Id {get;set;}
public string Name { get;set;}
}
i create the map for both
(consider the above both class are available in two namespace 'source', 'destination')
Mapper.CreateMap<source.Employee,destination.Employee>()
Mapper.CreateMap<source.Department,destination.Department>()
when i project it.
empQueryable.Project().To<destination.Employee>();
If i saw the NHProfiler
I found that it loads the entity Department
and create the query
select ... from employee left outer join Department .....
i don't know why it loads entity Department, it should make just the projection.
I'm going to go out on a limb here and assume that "destination.Employee" contains references to the "destination.Department". When AutoMapper builds a projection, it does the same as it would as "Mapper.Map". It crawls the destination type, its properties, and its member's properties all the way down. In short, it will build a Select expression something like:
.Select(e => new destination.Employee {
Id = e.Id,
Department = new destination.Department {
Id = e.Department.Id,
Name = e.Department.Name
}
});
You have a few choices here:
Ignore members you don't want mapped in your employee configuration, namely the "Department" property
Create targeted destination types based on use case, and don't share destination types that require different hydrated data based on different needs.
Use explicit expansion (ForMember(d => d.Department, opt => opt.ExplicitExpansion()), then explicitly expand that member in your projection as needed, with the overload for "Project.To" that takes a list of members to expand.

Categories

Resources