Entity Framework one to many : SqlException - entity-framework

I have a little problem when I try to save an item in my DB using EntityFramework.
My classes looks like:
public partial class Site
{
public int Id { get; set; }
public string Name { get; set; }
public string LongName { get; set; }
public string Adress { get; set; }
public City City { get; set; }
public Country Country { get; set; }
public string VATNumber { get; set; }
}
public class Country
{
public int CountryId { get; set; }
public string Name { get; set; }
public string IsoCode { get; set; }
}
And when I try to create a new site in my controller it works, but when I try to add a link to an existing Country :
if (SiteProvider.GetSiteByName(Site.Name) == null)
{
Site.Country = CountryProvider.GetCountryById(1);//it's working, i see the link to the right country
SiteProvider.Create(Site);
}
public static void Create(Site Site)
{
using (MyDBContext Context = new MyDBContext())
{
Context.Site.Add(Site);
Context.SaveChanges(); //here is the problem
}
}
I got this error:
SqlException: Cannot insert explicit value for identity column in
table 'Country' when IDENTITY_INSERT is set to OFF
Thanks in advance for your help.

Add CountryId property to Site class and when adding a new Site set CountryId instead of Country property
public int CountryId { get; set; }
[ForeignKey("CountryId")]
public Country Country{ get; set; }

You have a slight issue with your use of contexts here. You have used one DBContext instance to load the country (where this country object will be tracked) and then a second DBContext to save the site (where the first country object is a property).
It is preferable to perform all your operations for a single unit of work by using one DB context (that would be shared between your classes) and the responsibility for disposing of it to be handled outside your repository layer.

Related

How can I add two property in my model in EF code first from one model?

I want to add two properties from the city model:
after migration this error shows up:
Unable to determine the relationship represented by navigation
'City.Orders' of type 'ICollection'. Either manually configure
the relationship, or ignore this property using the '[NotMapped]'
attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
here is my code :
public class Order
{
public virtual City FromCity { get; set; }
public virtual City ToCity { get; set; }
}
public class City
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
I suppose your model is more complicated than just FromCity and ToCity because I don't think it's a good idea to store such information in a different table. Yet, You can use inheritance in this case.
The table-per-hierarchy (TPH) pattern is used by default to map the inheritance in EF. TPH stores the data for all types in the hierarchy in a single table.
However, for your scenario, you can have a base class that holds all related attributes.
public class CityBase
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
Then suppose you need two entities as per your scenario:
public class FromCity : CityBase
{
public virtual ICollection<Order> Orders { get; set; } = null!;
}
public class ToCity : CityBase
{
public virtual ICollection<Order> Orders { get; set; } = null!;
}
And the order entity:
public class Order
{
public int Id { get; set; }
public string OrderTitle { get; set; } = string.Empty;
public virtual FromCity FromCity { get; set; } = null!;
public virtual ToCity ToCity { get; set; } = null!;
}
This approach can solve your problem with a One-to-Many relationship between Orders and FromCity, ToCity as per below diagram:

Entity Framework fires query to load related object although explicit loading for those objects is already done

I have these models and context in my application :
public class Department
{
public int Id { get; set; }
public string Name { get; set;}
public virtual ICollection<Student> Students { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Department Department { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Department> Departments { get; set; }
}
Below is my code in Program.cs class :
class Program
{
static void Main(string[] args)
{
using (var context = new TestContext())
{
var students = context.Students.SqlQuery("Select * from dbo.Students").ToList();
context.Departments.Load();
Console.WriteLine(students[0].Department.Name);
Console.ReadLine();
}
}
}
Although related object - Department is loaded in the context by the line - context.Departments.Load(), still when the department name is printed in console entity framework fires a query in the database to fetch the related object. Shouldnt this query for related object fetching not be fired since the objects are already loaded in the context. ?
If i change the code to below -
var students = context.Students.ToList();
context.Departments.Load();
Console.WriteLine(students[0].Department.Name);
Then when u access student[0].Department.Name , Ef doestnot fire a sql query to load department property.
Apparently Change Tracker relationship fix-up doesn't work with the combination of Independent Associations and raw SQL queries.
To fix just add Foreign Key property to Student. eg
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
}

If Exists Dont Add Data Entity Framework Many-To-Many

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.

How can I update the base type's data to a derived class while using EF code first table per type

//here is my data models:
public class People
{
public int PeopleID { get; set; }
[Required]
public string Name { get; set; }
}
[Table("User")]
public class User : People
{
[Required]
public string LoginName { get; set; }
[Required]
public string PassWord { get; set; }
}
//and i stored an item into the People table
context.People.Add(new People { Name = "Jack" });
context.SaveChanges();
//and now "Jack" is promoted as an user of this system
//how can I update his data and let him has is LoginName and Password?
This is a common design mistake.
Objects can't change their type; you shouldn't be using inheritance in this case.
Instead, the User can have a reference to a Person (another hint: your entity names should be singular, regardless of what you name the table)
First of all you need to declare discriminator column. For example PeopleType which couln't discriminate users from all peolple by specific value.
So in your scenatio you will need to create Jack as User but you could assecc it as item of people
[Table("People")]
public class People
{
public int PeopleID { get; set; }
[Required]
public string Name { get; set; }
public int Type { get; set; }
}
public class User : People
{
[Required]
public string LoginName { get; set; }
[Required]
public string PassWord { get; set; }
}
Overide it in DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<People>().Map<User>(m => { m.Requires("PoepleType").HasValue(1 /* any value you like */); })
}

Entity Framework 4 CTP 5 POCO - Many-to-many configuration, insertion, and update?

I really need someone to help me to fully understand how to do many-to-many relationship with Entity Framework 4 CTP 5, POCO. I need to understand 3 concepts:
How to config my model to indicates
some tables are many-to-many.
How to properly do insert.
How to properly do update.
Here are my current models:
public class MusicSheet
{
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string Key { get; set; }
public virtual ICollection<Author> Authors { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Author
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public string Bio { get; set; }
public virtual ICollection<MusicSheet> MusicSheets { get; set; }
}
public class Tag
{
[Key]
public int ID { get; set; }
public string TagName { get; set; }
public virtual ICollection<MusicSheet> MusicSheets { get; set; }
}
As you can see, the MusicSheet can have many Authors or Tags, and an Author or Tag can have multiple MusicSheets.
Again, my questions are:
What to do on the
EntityTypeConfiguration to set the
relationship between them as well as
mapping to an table/object that
associates with the many-to-many
relationship.
How to insert a new music sheets
(where it might have multiple
authors or multiple tags).
How to update a music sheet. For
example, I might set TagA,
TagB to MusicSheet1, but later I need to change the tags to TagA
and TagC. It seems like I need
to first check to see if the tags
already exists, if not, insert the
new tag and then associate it with
the music sheet (so that I doesn't
re-insert TagA?). Or this is
something already handled by the
framework?
Thank you very much. I really hope to fully understand it rather than just doing it without fully understand what's going on. Especially on #3.
In the EF4 CTP5 the relationship is done by default convention when you put public virtual ICollection in each of the classes of the many to many relationship, as you already have done, your context class should look like this:
public class YourContextName : DbContext
{
public DbSet<MusicSheet> MusicSheets { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Author> Authors { get; set; }
}
Very simple you just create a instance of the MusicSheet class and then add all the instances of you authors and tags to each of the collections of Authors and Tags in your MusicSheet, and then add your instance of MusicSheet to your context collection of MusicSheets and then call SaveChanges:
MusicSheet musicSheet = new MusicSheet
{
Title = "Music Sheet 1",
Key = "Key",
Authors = new List<Author>
{
new Author
{
Name = "Author 1",
Bio = "Author 1 biographic text..."
},
new Author
{
Name = "Author 2",
Bio = "Author 2 biographic text..."
}
},
Tags = new List<Tag>
{
new Tag {TagName = "TagA"},
new Tag {TagName = "TagC"}
}
};
var context = new YourContextName();
context.MusicSheets.Add(musicSheet);
context.SaveChanges();
To update you have to load your MusicSheet and remove the tags you don't want and then add the ones you need to add, this is how:
var context = new YourContextName();
var myMusicSheet = context.MusicSheets.First();
//The Tag you wnat to remove.
var tagToRemove = myMusicSheet.Tags.First();
var tagToAdd = new Tag {TagName = "TagX"};
myMusicSheet.Tags.Remove(tagToRemove);
myMusicSheet.Tags.Add(tagToAdd);
context.Entry(myMusicSheet).State = EntityState.Modified;
context.SaveChanges();
You can also find any author and/or tag that you know that exist and added to your MusicSheet and vice versa, but this is the foundation.
Remember this is for the EF4 CTP5 Code first...
Excuse me my English is not my main language, I hope this can help you, best regards from Dominican Republic.
PS: Don't forget to add references to EntityFramework and System.Data.Entity, is your responsibility to do anything else like unit test, validation, exception handling...etc
EDIT:
First you need to add a constructor to your models:
public class Tag
{
[Key]
public int ID { get; set; }
public string TagName { get; set; }
public Tag()
{
MusicSheets = new List<MusicSheet>();
}
public virtual ICollection<MusicSheet> MusicSheets { get; set; }
}
...Then you can do something like this:
var context = new YourContextName();
var newMusicSheet = new MusicSheet();
newMusicSheet.Title = "Newly added Music Sheet";
//Your existing Tag.
var existingTag = contex.Tags.Find(3);
existingTag.MusicSheets.Add(existingTag);
context.Entry(existingTag).State = EntityState.Modified;
context.SaveChanges();
You can do the same for all your models.
I hope this can help you!
You do not really need an EntityTypeConfiguration to set the relationship between them. It should work as it is right now. With CTP5 all you have to do to establish a many-to-many relationship is to include ICollection in both entities.
Now about how to perform inserts and deletes, there are two ways I know of. The one I usually use is create an entity for the resultant table of the many-to-many relationship, then create an instance of this entity and feed it with all the data that is required, including instances of the other entities (the ones that have the many-to-many relationship). And finally I simply add it to the repository and commit the transaction (usually using a UnitOfWork class).
Quick example:
public class Item
{
public int ID { get; set; }
public string Title { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
}
public class User
{
public int ID { get; set; }
public string Username{ get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
}
public class Bid
{
public int ID { get; set; }
public float Amount { get; set; }
public DateTime Date { get; set; }
public virtual Item Item { get; set; }
public virtual User User { get; set; }
}
Then I would simply create instances of the Bid entity.
public void PlaceBid(User user, Item item, int amount)
{
if (ValidateBid(amount, user, item))
{
Bid bid = new Bid
{
Amount = amount,
Date = DateTime.Now,
User = user,
Item = item
};
try
{
repository.Add(bid);
unitOfWork.Commit();
}
catch (Exception ex)
{
//TODO: Log the exception
throw;
}
}
}