Changing relationships between tables to simplify data access in MVC3 - asp.net-mvc

Asp.net MVC3 app with Entity framework. Lets say I have 3 tables; Article, Category and Author.
I create relations between
Category.CategoryId -> Article.CategoryId and Author.AuthorId -> Article.AuthorId
Using code first navigation properties
public virtual Category Category { get; set; }
public virtual Author Author { get; set; }
That means that when I view a list of the articles I have to :
return View(db.Article
.Include(c=>c.Category)
.Include(a=>a.Author)
.ToList());
In order to have access to the names of categories and authors and not just their id’s
How much would it hurt to break this classic schema and not create relationships between these tables? Then I could just return SelectLists from Author and Category Tables in a ViewModel and populate the Category and Author fields in my Article table directly with the corresponding names not the id’s and also preserve data integrity.
My query would be simplified to just:
return View(db.Article.ToList());
I suppose I will have to create indexes for those fields to speed up searches.
Is this being done somewhere or is it completely wrong?
Does it have better or worse performance?

#Panos, your original approach is correct, deleting foreign keys would be a mistake. With the includes you avoid the lazy loading in this scenario and you have a good performance.

public virtual Category Category { get; set; }
public virtual Author Author { get; set; }
you defined category and Author as virtual and it means these object wont load without an Include command in your query. you man use a select list in your grid without removing these relations, because these relations doesn't have any real load without Include command in your query.
but be aware of using .ToList() this will load all records of your query and later it may become a large amount of data.

Related

Entity Framework - how to implement Tags (for Articles / Pages)

I'm writing a EF based website which will have many Articles (with id, author, date, title etc attributes) each of which is a member of a Category (10 possible categories each with id, title, associated icon etc)
Each article can also have several "tags". Like the question-tags that apply to SO questions.
For example Article ID 1 has:
Title: "10 most popular baby names of 2016", Author: "Jo Bloggs", Date: 2017-02-01, etc
CategoryID: 2 (foreign key to Category table where ID 2 is the "Year In Review" category)
(This is the part I'm unclear about) Tags: "Lists", "Top 10s", "Parenting", "2016", "Trends", "Babies", "Names"
Article ID 2 ("50 life hacks I discovered this year")
has its own Title, Author etc, also a FK to Category ID 2
(This is the part I'm unclear about) Tags: "2016", "Lists", "Life Hacks", "Self improvement"
From the above there are 9 'distinct' tags of which 2016 and Lists apply to both articles and the others appear once each.
With a large number of articles there are an increasingly large number of tags.
Is this just a case of a many-to-many relationship of Article to Tag or is there some more natural/idiomatic way to represent that using EF?
The main things I want to do are:
On an article page, show the tags that apply to that article (like the Stack Overflow tags that apply to a question when you view it).
Show a 'tag cloud' or list of tags with the number of articles that have been tagged as that. e.g. Lists (2) , Trends (1)
Potentially when creating an Article, be able to create a new Tag to go with it if the tag doesn't already exist (although if this is too difficult I can create the tag upfront instead).
I don't need to:
query the articles to find articles where tag = THIS and not THAT or any similar logic
preserve the ordering of the tags (I will sort them in the view/controller logic so it doesn't matter in what order they are stored)
What I need to know:
How should I model the tags in the database (should there be a Tag table and a ArticleTag table (ArticleID, TagID) or something else?)
What would I need to model using EF to represent the article--tag relationship efficiently?
My background is database so I understand about FKs and link tables etc, but am struggling on the EF side.
Note about my requirements: This is for a website I am creating for myself, so I already know the requirements. I won't have a situation where "the user" asks to be able to keep the ordering of the tags, for example.
You can add 2 Tables, Tag and TagRelation, Tag is responsible for saving TagNames such as #Test #AnotherTest, make sure there is no way to insert same tagName.
In TagRelation you should keep the TagId (Fk of Tag table) and Guid (Fk of Article, Page and you name it).
In this architecture every table needing Tag, must have a field with Guid type(uniqueidentifier in SQL data type) when you searching for ex. article you simply set inner join with TagRelation on Guid fields
public class Tag
{
public long Id { get; set; }
public string TagName { get; set; }
}
public class TagRelation
{
public long Id { get; set; }
public long TagId { get; set; }
public Guid FkUniqueGuid { get; set; }
}
public class Article
{
public long Id { get; set; }
public Guid UniqueGuid { get; set; }
//And another fields
}

Eager Load from Entity Framework SP

Im trying to populate my domain models and child entities with 1 SQL Store Proceedure execution. Perhaps this is answered here. Im pretty certain it's not possible but I though I would throw the question out there to find possible work arounds.
I have quite complex domain models and im looking for a more efficient way of loading my data rather than query a customer and then lazy load its children. I have presented a simple example of what im trying to achive below;
public class Customer{
public int Id { get; set; }
public virtual Address Address { get; set; }
}
public class Address{
public int Id { get; set; }
}
var customer = this.Database.SqlQuery< Customer >("exec SP_Name")
I know in EF5 you can return multiple data contexts but im hopeful I can resolve muliple child entities.
I hope ive made sense. Im lacking alot of sleep so apologies if it doesn't. Following a sport in a timezone 10 hours behind makes it difficult! :(
Stored procedures in EF don't offer eager loading. They can only load single level of entities. You can either use stored procedure with multiple result sets as mentioned in linked article but that works only with EDMX and you must execute mapped function import instead of SqlQuery. You can also simply use eager loading with LINQ query instead of stored procedure to avoid lazy loading:
var customers = context.Set<Customer>()
.Include(c => c.Address)
.FirstOrDefault(c => c.Name == someName);

How to delete all many-to-many relations without loading related entities in Entity Framework?

I have db scheme where Product table have many to many relation to Color table. I'm using EF and create POCO objects:
public class Product
{
public Guid Id {get;set;}
public ICollection<Color> Colors {get;set;}
}
public class Color
{
public Guid Id {get;set;}
public ICollection<Product> Products {get;set;}
}
In many situations it is necessary to delete all colors related to product and set new colors. So i want to delete all many to many relations whitout exactly knowing id of related colors. Is it possible to delete them without additional queries to db? I know i can just write stored procedure which will delete all relation with colors for specified product, but it will be better to find general approach through entity framework.
If you don't know keys of colors you cannot delete them without loading them first - EF deletes records one by one so it needs to know which record to delete.
The straight forward option is executing SQL DELETE directly:
dbContext.Database
.ExecuteSqlCommand("DELETE FROM dbo.ProductColors WHERE ProductId = #Id", product.Id);

Fluent Nhibernate HasMany to One Table from Several Tables

I have an entity/class/table which is referenced from several other entities, and I use Fluent NHibernate to handle the ORM for me. In a few instances, it's a simple reference where I can store the foreign key ID as a column and handle the reference in that simple way, but in a few other instances I need to reference a list of these items, and it needs done for at least three classes I can think of. You can assume this setup will be copied to handle the other classes' relationships.
Here's how the common entity looks (the one that is owned by several other entities in HasManys):
public class Student {
public virtual int Id {get; set;}
public virtual string Name {get; set;}
}
And, here's what the ShopCourse entity looks like:
public class ShopCourse {
public virtual int Id {get; set;}
public virtual int Name {get; set;}
public virtual IList<Student> Students {get; set;}
}
Imagine that a couple other classes I have, such as specific courses, can "own" several students. In order to maintain that relationship I must create a table in my database that tracks the foreign keys between the two (for each entity that references Student) - no entity needed for this intermediate table, and Fluent won't need to think of it unless I hand it the string name of the table itself:
Table: ShopCourseStudents
int - ShopCourseId
int - StudentId
Lastly, here are my mappings. You can assume that the entities themselves map out fine - things such as the naming scheme for the Id are resolved and working correctly. The issue lies when I attempt to initialize any entity that has a HasMany of Student:
//Inside a FluentlyConfigure().Mappings(m => m.AutoMappings.Add() call:
.Override<ShopCourse>(map => {
map.HasMany(x => x.Students)
.Table("ShopCourseStudents")
.KeyColumns.Add("ShopCourseId")
.KeyColumns.Add("StudentId")
.Cascade.All();
})
The issue is that when I attempt to load a list of ShopCourses I get the Fluent error:
Foreign key (ABC123AF9:Student [ShopCourseId, StudentId]) must have
same number of columns as the referenced primary key (ShopCourses
[Id])
I do not override Fluent's mapping of Student as it's straightforward. For the purpose of this example, Student doesn't need to know which ShopCourses it belongs to, or any of the other courses that may own that particular Student record.
This seems like I'm doing something basic, wrong - what is it, exactly? Much obliged in advance!
So, the issue was with the custom code that I re-use with my projects, apparently the piece written to handle the ManyToMany convention is mostly broken. What I was looking for here was a ManyToMany relationship, not HasMany. The issue I had was that my code was forcing a reference on the child object (in this example, Student) to the parent, which I do not need and only complicates things. Removing that, and my ManyToMany then works:
.Override<ShopCourse>(map => {
map.HasManyToMany(x => x.Students)
.Table("ShopCourseStudents")
.ParentKeyColumn("ShopCourseId")
.ChildKeyColumn("StudentId")
.Cascade.All()

Image database organization

I am curious as to how I should organize image storage in my application. They will be stored on disk somewhere, with the path / url stored in the database, but my application has become complex and image organization is leaving me baffled. The technology I am using is ASP.NET MVC 3 + EF 4.1 Code First (which is proving to be difficult with complex relationships).
This is a family based application, so there is a Family table, Member table, and a FamilyMember table to link to two. There is also a FamilyEvent, and MemberEvent table to track events in the family or member's life.
Use cases
A Member can upload multiple profile pictures (possibly to be put in a "profile" album?)
A Member can upload "family" photos, to be attached to the Family object, and put into some type of album for the family.
A Member can add a member event, and attach multiple images to the members event.
A Member can add a family event, and attach multiple images to the family's event.
These are the base use cases.
My thoughts
Base "Picture" table with id, description, and maybe the user id of the uploader.
MemberProfilePicture table with id, pictureId, memberId, etc...
MemberEventPicture table with id, pictureId, [member id?], membereventid, etc...
FamilyEventPicture table with id, pictureId, [familyId ?], familyeventid, etc...
This would allow a family or member to essentially "link" a Picture later to another profile pic / member event / or family event.
My question
Is this correct, or am I over-complicating this? I don't see any other way other than multiple tables to represent images for different things, which all refer to the base Picture table. Can anyone give any suggestions as to how I can improve this model? While I understand there may be many definitions to correct, I'm looking for solutions as far as how to organize the imaging aspect of my application, or improvements on what I have already.
Your using EF Code First so let's talk code not tables. You can realize your use cases by having only one Picture class and associate many Pictures with Member, Family and so on.
Taking this code
public class Family
{
public int Id { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
public virtual ICollection<Member> Members { get; set; }
}
public class Member
{
public int Id { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
}
public class Picture
{
public int Id { get; set; }
public String Location { get; set; }
}
Will generate 3 Tables, Families, Members, Pictures with the Pictures table having two foreign keys Family_Id and Member_Id. This model allows to associated a single picture with Member, Family or both.
From a scalability point of view I would avoid to store the image binary in the database and using an ORM to access the binary. Better ideas are to store the picture in the file system or a "specialized" server like MongoDBs GridFS.
What I would do is have three tables.
The 'picture' table which stores a unique id for each picture and the location.
The 'profile' table which has your dtails about the profile.
A third 'linking' table composed of pictureId and profileId
If I want to link picture A to profile X I just add in "A,X" to the linking table. Same thing for events or whatever. If there's going to be a many-to-many relationship this is the cleanest way to go (I'm assuming that the same picture can be in multiple profiles).
Hope I understood your problem correctly.

Resources