I have these two Models the logic is here One Post can have multiple Categories.
public class Post
{
public Post()
{
this.Categories = new HashSet<Category>();
}
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ShortDescription { get; set; }
public string PostImage { get; set; }
public string Thumbnail { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? PublishedDate { get; set; }
public string CreatedBy { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
I have three static categories.
When I am trying to add new post its multiplexing CategoryTable creating new categories with same name ,And Mapping Them in to CategoryPostsTable.
The problem is here i want to map that data with existing categories. I dont want to add new category with same name.
I am using Repository Pattern how should i control that ? Is EF has some solution for that ?
I assume you have code like:
var post = new Post();
post.Categories.Add(cat);
context.Posts.Add(post);
...where cat is a Category object representing an existing category.
The latter Add method (DbSet.Add) doesn't only mark the received entity as Added but all entities in its object graph that are not attached to the context. So cat will also be marked as Added if it wasn't attached yet.
What you can do is
context.Entry(cat).State = System.Data.Entity.EntityState.Unchanged;
Now EF will only create the association to the category, but not insert a new category.
Related
Let's say I have a bike shops that sell various types of bikes: pro, kids, youth, leisure and any mixture. So I have a table of shops that refers/relates to a table of possible types. Now these shops also host events with the same types: events for pros, kids etc again any mixture. And so I have another table of events that also need to refer/relate to the same table of types:
I need to be able in a single quick query get a list of all bike types for a shop or event.
So I figured I'd have 3 main tables: Shops, Events, BikeTypes and two intermediate to link shops and events to bike types:
And I organized my models as:
public class BikeShop
{
public long Id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
}
public class BikeEvent
{
public long Id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime date { get; set; }
public string location { get; set; }
}
public class BikeType
{
public long Id { get; set; }
public string name { get; set; }
public string code { get; set; }
}
public class ShopBikeTypes
{
public long Id { get; set; }
public BikeShop shop { get; set; }
public BikeType biketype { get; set; }
}
public class EventBikeTypes
{
public long Id { get; set; }
public BikeEvent bikeevent { get; set; }
public BikeType biketype { get; set; }
}
With DataCotext:
public class DataContext : DbContext
{
public DbSet<BikeShop> Shops { get; set; }
public DbSet<BikeEvent> Events { get; set; }
public DbSet<BikeType> BikeTypes { get; set; }
public DbSet<ShopBikeTypes> ShopBikeTypes { get; set; }
public DbSet<EventBikeTypes> EventBikeTypes { get; set; }
}
Migration creates correct database structure just as my diagram. Great!
Now how do I make a straight forward query:
get list of all bike types for a shop
get list of all bike types for an event
Is my structure even correct?
Do I need some List<> in the main object models BikeShop and BikeEvent?
EF's include and theninclude seem to require some list?
This feels like such a typical scenario. What's the right way of doing this?
Thank you.
Those are the linq queries that you are asked but when i look at that your class models, i can say they are wrong. U need to define first which relation theyre having. if all of that relation has based on one-to-one, u wont gonna need any List<> in your class models. but if u have one-to-many relation,u gonna need them.
1- get list of all bike types for a shop
return DbContext.Shops
.Include(x>=x.ShopBikeTypes)
.ThenInclude(x=>x.BikeTypes).ToList();
2- get list of all bike types for an event
return DbContext.Events
.Include(x=>x.EventBikeTypes)
.ThenInclude(x=>x.BikeTypes).ToList();
3- Get all data in that relation
return DbContext.BikeTypes
.Include(x>=x.EventBikeTypes)
.ThenInclude(x=>x.Events).AsSplitQuery()
.Include(x=>x.ShopBikeTypes)
.ThenInclude(x>=x.Shops).AsSplitQuery()
.ToList();
it can be a tough query, do not try to use AsNoTracking() because it can cause Cartesian Explosion.
#BerkGarip: thank you for your help. I ended up with this models structure:
public class AShop
{
public long Id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string phone { get; set; }
public List<AShopType> aTypes { get; set; }
}
public class AEvent
{
public long Id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime date { get; set; }
public string location { get; set; }
public List<AEventType> aTypes { get; set; }
}
public class AType
{
public long Id { get; set; }
public string name { get; set; }
public string code { get; set; }
}
public class AShopType
{
public long Id { get; set; }
public AType aType { get; set; }
}
public class AEventType
{
public long Id { get; set; }
public AType aType { get; set; }
}
In order to achieve what I needed using answer from #BerkGarip I figured out that the trick there was to have lists in the 'shop' and 'event' models to the intermediate objects which in turn have a single reference to 'type'. This way database layout is the same and it is many-to-many relationship and I can use 'include' and 'thenInclude' exactly as expected:
return await _context.AShops.Where(x => x.name == "Z")
.Include(x => x.aTypes)
.ThenInclude(y => y.aType)
.ToListAsync();
LazyLoading is disabled on my project. I want to get Product which is Id = 1 with Category navigation property of it. But I need just Id and Name properties of Category. That's why I want Category navigation property to has only these two fields.Is it possible to create such a query ?
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public dobule Price{ get; set; }
public string Description { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public int CategoryId{ get; set; }
public Category Category{ get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public dobule Description{ get; set; }
public Category IsDeleted { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
}
If you only want a few specific fields you will need to select them explicitly. Something like this would work:
dbContext.Products
.Select(p => new Product
{
Id = p.Id,
Name = p.Name,
// etc... The fields you need from product go here
Category = new Category
{
Id = p.Category.Id,
Name = p.Category.Name
}
}
It might be better to have a Product and Category model class that only has the two fields. Now your method would return a Category object that lacks values for most fields which the caller might not expect. Depends on what exactly you're doing.
Depends if you know what do you want before calling the DB.
If you know what do you know, then you can use some 'Include' logic or the awnser from #Sangman or check docu here.
If you already have the entity in the memory and then you decide to load additional navigation property.
context.Entry(yourEntity).Reference(a => a.Category).Load();
More examples here.
I have 2 tables in SQL server without a FK :
So each Image has many Comments.
Using this command I've created scaffolded files :
dotnet ef dbcontext scaffold
"Server=sff-pc;Database=IMG;Trusted_Connection=True;"
Microsoft.EntityFrameworkCore.SqlServer -o Models -t Images -t
comments --context-dir Context -c MyContext -f
Here are the generated files :
public partial class Images
{
public int ImageId { get; set; }
public Guid ImgGuid { get; set; }
public string ImgExtension { get; set; }
public DateTime Datecreated { get; set; }
public string Origin { get; set; }
public decimal? Size { get; set; }
public string Ip { get; set; }
public int UserId { get; set; }
public string Description { get; set; }
public bool? IsActive { get; set; }
public int? VotesUp { get; set; }
public int? VotesDown { get; set; }
public int ImgType { get; set; }
}
public partial class Comments
{
public int CommentId { get; set; }
public int ImageId { get; set; }
public int UserId1 { get; set; }
public string CommentTxt { get; set; }
public DateTime DateCreated { get; set; }
public bool? IsActive { get; set; }
}
Question:
Using those generated classes and this full context file, How can I get each Image and Include its comments?
What have I tried :
I've tried adding the ForeignKey attribute :
public partial class Comments
{
public int CommentId { get; set; }
[ForeignKey("Images")] <--------------Here
public int ImageId { get; set; }
public int UserId1 { get; set; }
public string CommentTxt { get; set; }
public DateTime DateCreated { get; set; }
public bool? IsActive { get; set; }
}
And to run this :
var context = new MyContext();
var result = context.Images.Include("Comments"); //exception
foreach (var a in result)
{
Console.WriteLine(a);
}
Exception :
The property 'Comments' is not a navigation property of entity type 'Images'
When you use [ForeignKey("Images")] over your fk "Images" is expected to be a navigation property inside your Comments class not the name of your DbSet property in your DbContext. You can read more here and relationships here. In the case of your code based on the definition of your Comments class, you will need to have a property Images.
[ForeignKey("Images")]
public int ImageId { get; set; }
public Images Images { get; set; } // ForeignKey attribute points to this
While the above shows how you can use [ForeignKey("Images")], I would recommend making the property's name Image instead of Images. Not only will it be a better explanatory name, but it would also make the [ForeignKey("Image")] unnecessary. EF would automatically map Image to Imageid by naming convention.
Based on the way you are using to access comments using context.Images.Include("Comments"), you seem to be trying to access the list of comments under an image. However, if you notice your result variable would be a IQueryable of Images. To achieve this, you will need to add a navigation property into your Images class.
public IEnumerable<Comments> Comments { get; set; }
Since you have already mapped your foreign key, your mapping is implicit between ImageId and Comments. You can, however, use InverseProperty attribute to be safe (check the relationships link for more info). Once this is done you can use the following code:
var result = context.Images.Include(i => i.Comments);
simply I ask this How to Map , How to ProductCustomer in the sample ??
public class ProductCustomer
{
public virtual Product Product { get; set; }
public virtual Customer Customer { get; set; }
}
and about Product and Customer :
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public decimal Amount { get; set; }
}
thanks!
You don't need to create the ProductCustomer object.
In EF, you create your Customer and Product, and then you create collections to each. This will automatically create the proper link tables.
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
public virtual List<Product> Products {get;set;}
}
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public decimal Amount { get; set; }
public virtual List<Customer> Customers {get;set;}
}
This is only the case, however, if your link table has no payload (has no additional data). If it does, then you will need to create the link table as an entity similar to what you originally did, but you add 1:many links in your product and customer classes to the link entity. You then have to modify your queries to query through the link table.
I would like to create a table for images within my database that can be used by several different models. I'm using entity framework and have defined my images class as follows:
public class Images
{
public int ID { get; set; }
public String Name {get; set;}
public byte[] ImageData { get; set; }
public string Type { get; set; }
}
I would then like to link to this from any of my other models that need to hold an image. E.g
public class Project
{
public int ID { get; set; }
public String Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public virtual ICollection<tImages> Images { get; set; }
}
and also
public class User
{
public int ID { get; set; }
public String UserName { get; set; }
public virtual ICollection<tImages> Images { get; set; }
}
The trouble is that entity framework is adding a foreign key for both project and user to the image table. I kind of expected this I guess.
What I want is to define an image key that can be used to index into my image table. I'm just not sure how to define the classes to get this affect. E.g how do I define my entity classes to acheive the result below:
Project Table
//ID Name StartDate EndDate Image
//1 Project1 05/02/2013 06/04/2013 PROJ01IMG
User Table
//ID UserName Image
//1 Bob USER01IMG
Image Table
//ID Name Imagekey ImageData Type
//1 img1 PROJ01IMG 0xFFF00F1 .. etc JPEG
//2 teamphoto PROJ01IMG 0xFEB0011 ..etc JPEG
//3 outline PROJ01IMG 0xFFF0AA3 ..etc PNG
//4 bob USER01IMG 0xFFF01233 ..etc JPEG
Of course it may be that having the foreign keys is the correct approach and I shouldn't worry about it? Its just that I have a lot of models and they all need to store images so I'd rather just have them in one table rather than multiple image table as it will make searching/ galleries etc of all images easier down the line I think.
I get the feeling that this a complete noob question and I'm missing something obvious so apologies if it is.
If you don't want EF to map a FK, and instead you will manually populate a list based on the image key, define your entities like this. But I think you are better off with what you had, using FK's.
public class Project
{
public int ID { get; set; }
public String Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string ImageKey { get; set; }
[NotMapped]
public List<Image> Images {get; set;}
}
public class User
{
public int ID { get; set; }
public String UserName { get; set; }
public string ImageKey { get; set; }
[NotMapped]
public List<Image> Images {get; set;}
}
I suppose you might try defining your classes like this to see if it's what you want:
public class Images
{
public int ID { get; set; }
public String Name {get; set;}
public byte[] ImageData { get; set; }
public string Type { get; set; }
public virtual User User { get; set; }
public virtual Project Project { get; set; }
}
public class Project
{
public int ID { get; set; }
public String Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
public class User
{
public int ID { get; set; }
public String UserName { get; set; }
}
That should map a FK to Images in Project as well as in User, which is what I think you're really looking for.