I would like to create a nested set model inside my database. However, I have a problem, 'cause I don't know how to start the implementation using Entity Framework Code-First.
Lets say I have such class:
public class Category
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Category> SubCategories { get; set; }
// public int left { get; set; }
// public int right { get; set; }
}
In my model I need SubCategories collection as it is right now. However I would like to implement automatic (implicit) update of left and right properties when I add/remove subcategories to/from SubCategories collection.
Is it possible?
Thank you for any answer in advance!
Best!
Nested sets and object graphs are two totally different models. You shouldn't store both of them in one type; that violates SRP.
Instead, put Left and Right on your code-first POCOs, load the, and then project/copy that onto objects of a different type (which are not entities) with a graph schema.
Related
EF7 fills contained navigation properties even when not requested. For example, I have the below entities.
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
My fetch query is as below.
ctx.Employees.Where(e => e.Gender == "Male").Include(e => e.Department)
I get Department property of Employee object filled – which is as expected as I have an Include for Department. I find that Department.Employees is also filled but partially (only with male employees). I have not specified an Include for Department.Employees, but it is still getting populated. Is this behavior by design? Is there any way to avoid fetching Department.Employees in this scenario?
I am using EF7 NuGet package with version 7.0.0-rc1-final.
That is the normal behavior of EF. When you execute your query, all the entities you load is going to be attached to your context. So, EF is not executing another query and loading Department.Employees partially, those employees were loaded earlier when you execute your query. In summary, when you consult Department.Employees navigation property, EF is going to fill that property with the employees that you load with your query filtering by Gender.
Update:
As I pointed out in my comment above, Lazy Loading is not supported in EF7. If you want to avoid that Json.NET serializes that property, you can use the attribute JsonIgnore over that property or you can create a custom class (DTO) to project your query and fill only the properties that you need. I also recommend take a look to Automapper if you decide to use this last solution.
In Entity Framework when I want to specify that an entity has many of another type of entity it seems to do things backwards to me.
For instance let's say I have a keyword entity that is used in several places throughout my app. All keywords are unique but on my other entities I want to have multiple keywords attached them so to me this would make sense:
class Page
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Keyword> Keywords { get; set; }
}
class Search
{
public int ID { get; set; }
public DateTime Date { get; set; }
public virtual ICollection<Keyword> Keywords { get; set; }
}
class Keyword
{
public int ID { get; set; }
public string Name { get; set; }
}
However when I do this the foreign key is added to the Keyword table whereas I want it to be on the actual entity so I can look at it in database and see small list of keywords instead of looking at keyword and seeing a ridiculous large number of page results.
So instead to get Entity Framework to put the Keyword_IDs on Page and Search entities I am doing this:
class Page
{
public int ID { get; set; }
public string Name { get; set; }
}
class Search
{
public int ID { get; set; }
public DateTime Date { get; set; }
}
class Keyword
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Page> Pages { get; set; }
public virtual ICollection<Search> Searches { get; set; }
}
This feels backwards as I am specifying the relationship on the entity that doesn't get the foreign ID field in the database table.
I feel like I am doing something wrong as I should be able to see the relationship by looking at my search & page class.
I am sorry for the basic question but for some reason I have read documentation and I am not fully understanding it.
In a one-to-many association it's always the many side that refers to the one side. How else would you implement it? If a Page would have a KeywordId as FK, it could only have one keyword, ever.
Also, even when a Keyword would belong to a myriad of pages, that doesn't mean you always have to access all of these pages through one keyword. You'd only do that if you'd do a search for pages in which specific keywords are used.
But now back to your model. You can't have one-to-many associations here. It would mean that any keyword can only belong to one Page or one Search. And if you invert the relationship, as you proposed, a Page or Search can only ever have one keyword (the one that Keyword_ID refers to).
In reality, you're dealing with many-to-many associations and the good news is, it leaves your Keyword intact.
Modelling it as many-to-many doesn't change the way your model looks (the first version), but the mapping is different:
modelBuilder.Entity<Page>().HasMany(p => p.Keywords)
.WithMany()
.Map(m =>
{
m.ToTable("PageKeyword");
m.MapLeftKey("PageID");
m.MapRightKey("KeywordID");
});
modelBuilder.Entity<Search>().HasMany(s => s.Keywords)
.WithMany()
.Map(m =>
{
m.ToTable("SearchKeyword");
m.MapLeftKey("SearchID");
m.MapRightKey("KeywordID");
});
This will generate two junction tables in your database, PageKeyword and SearchKeyword that record the many-to-many associations.
I have classes that are structured like the following:
public class Forecast
{
[Key]
[ForeignKey("Stop")]
public string Abbreviation { get; set; }
public virtual Stop Stop { get; set; }
public virtual List<Direction> Directions { get; set; }
}
public class Direction
{
public int DirectionId { get; set;}
public string Abbreviation { get; set;}
public virtual Forecast Forecast { get; set;}
public virtual List<Transport> Transports { get; set;}
}
public class Transport
{
public int TransportId { get; set; }
public int DirectionId { get; set;}
public virtual Direction Direction { get; set;}
}
public partial class Stop
{
[Key]
public string Abbreviation { get; set; }
public virtual Forecast Forecast { get; set; }
}
I developed these classes and used EF Code First 4.1 to generate the database. CF does appear to properly create all of the primary and foreign key relationships between the classes within the database (MSSQL).
My problem is when I want to delete a Forecast. I thought I do could something like the following:
using (MyContext ctxt = new MyContext())
{
// get a forecast, somehow, not really important
// The one assumption is I'm absolutely sure it's
// Abbreviation key already exists in the database
// and the list of Forecasts.
Forecast f;
ctxt.Forecasts.Remove(f);
}
This deletes the top-level object from the database just fine. However, all of its child objects - all of the directions and transports - remain in the database and become orphaned (their key relationship column gets set to null. I expect that but I DON'T know why they're not just deleted). I have resorted to recursing down the object graph and calling Remove on every object from its appropriate DbSet in ctxt, but that seems like... the wrong way to do it.
What am I missing here?
Why can't I just say
ctxt.Forecasts.Remove(f);
and be done with it?
Edit:
#Ladislav gave me the right answer - I
needed to add [Required] to the
Abbreviation property on Direction.
However, I am still forced to actually
load the child entities for this to
work - doing something as simple as
Direction d = f.Directions[0];
will cause the delete to actually
delete the child entities. I'm well
aware that this is due to lazy
loading. I thought the point of the
FK relationship and ON CASCADE DELETE
was that you wouldn't have to actually
load the entities to delete them?
Again I seem to be missing something simple.
#Eranga is right that this is done by ON DELETE CASCADE setting on relation in the database BUT you are using code first approach and EF creates database for you so the problem here is that your model is not correctly defined because EF didn't create cascading rule for you.
Why? Because of this:
public class Direction
{
public int DirectionId { get; set; }
public string Abbreviation { get; set; }
public virtual Forecast Forecast { get; set; }
public virtual List<Transport> Transports { get; set; }
}
Abbreviation is FK property and it is nullable! So EF looks at your model and it sees that you defined Direction entity which can have Abbreviation set to null and because of that it can exists orphaned. Change it to:
public class Direction
{
public int DirectionId { get; set; }
[Required]
public string Abbreviation { get; set; }
public virtual Forecast Forecast { get; set; }
public virtual List<Transport> Transports { get; set; }
}
and removing Forecast will delete all related Direction instances and Transport instances. Stop is different story because it is parent entity to Forecast so it will never be removed with Forecast.
Edit:
One more point - you don't want to add ON DELETE CASCADE to your relations manually because EF have to know about enabled cascade deletes. EF use this information in case where you have related entities loaded.
If you place the rule manually into the database you must use fluent mapping and tell EF about this rule as well. Once you force cascade delete in fluent api you don't need to make it manually in the database - it will be created automatically during database recreation.
You can easily achieve this by setting ON DELETE CASCADE when you create foreign keys in the Database.
please have a look at the following POCOs:
public class Country
{
[Key]
public Guid ID { get; set; }
[Required]
public virtual Currency Currency { get; set; }
}
public class Currency1
{
[Key]
public Guid ID { get; set; }
public virtual ICollection<Country> Countries { get; set; }
}
public class Currency2
{
[Key]
public Guid ID { get; set; }
}
I am not exactly sure what I need navigation properties like the ICollection in Currency1 for. If it comes to EF CodeFirst I see no difference in the database structure created. The tables of Currency1 and Currency2 look pretty much the same to me. So why or when does it make sense to add this extra property?
Of course, just thinking of the POCOs I understand that I can't access any countries from a Currency2 object. For example:
var a = currency1.Countries; // works fine
var b = currency2.Countries; // does not even compile
But is this the only difference? In other words: If I do not need to access countries from a Currency2 object, there is no need to add a corresponding navigation property in the Currency2 class for the purposes of EF? Kind of confused here...
Navigation properties are used either for direct access (as you described) or in linq-to-entities queries. If you don't plan to use it you can remove it from your model. Just be aware that you need a navigation property on at least one side to be able to model database realation using the code first approach.
I have taken a model first approach for a project i'm working on. An example of a class relationship is shown as follows, pretty strightforward:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
List<Photo> Photos { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Path { get; set; }
}
The database schema will roughly be:
--------------
Products Table
--------------
Id int,
Name Varchar
------------
Photos Table
------------
Id int,
Path varchar
ProductId int FK Products.ID
A Product can have Zero or more Photos.
Now when i try to plug is my ORM of choice (Entity Framework V4 - Poco approach) iam forced to map my relationships in the domain model!
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
List<Photo> Photos { get; set; }
}
public class Photo
{
public int Id { get; set; }
public string Path { get; set; }
public int ProductId {get; set; } //Foriegn Key
public Product Proudct {get; set; } //For uni-directional navigation
}
Firstly, i dont need/want uni-directional navigation. I understand this can be deleted. Secondly, I dont want the Foriegn Key declared in the Photos class.
I dont think this is true POCO/persistence ignorance if i must define database properties in the Domain Objects?
Do other ORM's behave this way?
I found the answer. Using the wizard, there is an option to "Include foreign key columns in the model" - Uncheck this box and you will a clean conceptual model without FK.
Make sure Code Generation Strategy is set to none in the properties window.
Why don't you want to have Photo.Product property? If there is no such property, it seems one photo can belong to several products and since database schema should be more complex (with auxiliary table).
The relationships don't have to be two-way, and don't have to be public (if you use true POCOs, not proxy types). You've said quite a bit about what you don't want in your code, but can you be clearer about how you do want to define the relationships? It has to go somewhere. Where would you like to put it? There are many options.