Entity Framework 4: Many to Many relationship IQueryable instead of ICollection - c#

Good morning everyone,
I am trying to tackle a problem I run into with EF code first. My schema is the following
public class Article : IUrlNode
{
[Key]
public Guid ArticleID { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateUpdated { get; set; }
public string Summary { get; set; }
[System.ComponentModel.DataAnnotations.InverseProperty("CategoryArticles")]
public virtual IQueryable<Category> ArticleCategories { get; set; }
public string FriendlyUrl
{
get;
set;
}
}
[RouteChild("CategoryArticles")]
public class Category : ContentNode
{
public Guid ServiceId { get; set; }
[System.ComponentModel.DataAnnotations.InverseProperty("ArticleCategories")]
public virtual IQueryable<Article> CategoryArticles { get; set; }
}
I have written code with which I am able to retrieve a category from the database without actually knowing that its a category. From then on I must retrieve a single article of that category again without knowing that its an article. For categories I am relying on the ContentNode base class and for Articles on the IUrlNode interface.
Category retrieval works fine and with a single query but after I actually get the category I have to use reflection to get the navigation property pointed by the RouteChild attribute to find that single article that matches my criteria. Problem is that the navigation property type is ICollection which means that it will at best use lazy loading and will bring all the articles from the database and will find the one I am looking for in memory.
My problem is also described in this previous post (not by me):
Entity Framework Code First IQueryable
Is there a way to have that navigation property as IQueryable or some other design that can go around this limitation?

No there is no way to have navigation property as IQueryable but you can change the collection to IQueryable by using:
IQueryable<Article> query = context.Entry(category).Collection(c => c.articles).Query();
query.Where(...).Load();
Generally your "algorithm" looks pretty strange. You want to work with base class but in the same time you want to access child properties. That sounds wrong and it can most probably be solved in better way (non "generic" way is also better).

Related

ASP.NET Core 2: What could be the code behind a "many-to-one" relationship in this case?

I'm preparing a project's data structure (code-first) in an ASP .NET Core 2 application, with the help of Entity Framework. This specific relationship I have no experience with: the user has to be able to choose diseases with checkboxes, and we have similar choices: cancer type, dietary, etc..
I have more than two tables like the ones on the picture, which will be referred from the UserKitProperties table. This table should work like a connector table, connects the user entity with other entities.
userid1 | cancertypeid1
userid2 | dietaryid1
userid1 | cancertypeid2
userid3 | dietaryid1
How should this be specified in the code, to support this relationship? I was thinking on doing a base class and maybe refer to that id. And this is the connector class..
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
// this should be used for cancer type, dietary, etc..
public long PropertyID { get; set; }
/* Instead of using two classes' ids, maybe call the base class' id
[ForeignKey("PropertyID")]
public CancerType CancerTypes { get; set; }
[ForeignKey("PropertyID")]
public Dietary Dietaries { get; set; } */
}
Thank you in advance for your suggestions! :)
The following should work:
public class Property
{
public long PropertyId { get; set; }
}
public class CancerType : Property
{
// Your code
}
public class Dietary : Property
{
// Your code
}
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
public long PropertyID { get; set; }
[ForeignKey("PropertyID")]
public Property Property { get; set; }
}
But as this MS doc mentions, setting up such inheritence will use a special Discriminator
column in the base class table, to represent what specific type is stored in a row.
I personally would resort to having nullable fields instead in order to not add more complexity. This doesn't enforce, however, that PatientProperties only has one property, which is a considerable minus:
public class PatientProperties : EntityModel
{
[Key]
public long ID { get; set; }
public long PatientID { get; set; }
[ForeignKey("PatientID")]
public Patient Patients { get; set; }
public long? CancerTypeID { get; set; }
[ForeignKey("CancerTypeID")]
public CancerType CancerType { get; set; }
public long? DietaryID { get; set; }
[ForeignKey("DietaryID")]
public Dietary Dietary { get; set; }
}
Instead of thinking about the database layout first, you should think about how you would represent this relationship in code. After all, you are doing a code-first approach.
There are basically two choices you could choose: Either the patient has multiple properties, one for each property type, or there is just a single collection for all properties:
public class Patient
{
// …
// option 1
public CancerType CancerType { get; set; }
public Dietary Dietary { get; set; }
public OtherProperty OtherProperty { get; set; }
// option 2
public IList<PatientProperty> Properties { get; set; }
}
Both of these options have their advantages and disadvantages. While option 1 is very explicit and enforces a single value for every type, it also requires you to have a (class) property for every (patient) property. So if you extend your model later, you will have to adjust your patient model.
Option 2 has the benefit that it can just collect everything. So you can just add properties to your patient without having to modify the model later if you introduce new properties. In addition, it would also directly support multiple selections for a single kind. On the downside, it does not verify anything on its own, so you need business logic to actually enforce your rules.
Moving onto the database, for option 2 you obviously need a link table since that is a many-to-many relationship now. Since you only have a link to the base type PatientProperty but you actually want to talk about the concrete type, you will need some kind of discriminator. Discriminators are basically just a notation to additionally store the kind of object in the database.
When storing data with inheritance, what is commonly done is “table-per-hierarchy”. That means that all types within the hierarchy of the PatientProperty base type will share the same table. A discriminator column is used to specify the type, and additional properties that some property types may have are implemented with nullable columns. This setup works out of the box with Entity Framework and is described in this chapter in the documentation.
The other approach, “table-per-type” is not supported in EF Core, so if you wanted to follow that, you would have to implement it yourself. But in your case, where the property types are mostly very similar, I would actually argue against that and actually keep them in the same table.
For option 1, as long as you only have a single property of each kind assigned to the patient, things are a bit simpler. Since you don’t have many-to-many there, you don’t actually need a link table. You just need to store the id for each linked property type in the patient model, as shown in the above UML. Doing that, you can also keep the property types as separate types that do not share a single table in the database.

Sorting by a property of NHibernate component without explicitly specifying it?

I've got two entities:
class Complex
{
public virtual MyComponent Component { get; set; }
public virtual string OtherProperty { get; set; }
}
class MyComponent
{
public virtual int Number { get; set; }
public virtual string Description { get; set; }
}
Component property of Complex is mapped in NHibernate as a component. I would like NHibernate to always use MyComponent's Number property when sorting by MyComponent. So whenever I write:
session.Query<Complex>().OrderBy(x => x.Component)
I would like the result to be sorted only by Number and not Description - right now NHibernate sorts by both columns. It would be great if the solution worked both in Linq and HQL.
I looked at LinqToHqlGeneratorsRegistry. I could use it to generate proper HQL for that, but I would need to register a generator for every property of type MyComponent (so if I have more entities like Complex I would need to register it many times), and I want this kind of sorting to be used always when sorting by property of this type.
Is it possible in NHibernate?

Entity Framework 5 and Code first - unable to add record to a table that has relation with other table(s)

I am working on an ASP.NET MVC4 application using EF 5 and Code First workflow. I have two entities:
public class Document
{
//other properties...
public int DocumentID { get; set; }
public virtual IList<UploadedFile> UploadedFiles { get; set; }
}
and:
public class UploadedFile
{
//other properties..
public int UploadedFileID { get; set; }
public int DocumentID { get; set; }
public virtual Document Document { get; set; }
}
In short - I have different kind of documents and each document may have 1 or more files associated with him. What I haven't thought about is that I'm going to have files that are not associated with a document. So I still need to save the file specific information and the UploadedFile entity has all the properties I need for that but I don't want to set values for the Document properties (leave them null). Which leads to the problem.
I already have some business logic written and if I change to public int? DocumentID { get; set; } - make the FK nullable I get compile errors from the code that I already have. Also, I might to decide and rewrite my code to reflect that change but I'm not sure if setting the FK to be nullable is best solution anyways.
The second solution that I can think of is to just create another entity OtherFiles (or something like this, just example name) where I will keep the records for the files that are not associated with some kind of document. Which will solve my problem in general but it seems like the worst solution since I'm gonna repeat all the properties of UploadedFile but without the relation to another table.
And third - if anyways I have to rewrite my code in order to keep some decent design (which I am trying to do) maybe there's a better way than setting the FK to null. I've watched a part of video tutorial by Scott Allen where he was setting two entities with the same structure, there it was Restaurant and RestaurantsReviews and in this video he didn't bother at all adding public virtual Restaurant Restaurant { get; set;} and had only public int RestaurantID { get; set; } saying that this is not mandatory but might be useful in some cases.
I don't know which cases are those, but maybe for my case it would be best just to remove DocumentID and virtual Document Document and rewrite my code? If this is the case what I lose as ability when I remove DocumentID from my UploadedFile entity?
Having the Id column and the virtual object to define the FK, helps when using lazy loading, and in many other ways. So if you have something like this:
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<RestaurantReviews> Reviews{ get; set; }
}
public class RestaurantReviews
{
public int Id{ get; set;}
public string Review { get; set; }
public int RestaurantId { get; set; }
public virtual Restaurant Restaurant { get; set; }
}
And for some reason you're working with the any Review and need the Restaurant's name, you just say:
var restaurantNameToShow = myReview.Restaurant.Name;
So EF is going to go again to the DB and get you the name (because he's lazy and didn't bring it in the first place). This answer your doubt about EF, regarding what option you should take, it's seems to me that is a very very complicated thing, and maybe a complicated solution ain't bad at all. For all the thing we spoke in the comments I'd choose the second option. If for some reason I get another idea on how solve it, I'm gonna tell it to you ;)

Filling Foreign Key Object in Entity Framework 4

I am using EntityFramework for the first time and maybe this question is so simple...I've used code first method..I have a Class Personnel which looks like this:
public class Personnel
{
public string Id { set; get; }
public int Code { set; get; }
public string Name { set; get; }
public int Type { set; get; }
public JobTitle Title { set; get; }
}
and the JobTitle class:
public class JobTitle
{
public string Id { set; get; }
public int Number { set; get; }
public string Title { set; get; }
public List<Personnel> Personnels { set; get; }
}
which the last property in Personnel Class is a foreign key in personnel table of course..my problem is when I want to retrieve all personnels ( or a personnel ) from DB using lambda expression..the foreign key object is null..the lambda expression is like below:
Context.ContextInstance.Personnels.ToList();
and if I change the expression to this the foreign key object is not null any more.
Context.ContextInstance.Personnels.Include("Title").ToList();
is it the right way??..is there any better way??..I supposed that EF will automatically understand that!!!!..if there are more than 1 FK then I have to use Include for all of them?? please help me to understand.
Thanks
This is due to lazy loading. When you call Context.ContextInstance.Personnels.ToList(); this will fetch all personnel's but Title will not fetch until it get instanced, so make it virtual to get it.
or, you can disable lazy loading by
public MyEntitiesContext() : base("name=MyEntitiesContext", "MyEntitiesContext") {
this.Configuration.LazyLoadingEnabled = false;
}
Doing this will get all related data from context. Using "include" is loading on demand, when you specify properties you want to query.
Virtual keyword allows entity framework runtime create dynamic proxies for your entity classes and their properties, and by that support lazy loading. Without virtual, lazy loading will not be supported, and you get null on collection properties.
If your JobTitle property would be defined as virtual, you wouldn't need to use include.
It's really good explained here: Entity Framework 4.1 Virtual Properties

Many-to-many relationships using EF Code First

I have two classes defined as such:
public class Questionnaire
{
public int QuestionnaireID { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Vendor> Vendors { get; set; }
}
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
public virtual ICollection<Questionnaire> OpenQuestionnaires { get; set; }
public virtual ICollection<Questionnaire> SubmittedQuestionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
I beleive this is the correct way to establish a many-to-many relationship between these classes, and when the project is built, I would expect three tables to be created.
However, when I attempt to to relate one Questionnaire to two different Vendors, I receive the following error when attempting to save the changes (context.SaveChanges()):
*Multiplicity constraint violated. The role 'Vendor_OpenQuestionnaires_Source' of the relationship 'QuestionnaireApp.Models.Vendor_OpenQuestionnaires' has multiplicity 1 or 0..1.*
If I assign a Questionnaire to only one Vendor, save the changes and then assign it to another and again save changes I no longer get the error; however the Questionaire is then related only to the last Vendor to which it was assigned, indicating that (at best) there is a one-to-many relationship being created.
I'm hoping that there is something wrong with the way I'm declaring the many-to-many relationship between these classes, or perhaps there is something I need to add to the context class to "encourage" the relationsip, but perhaps many-to-many relationships like this are not supported, or cannot be created using "Code First"?
Thank you for your time,
Jason
If you don't have any Fluent API code your expected mapping relies on EF Code First conventions. The convention which you expect to kick in here is the AssociationInverseDiscoveryConvention. Now if you look in Intellisense (and probably also documentation) it says about this convention:
Convention to detect navigation properties to be inverses of each
other when only one pair of navigation properties exists between the
related types.
Now, that's the problem: You don't have only "one pair" of navigation properties between Questionnaire and Vendor. You have two collections in Vendor refering to Questionnaire and one collection in Questionnaire refering to Vendor. The result is that this convention doesn't get applied and EF maps actually three one-to-many relationships with only one end exposed as navigation property in the model.
Moreover the mapping you want to achieve is not possible with your model: You cannot map the one end Questionnaire.Vendors to the two ends Vendor.OpenQuestionnaires and Vendor.SubmittedQuestionnaires.
One workaround is to change your model the following way:
public class Vendor
{
public int VendorID { get; set; }
public string VendorName { get; set; }
[NotMapped]
public IEnumerable<Questionnaire> OpenQuestionnaires
{
get { return Questionnaires.Where(q => q.IsActive); }
}
[NotMapped]
public IEnumerable<Questionnaire> SubmittedQuestionnaires
{
get { return Questionnaires.Where(q => !q.IsActive); }
}
public virtual ICollection<Questionnaire> Questionnaires { get; set; }
public virtual ICollection<QuestionnaireUser> QuestionnaireUsers { get; set; }
}
Now Vendor.Questionnaires is mapped to Questionnaire.Vendors (AssociationInverseDiscoveryConvention should detect this) and the helper properties OpenQuestionnaires and SubmittedQuestionnaires allow you to pull out the selected items. (I'm not sure if IsActive is your distinguishing flag. Otherwise you have to introduce some new flag.)
The [NotMapped] attribute is just here to make it explicite. It is probably not necessary because EF won't map IEnumerable collections and readonly properties with only a getter anyway.
Go figure, after an hour or so of searching, I go and find the exact answer 30 seconds after I post my question.
The solution was to add the following to the context class:
modelBuilder.Entity<Vendor>()
.HasMany<Questionnaire>(x => x.OpenQuestionnaires)
.WithMany(x => x.Vendors)
.Map(x =>
{
x.MapLeftKey("vID");
x.MapRightKey("qID");
x.ToTable("VendorQuestionnaires");
});
I found the answer by reading this Stack Overflow post: EF Code First Many-to-Many not working

Categories

Resources