I'm kind of confused about what relationMappings do inside a Objection.js Model class.
I thought once we setup the relationMapping inside a Model, we will get related data in every query. But, it turns out that I still only the Model properties itself.
Is there anything else I should use to get related data in query?
Relation mappings gives model semantics how relations can be fetched when they are needed. It would be really bad for performance to always query all related rows in addition to main table's row. When you create relation mappings to model, you will not need to write joins manually every time you need to query relations. Also they enable many other objection features, which requires information how row relations goes in DB.
To use relation mappings in query Objection.js requires that within every query you must tell which relations you want to fetch with the main row with .withGraphFetched or .withGraphJoined methods https://vincit.github.io/objection.js/guide/query-examples.html#eager-loading
for example:
class Person extends Model {
static get tableName() {
return 'persons';
}
static get relationMappings() {
return {
pets: {
relation: Model.HasManyRelation,
modelClass: Animal,
join: {
from: 'persons.id',
to: 'animals.ownerId'
}
}
};
}
}
const people = await Person.query().withGraphFetched('pets');
// Each person has the `pets` property populated with Animal objects related
// through the `pets` relation.
console.log(people[0].pets[0].name);
console.log(people[0].pets[0] instanceof Animal); // --> true
Mappings are also used when you insert nested object data with .insertGraph so that related objects are inserted to related tables and foreign key references etc. are automatically filled according to relation mapping declarations.
There are many other places where those are used, but I hope this gives a rough idea why they exist.
Related
I am working on a system that have lots of users each has his own information so I needed to create a model for each. On the other hand, all those users have a common user model where their credentials being collected form those requirements it was suitable to have a polymorphic relation between all user types and the user model i.e., coordinator as a model and the user as a model I did the following
class Coordinator extends Model
{
protected $fillable= ['userid', ...];
...
public function user()
{
return $this->morphOne(User::class, 'userable');
}
}
class User extends Model
{
...
public function userable()
{
return $this->morphTo();
}
}
class CreateUsersTable extends Migration
{
public function up()
{
$table->bigIncrements('id');
...
$table->morphs('userable');
}
}
class CreateCoordinatorsTable extends Migration
{
public function up()
{
$table->bigIncrements('coordid');
...
$table->foreign('userid')->references('ID')->on('wp_users')->onDelete('cascade');
}
}
After migration I noticed that columns userable_type and userable_id not allowing null. How come I create a coordinator entity with its associated user entity?
I got it after googling...
Laravel One to Many Polymorphic Relationship - Create Records
The idea is not as I thought at the beginning. When I used the $table->morphs('userable'); the user table had two columns, userable_id and userable_type, and each allows no null by default and by ORM convention and I was thinking that they consider the user table is the master and other tables (i.e., coordinator, educator, shipper etc.) each as the detail table. According to this initial wrong understanding I was adding a userid column at each table to store the related user id for each specific user type and I was wondering how I am going to save the user that needs the userable_id and userable_type to be filled by saving the specific user first to get its id that will be provided to the user's userable_id the case resembles a deadlock situation as each table needs a piece of information that will be known after storing data on each to be able to save data to each table, wired!!!.
However, when I read the article in the above link I figured that it considers the table in an opposite way, unlike my thinking was (i.e., coordinator, shipper, etc. are the masters and the user table is the detail). That is for guys who use the models directly but for those who use repository package it is a little bit tricky and you need to do an extra work...
I have a legacy database with a particular table -- I will call it ItemTable -- that can have billions of rows of data. To overcome database restrictions, we have decided to split the table into "silos" whenever the number of rows reaches 100,000,000. So, ItemTable will exist, then a procedure will run in the middle of the night to check the number of rows. If numberOfRows is > 100,000,000 then silo1_ItemTable will be created. Any Items added to the database from now on will be added to silo1_ItemTable (until it grows to big, then silo2_ItemTable will exist...)
ItemTable and silo1_ItemTable can be mapped to the same Item entity because the table structures are identical, but I am not sure how to set this mapping up at runtime, or how to specify the table name for my queries. All inserts should be added to the latest siloX_ItemTable, and all Reads should be from a specified siloX_ItemTable.
I have a separate siloTracker table that will give me the table name to insert/read the data from, but I am not sure how I can use this with entity framework...
Thoughts?
You could try to use the Entity Inheritance to get this. So you have a base class which has all the fields mapped to ItemTable and then you have descendant classes that inherit from ItemTable entity and is mapped to the silo tables in the db. Every time you create a new silo you create a new entity mapped to that silo table.
[Table("ItemTable")]
public class Item
{
//All the fields in the table goes here
}
[Table("silo1_ItemTable")]
public class Silo1Item : Item
{
}
[Table("silo2_ItemTable")]
public class Silo2Item : Item
{
}
You can find more information on this here
Other option is to create a view that creates a union of all those table and map your entity to that view.
As mentioned in my comment, to solve this problem I am using the SQLQuery method that is exposed by DBSet. Since all my item tables have the exact same schema, I can use the SQLQuery to define my own query and I can pass in the name of the table to the query. Tested on my system and it is working well.
See this link for an explanation of running raw queries with entity framework:
EF raw query documentation
If anyone has a better way to solve my question, please leave a comment.
[UPDATE]
I agree that stored procedures are also a great option, but for some reason my management is very resistant to make any changes to our database. It is easier for me (and our customers) to put the sql in code and acknowledge the fact that there is raw sql. At least I can hide it from the other layers rather easily.
[/UPDATE]
Possible solution for this problem may be using context initialization with DbCompiledModel param:
var builder = new DbModelBuilder(DbModelBuilderVersion.V6_0);
builder.Configurations.Add(new EntityTypeConfiguration<EntityName>());
builder.Entity<EntityName>().ToTable("TableNameDefinedInRuntime");
var dynamicContext = new MyDbContext(builder.Build(context.Database.Connection).Compile());
For some reason in EF6 it fails on second table request, but mapping inside context looks correct on the moment of execution.
We are using EF 4.3 Code first and have an object model like so:
class Content { }
class Product:Content { }
class News:Content { }
These are mapped as Table per Type.
There are scenarios where I just want to load only the columns belonging to the base table, like say a list of all the content titles. But a query like
from c in Content
where c.IsDeleted == false
select c
results in some really nasty SQL with joins to the other two tables. Is there any way to force EF to just do a select from the base table only without joins to the other tables?
TPT is problematic and EF generated queries are usually very inefficient. Moreover your expectations are probably incorrect. Linq-to-entities always returns the real type of entity. It cannot return instance of Content type if the record is in fact a Product entity. Your query can have only two meanings:
Return all non deleted contents - this must perform joins to correctly instantiate a real types of entities. The query will return enumeration of Content, Product and News instances.
Return all non deleted Content instances - this must probably again perform joins to correctly instantiate only records mapped to Content directly (without relation to Product and News). No record mapped to Product or News will be returned in the enumeration. This query is not possible with Linq-to-entities - you need to use ESQL and OFTYPE ONLY operator.
There are few things you can try:
Upgrade to .NET 4.5 - there are some improvements for TPT queries
Return projection of properties instead of Content - Product and News are also content so you will never get query without joins if you return Content instances from Linq-to-entities query
I'm pretty new to the Entity framework and I'm modelling this simple structure:
With this model what I have is a Users class with a property UsersGroups (a collection of UserGroups objects).
I would like to have a Users class with a property like Groups with type Tuple or something like this (a new PriorizedGroup class, etc) that is much more related with the bussines.
Is this possible with the Entity framework?
Thanks in advance.
EDIT: If I were modeling the bussines objects I would create a User class with a Groups property that contained all the groups the user pertains with an extra property to store its priority (with a tuple, with an inherited class, as you wish). The thing is that I feel that the objects created by the Entity framework resemble the SQL structure, not the business structure.
Not directly. EF can map the relation only in the way you see it at the moment but you can add your custom behavior to your partial part of the entity. The simple way is something like
public partial class Users
{
public IEnumerable<PrioritizedGroup> Groups
{
get
{
return UserGroups.Select(ug => new PrioritizedGroup
{
Priority = ug.Priority,
Id = ug.Group.Id,
Name = ug.Group.Name,
Description = ug.Group.Description
})
.OrderBy(g => g.Priority);
}
}
}
To make this happen directly in EF you need some advanced mapping technique which will require you to modify EDMX source code directly (either DefiningQuery or QueryView) and it will make the entity read only (you will need stored procedures for modification).
To make the collection exposed on Users updatable you would probably need to use ObservableCollection and transfer all modifications triggered by ObservableCollection back to original UserGroups collection. Once you have something like that implemented you can hide original collection.
In EF eager loading related entities is easy.
But I'm having difficulties including inherited entities when loading data using table-per-type model.
This is my model:
Entities:
ArticleBase (base article entity)
ArticleSpecial (inherited from ArticleBase)
UserBase (base user entity)
UserSpecial (inherited from UserBase)
Image
Relations are as shown on the image (omitting many columns):
In reality my users are always of type UserSpecial, since UserBase is used in another application, thus we can share credentials. That's the only reason I have two separate tables. UserBase table can't be changed in any way shape or form, because the other app would break.
Question
How am I suppose to load ArticleSpecial with both CreatedBy and EditedBy set, so that both are of type UserSpecial (that defines Image relation)?
I've tried (unsuccessfully though) these options:
1.
Using lambda expressions:
context.ArticleBases
.OfType<ArticleSpecial>()
.Include("UserCreated.Image")
.Include("UserEdited.Image");
In this case the problem is that both CreatedBy and EditedBy are related to UserBase, that doesn't define Image navigation. So I should somehow cast these two to UserSpecial type like:
context.ArticleBases
.OfType<ArticleSpecial>()
.Include("UserCreated<UserSpecial>.Image")
.Include("UserEdited<UserSpecial>.Image");
But of course using generics in Include("UserCreated<UserSpecial>.Image") don't work.
2.
I have tried using LINQ query
var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
join created in ctx.UserBase.OfType<UserSpecial>().Include("Image")
on articleSpecial.UserCreated.Id equals created.Id
join edited in ctx.UserBase.OfType<UserSpecial>().Include("Image")
on articleSpecial.UserEdited.Id equals edited.Id
select articleSpecial;
In this case I'm only getting ArticleSpecial object instances without related properties being set. I know I should select those somehow, but I don't know how?
Select part in my LINQ could be changed to something like
select new { articleSpecial, articleSpecial.UserCreated, articleSpecial.UserEdited };
but images are still not loaded into my context. My joins in this case are barely used to filter out articleSpecial results, but they don't load entities into context (I suppose).
This seems to be a limitation in the current version of Entity Framework (1.0) Have a look at this related SO question.
In your case including the related UserCreated and UserEdited properties in the projection is the right solution. However if you also want to populate the Image property on the UserSpecial object, you must be sure to include that as well:
var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
select new
{
articleSpecial,
articleSpecial.UserCreated,
((UserSpecial)articleSpecial.UserCreated).Image,
articleSpecial.UserEdited,
((UserSpecial)articleSpecial.UserEdited).Image
};
Of course this query builds on the assumption that all ArticleSpecial entities always refer to a UserSpecial entity, otherwise the casting will fail.
If this assumption isn't always true, you could express the same query using the LINQ extension methods and a multi-line lambda function to perform a safe casting:
var results = ctx.ArticleBase
.OfType<ArticleSpecial>()
.AsEnumerable()
.Select(a =>
{
var userCreated = a.UserCreated as UserSpecial;
if (userCreated != null)
{
var image = userCreated.Image;
}
var userEdited = a.UserEdited as UserSpecial;
if (userEdited != null)
{
var image = userEdited.Image;
}
return a;
});
In the latter example, you also do not need to include UserSpecial and Image entities in the results. Instead you just need to access the navigation properties on the ArticleSpecial entities during the projection phase in order to force Entity Framework to eager load the related objects.