Entity Framework Code First: Custom Mapping - c#

public class User
{
public int Id {get;set;}
public string Name {get;set}
public ICollection<User> Followers {get;set;}
public ICollection<User> Following {get;set;}
}
My Model looks like above, Entity framework automatically creates A table and UserUser with rows User_ID and User_ID1 in DB to map this model. I want to map that table and rows myself.
How can i do that, Thanx!!

From Scott Gu's blog about Many-valued Associations:
Many-to-Many Associations
The association between Category and Item is a many-to-many
association, as can be seen in the above class diagram. a many-to-many
association mapping hides the intermediate association table from the
application, so you don’t end up with an unwanted entity in your
domain model. That said, In a real system, you may not have a
many-to-many association since my experience is that there is almost
always other information that must be attached to each link between
associated instances (such as the date and time when an item was added
to a category) and that the best way to represent this information is
via an intermediate association class (In EF, you can map the
association class as an entity and map two one-to-many associations
for either side.).
In a many-to-many relationship, the join table (or link table, as some
developers call it) has two columns: the foreign keys of the Category
and Item tables. The primary key is a composite of both columns. In EF
Code First, many-to-many associations mappings can be customized with
a fluent API code like this:
class ItemConfiguration : EntityTypeConfiguration<Item> {
internal ItemConfiguration()
{
this.HasMany(i => i.Categories)
.WithMany(c => c.Items)
.Map(mc =>
{
mc.MapLeftKey("ItemId");
mc.MapRightKey("CategoryId");
mc.ToTable("ItemCategory");
});
} }
Register this configuration in your DbContext's (you using the DbContext api right?) like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ItemConfiguration());
}
Good luck, hope this help!

To map an entity to itself, you would do something like this
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Followers)
.WithMany().ForeignKey(u => u.FollowerId);
base.OnModelCreating(modelBuilder);
}
its hard to tell without seeing your database model though, and how you actually relate the followers to the user.

Related

Entity Framework one to zero one association reference key mapping [duplicate]

I am trying to establish a One-to-Zero-or-One relationship between two entities and I want the dependent entity to still contain its own Indentity column, instead of it being a shared key.
I want to do as much as possible following the conventions and not declaring explicitly anything that does not require explicit declaration (so, no unnecessary data annotations or fluent api clauses)
The entites:
public class File
{
public int FileId {get;set;}
//some omitted file properties
public virtual Task Task {get;set;}
}
public class Task
{
public int TaskId {get;set;}
//some omitted task properties
public int FileId {get;set;}
public virtual File File {get;set;}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<File>().HasOptional(f => f.Task).WithRequired(t => t.File);
base.OnModelCreating(modelBuilder);
}
This creates a weird relation where the TaskId is both PK and FK column of the Tasks table. Which, I think means that it should have the same value as the file ID? (That is a question:) )
So, how do I make TaskId hold its own, sequential value and have FileId become a foreign key to the Files table?
Or maybe in case of 1-0..1 I should rather get rid of the TaskId property and make FileId the PK/FK property?
Cheers!
Bidirectional one-to-one relationship with explicit FK property is not supported.
So either continue using what you have now - Shared Primary Key association. Just get rid of one of the TaskId or FileId properties from Task and make the remaining a PK (EF will automatically use it as FK because that's the default EF one-to-one relationship model).
Or get rid of the FieldId property from Task and use the following fluent configuration (all is necessary):
modelBuilder.Entity<File>()
.HasOptional(f => f.Task)
.WithRequired(t => t.File)
.Map(m => m.MapKey("FileId"))
.WillCascadeOnDelete();
But I would recommend using the first approach (if there is no special reason of not doing it like existing database etc.) because it's better supported - the second includes some LEFT OUTER JOINs in SQL queries as you can see from this post EF - WithOptional - Left Outer Join?.

EF code first - configure One-to-Zero-or-One relationship without shared PK/FK

I am trying to establish a One-to-Zero-or-One relationship between two entities and I want the dependent entity to still contain its own Indentity column, instead of it being a shared key.
I want to do as much as possible following the conventions and not declaring explicitly anything that does not require explicit declaration (so, no unnecessary data annotations or fluent api clauses)
The entites:
public class File
{
public int FileId {get;set;}
//some omitted file properties
public virtual Task Task {get;set;}
}
public class Task
{
public int TaskId {get;set;}
//some omitted task properties
public int FileId {get;set;}
public virtual File File {get;set;}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<File>().HasOptional(f => f.Task).WithRequired(t => t.File);
base.OnModelCreating(modelBuilder);
}
This creates a weird relation where the TaskId is both PK and FK column of the Tasks table. Which, I think means that it should have the same value as the file ID? (That is a question:) )
So, how do I make TaskId hold its own, sequential value and have FileId become a foreign key to the Files table?
Or maybe in case of 1-0..1 I should rather get rid of the TaskId property and make FileId the PK/FK property?
Cheers!
Bidirectional one-to-one relationship with explicit FK property is not supported.
So either continue using what you have now - Shared Primary Key association. Just get rid of one of the TaskId or FileId properties from Task and make the remaining a PK (EF will automatically use it as FK because that's the default EF one-to-one relationship model).
Or get rid of the FieldId property from Task and use the following fluent configuration (all is necessary):
modelBuilder.Entity<File>()
.HasOptional(f => f.Task)
.WithRequired(t => t.File)
.Map(m => m.MapKey("FileId"))
.WillCascadeOnDelete();
But I would recommend using the first approach (if there is no special reason of not doing it like existing database etc.) because it's better supported - the second includes some LEFT OUTER JOINs in SQL queries as you can see from this post EF - WithOptional - Left Outer Join?.

Cascading delete with Entity Framework

I have a comments table which contains amongst other things the columns Id - the ID of the comment, Commenter_Id the ID of the user who posted it, and ParentComment_Id a self referencing foreign key. There are only two levels to the commenting, parents and sub comments. If a comment record has a ParentComment_Id of null, then it is a parent comment.
I'm trying to write an expression to delete all the comments for a user, but this is causing problems because of that self reference I mentioned earlier.
Take this records sample as an example of the problem:
User with ID 2 posted a comment, with an ID of 3. Because this was the parent comment it has a ParentComment_Id value of null. Later on, User with ID 1 responds to that comment with a sub-comment, creating comment 7 (there were other comments/subcomments between these two hence the Id increment jump).
I'm not able to delete Comment ID 3 because the sub comment, Comment ID 7, has a foreign key to it.
Currently my Entity Framework statement for trying to delete comments is as follows:
context.Comments.Where(x => x.Commenter.Id == user.Id).Delete();
But this gives me an exception because of the described problem.
I could probably fix this using a few foreach loops, but I was hoping there is an easier way like context.Cascade().Where(.... For those wondering the Delete() method is part of the EntityFramework.Extended package.
If you have an entity like this:
public class Comment
{
public int Id{get;set;}
public int? ParentCommentId{get;set;}
public virtual Comment ParentComment{get;set;}
public virtual ICollection<Comment> Comments{get;set;}
}
You could try with this configuration:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Comment>()
.HasOptional(c=>c.ParentComment)
.WithMany(c=>c.Comments)
.HasForeignKey(c => c.ParentCommentId)
.WillCascadeOnDelete(true);
}
You can configure cascade delete on a relationship by using the WillCascadeOnDelete method.If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null.
You can try adding this to your dbcontext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Comments>()
.HasForeignKey(d => d.ParentCommentId)
.WillCascadeOnDelete(true);
}

asp.net Identity classes from Entity Framework

Am I able to use Entity Framework models (Classes) as a classes for asp.net Identity
so the relations that I have in my database will be loaded when I retrieve the user and if I update any columns or add tables I only have to deal with Entity Framework model.
I did do my custom classes for users
public class MyUser : IdentityUser<long, MyLogin, MyUserRole, MyClaim>{ ... }
and connected it with the table
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyUser>().ToTable("Users");
.....
}
but I have more tables that are connected with 'Users' table that isn't with Identity model
for example:
I have a table called Person(Not really just an example)
and each person may have many users and each user may have many
persons
So We have another table called 'PersonsUsers'
So the user have a list
I don't want to call the database twice to retrieve a single user data, and mapping the table from code will make my code static and depends on me updating the source code.
so Is it possible to use the classes that EF generated for the tables?
Do you have any other solution?
Yes, this is possible. Your MyUser class could have a property with the list of Person. then in your modelBuilder, you set up the mappings. Something like this:
modelBuilder.Entity<MyUser>().HasMany(m => m.Person)
.WithMany(p => p.MyUser)
.Map(m => {
m.ToTable("PersonUsers");
m.MapLeftKey("MyUserID");
m.MapRightKey("PersonID");
I believe this will accomplish what you're looking for.

EFCodeFirst : The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects

I am trying to find out what is causing this error, I have listed some of the relevant areas of my code that should hopefully help answer my problem.
The recipe entity's members collection is as shown below:
public virtual IList<Member> Members { get; set; }
and here is the Recipes collection on the member entity:
public virtual IList<Recipe> Recipes { get; set; }
I do the below when creating my DbContext in order to make a many-to-many relationship in a separate table
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// have to specify these mappings using the EF Fluent API otherwise I end up with
// the foreign key fields being placed inside the Recipe and Member tables, which wouldn't
// give a many-to-many relationship
modelBuilder.Entity<Recipe>()
.HasMany(r => r.Members)
.WithMany(m => m.Recipes)
.Map(x => {
x.ToTable("Cookbooks"); // using a mapping table for a many-to-many relationship
x.MapLeftKey("RecipeId");
x.MapRightKey("MemberId");
});
modelBuilder.Entity<Recipe>()
.HasRequired(x => x.Author)
.WithMany()
.WillCascadeOnDelete(false);
}
I also seed my database when the model changes and all I have had to do is add to a recipe's member collection and it seems to be able to sort the rest out for me and place the relevant keys in my cookbook relationship table.
This is some of the code in my recipe controller action that performs the work:
var memberEntity = memberRepository.Find((int)memberId);
var recipeEntity = recipeRepository.Find(recipeId);
recipeEntity.Members.Add(memberEntity);
recipeRepository.InsertOrUpdate(recipeEntity);
recipeRepository.Save();
Here is the insert or update method on my Recipe repository
public void InsertOrUpdate(Recipe recipe)
{
if (recipe.Id == default(int))
{
// New entity
context.Recipes.Add(recipe);
} else
{
// Existing entity
context.Entry(recipe).State = EntityState.Modified;
}
}
I get an error of "InvalidOperationException : The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects." on this line:
context.Entry(recipe).State = EntityState.Modified;
Does anyone know why that would happen? Do I have to add the member to the recipe and vice versa to get this to work? I'm not sure what the problem is because the recipeEntity seems to be the correct one.
Any help would be appreciated, thanks.
EDIT
The context is being created in each repository (RecipeRepository & MemberRepository) as shown, so I presume this is the problem in that a different context is being used for each .Find() request? and that causes problems?
private EatRateShareDbContext context = new EatRateShareDbContext();
I'm not sure this is the solution but it seems like you're using different contexts in your repository.
First make sure your have the same context for each lifetime. lifetime could be different based on your project type. (e.g. for web projects, usually it is the same for each HttpContext). You can use IoC to manage your context lifetime. Good IoC libraries for .Net are autofac and Castle Windsor
Also, I think your call to InsertOrUpdate method is unnecessary (unless you're calling Find method with no tracking.) just remove the line and see if it works:
var recipeEntity = recipeRepository.Find(recipeId);
recipeEntity.Members.Add(memberEntity);
recipeRepository.Save();
One easy way to share your DbContext per HttpRequest is mentioned here.
If you are using AutoFac, you must add SingleInstance() to your register code.
Example:
builder.Register(a => new EntityContainer()).As().SingleInstance()
Where EntityContainer is your DbContext

Categories

Resources