How do I improve performance using TPT in Entity Framework - c#

I've been building application using TPT (Table-Per-Type) in entity framework 6.
I have these classes
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Customer : Person
{
public string BillingAddress { get; set; }
}
public class Vendor : Person
{
public string Address { get; set; }
}
public class IndividualCustomer : Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
public class CorporateCustomer : Customer
{
public string ContactPerson { get; set; }
public string CorporateTaxNo { get; set; }
}
public class SalesInvoice
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public Customer Customer { get; set; }
}
The query is very ugly and took a long time to generate when i query against the base class (which are Person and Customer), But quite ok when querying the derived class (IndividualCustomer & CorporateCustomer).
I have read that using TPT is very bad for performance and i'm considering to use TPH (Table-Per-Hierarchy) because of this. But when i use TPH, when adding SalesInvoice, i will have to validate if the user is sending the correct CustomerId. Because database will allow the user sending a vendorid as CustomerId. While using TPT, database will throw error. So TPH will add complexity when doing CRUD.
Are there any suggestion how to improve TPT query on my case?
Note:
I found that when querying the base class, if i specify which fields i want to retrieve, eg Select(x => new {x.Id, x.Name}), the query looks better and does not include left outer join the derived classes.
My Queries:
//Querying base class
var people = ctx.People.Select(x => new {x.Id, x.Name}).ToList();
//Querying derived class
var corporates = ctx.CorporateCustomers.ToList();

Related

How to map recipes with ingredients using AutoMapper

I have following RecipeModel, IngredientModel and RecipePartModel classes which represent the DTO classes for the frontend user:
public class RecipeModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public IEnumerable<RecipePartModel> RecipeParts { get; set; }
}
public class IngredientModel
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class RecipePartModel
{
public Guid Id { get; set; }
public IngredientModel Ingredient { get; set; }
public string Unit { get; set; }
public decimal Quantity { get; set; }
}
Here are my entity classes:
public class Recipe : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public virtual IEnumerable<RecipePart> RecipeParts { get; set; }
}
public class Ingredient : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
public virtual IEnumerable<RecipePart> RecipeParts { get; set; }
}
public class RecipePart : BaseEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
public Ingredient Ingredient { get; set; }
public Recipe Recipe { get; set; }
public string Unit { get; set; }
public decimal Quantity { get; set; }
}
My question is - how can I map the Recipe to RecipeModel using AutoMapper? I tried something like this but I assume it is bad, because it just join all the RecipeParts for the whole database, am I correct?
public class DomainProfile : Profile
{
public DomainProfile()
{
CreateMap<Ingredient, IngredientModel>().ReverseMap();
CreateMap<Recipe, RecipeModel>()
.ForMember(x => x.RecipeParts, opt => opt.MapFrom(src => src.RecipeParts));
}
}
To answer your question about how to use AutoMapper to map a type to another type, there are many ways of doing this. Documentation is here: http://docs.automapper.org/en/stable/Getting-started.html.
I wrote a console app and got it working in the quickest way I know possible using your code. When I debug this, and check inside recipeModel, it references a list of RecipePartModels with a single RecipePartModel. Inside that RecipePartModel, it references an IngredientModel.
static void Main(string[] args)
{
var profile = new DomainProfile();
Mapper.Initialize(cfg => cfg.AddProfile(profile));
var recipe = new Recipe
{
RecipeParts = new List<RecipePart>
{
new RecipePart()
{
Ingredient = new Ingredient()
}
}
};
var recipeModel = Mapper.Map<Recipe, RecipeModel>(recipe);
Console.ReadKey();
}
To answer your concern about getting all recipes from the database, if you're using Entity Framework, it depends on if you have lazy loading turned on. Lazy loading ensures that, when you get a recipe from the database, the recipe parts will not be loaded. They will only be loaded when you access the recipe part directly later on in the program flow. Lazy loading is turned on by default so this is the default behaviour. If you turn it off, you've enabled eager loading which loads all recipe parts and in turn their ingredient.
This might help: http://www.entityframeworktutorial.net/lazyloading-in-entity-framework.aspx.
There is nothing bad about this mapping. In fact you don't even need the ForMember call as this is the default convention. The mapping will simply convert each element in the entity child collection to a corresponding model object.
Of course, whether you load your entities in an efficient manner is another matter. If you load a large amount of Recipe entities, and lazy load the RecipeParts collections for each, you will have a major "SELECT N+1" problem. But this is not the fault of AutoMapper.

Entity Framework Code First One Property of Class and List of The Same Class

I'm using entity framework code first approach
I have a class
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public Person Director { get; set; }
public virtual ICollection<Person> Actors { get; set; }
}
and a class
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
When the database is created I get one table Movies with Id, Title, Director_Id and a table Person with Id and Name.
I expect to have a table Movies_Persons with columns Movie_Id and Actor_Id
How can I achieve this?
Your Problem is, that you don`t tell the Person Class, that there can be multiple Movies per person.
So by adding the following line in your person class:
public virtual ICollection<Movie> Movies { get; set; }
Your entity knows that both your classes can have multiple references to the other class.
To fulfill this requirement Entity Framework will create a third table with Movie_ID and Person_ID.
If you want more informations just look for:
Entity Framework - Many to many relationship
or follow this link:
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
You can check out the other articels on that page too, if you are new to entity framework.
UPDATE:
Sorry i missed, that you are already have another reference to your person table.
Here you have to tell your entity framework, which way you want to reference the two tables by fluent api.
Check out this stackoverflow answer. That should do the trick.
You have to insert this code into your OnModelCreating Function of your DbContext Class.
So your final code should look like this:
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public virtual Person Director { get; set; }
public virtual ICollection<Person> Actors { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Movie> Movies_Actors { get; set; }
public virtual ICollection<Movie> Movies_Directors { get; set; }
}
And in your OnModelCreating add following code:
modelBuilder.Entity<Movie>()
.HasMany(a => a.Actors)
.WithMany(a => a.Movies_Actors)
.Map(x =>
{
x.MapLeftKey("Movie_ID");
x.MapRightKey("Person_ID");
x.ToTable("Movie_Actor");
});
modelBuilder.Entity<Movie>()
.HasRequired<Person>(s => s.Director)
.WithMany(s => s.Movies_Directors);
I don't have the possibility to test the code, but that should do the trick.
If you have to do some adjustments to make it work, plz add them in the comments, so other ppl can benefit from it.

Entity Framework principal end of an association error

I have two models
class Employee {
[Key]
public int ID {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public int EmploymentID {get;set;}
[Required, ForeignKey("Employment")]
public virtual Employment Employment {get;set;}
}
class Employment {
[Key, ForeignKey("Employee")]
public int ID {get;set;}
public string Department {get;set;}
public int OfficePhone {get;set;}
public int EmployeeID {get;set;}
public virtual Employee Employee {get;set;}
}
basically each employee has employment information in the Employment class. I'm not sure if I need the [Required] annotation there, and I don't know if I'm putting the [ForeignKey] annotation in the right spot either.
The problem is, when I try to create a new scaffolded item, it gives me this error:
Unable to determine the principal end of an association between the types 'Bla.Models.Employee' and 'Bla.Models.Employment'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Thank you for the help
EDIT
assume the Employee model has the following instead:
class Employee {
[Key]
public int ID {get;set;}
public string FirstName {get;set;}
//note the return value is an ICollection object
public ICollection<LastName> LastName {get;set;}
public int EmploymentID {get;set}
public virtual Employment Employment {get;set;}
}
and the Employment model remains the same,
and the LastName field has the following class
class LastName {
public string EmployeeLastName {get;set;}
//assuming last name can change, and employee had a different last name at some point
public int year{get;set;}
}
is it incorrect to make the LastName class, a model? or should it remain a model?
how can I make it so that its just a resource class (i.e. not a model to be made into a table)
further, will this kind of thing break the relationships between employee/employment models?
because I'm still getting the error and not sure why; by the way, I have many of these classes like the "LastName" example and they are all currently under models, and I'm not sure if they should be a model or some resource class, and if so, I don't know where they are supposed to go
also heres my dbcontext
public class MyDbContext : DbContext
{
public MyDbContext()
: base("MyDbContext")
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Employment> Employments { get; set; }
public DbSet<BasicAddress> Adresses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<BasicDate> BasicDate { get; set; }
public DbSet<BasicPhoneNumber> BasicPhoneNumber { get; set; }
public DbSet<EmployeeIdentification> EmployeeIdentification { get; set; }
public DbSet<FederalIdentification> FederalIdentification { get; set; }
public DbSet<JobTitle> JobTitle { get; set; }
public DbSet<LastName> LastName { get; set; }
public DbSet<MaritalStatus> MaritalStatus { get; set; }
public DbSet<OfficeLocation> OfficeLocation { get; set; }
}
everything other than employee and employment is a basic class (like the LastName class), I'm not sure if they should be with "models" and made into tables or just be regular classes on the side. If they shouldn't be made into tables, where should they go in the project?
thank you again for the help! (please let me know if anything needs clarification)
Employment cannot exist with Employee. As per the law of foreign relationship the Employment must not be saved without Employee. Thus with the help of [ForeignKey] notation you need to advise Employment that Employee relation should be maintained.
Employment is dependent on Employee. Thus you got to tell Employment that you are got to have a linking with the principal.
Thus dependent class must have Employee id reference.
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace TestConsole
{
public class Employee
{
public int ID { get; set; }
[Required]
public string FirstName { get; set; }
public string LastName { get; set; }
[Required]
public virtual Employment EmploymentDetails { get; set; }
}
public class Employment
{
[Key, ForeignKey("Employee")]
public int ID { get; set; }
public string Department { get; set; }
public int OfficePhone { get; set; }
public virtual Employee Employee { get; set; }
}
internal class Program
{
public TestConsoleDbContext MyDbContext { get; set; }
public Program()
{
MyDbContext = new TestConsoleDbContext();
}
private static void Main(string[] args)
{
var program = new Program();
var records = from p in program.MyDbContext.Employees
select new { p.EmploymentId, p.LastName, p.Employment.Department };
foreach (var r in records)
{
Console.WriteLine("EmploymentID: {0} {1} Department: {2}", r.EmploymentId, r.Department);
}
Console.ReadLine();
}
}
}
using System.Data.Entity;
namespace TestConsole
{
internal class TestDbContext : DbContext
{
public IDbSet<Employee> Employees { get; set; }
public IDbSet<Employment> Employments { get; set; }
}
}
I don't think Employment field needs the Required attribute and the ForeignKey attribute must be applied to the EmploymentID field.

Entity relationship multiple inheritance issue

I have this entity model:
I want to have a Customer entity, which can be either a Person or an Organization, It can't be both.
As of now I came up with a customer entity which points to both organization and person but with a nullable field (meaning Guid?), which means having a non-mandatory relationship with Person and Organization. Something like:
class Customer
{
public Guid ID { get; set; }
public Guid? RelatedPersonID { get; set; }
public Guid? RelatedOrganizationID { get; set; }
public int CustomerStatus { get; set; }
public bool IsVIP { get; set; }
// ... other customer related properties
public virtual Person RelatedPerson { get; set; }
public virtual Organization RelatedOrganization { get; set; }
}
I'm using entity framework 5, codefirst approach and I haven't created the database yet. I was wondering if there's a better model that meets this requirements.

How to add userId to every query without adding it to all domain classes?

I'm working with sqlite and the c# sqlite-net library.
Some of my entities:
public class Product
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(50)]
public string Brand { get; set; }
[MaxLength(50)]
public string Type { get; set; }
[MaxLength(50)]
}
public class ProductImage
{
public int Id { get; set; }
public string Name { get; set; }
public string Container { get; set; }
public int ProductId { get; set; }
}
Now all entities belong to a user and the user should always work only with his own entities. So when I insert an entity in the database I want to store the userId with it.
However... I don't want to add the userId to all my domain classes.
I'm storing my entities like this right now:
await _databaseManager.GetDatabaseInstance().InsertAsync(entity);
and selecting like this:
var products = await _databaseManager.GetDatabaseInstance().Table().ToListAsync();
So is there a way with sqlite-net to add the userId to the database without adding it to all domain classes?
Doesn't it support inheritance, as in looking at the type's hierarchy? I would suggest it would, or at least should. So, if it does you could use an abstract base class or an interface. Something like this:
public abstract class StandardEntity {
public int UserId { get; set; } // decorate with attributes as necessary
}
And inherit:
public class Product : StandardEntity {
}

Categories

Resources