Saving 10000's of row in bridge table - c#

I have five tables Agent, AgentAddress , Address , ContactInfo and AgentContactInfo.
Here AgentAddress and AgentContactInfo are bridge table.
I have to Import 10000's of row from excel and save.
What is the best approach to save the file
Now I am using following code but getting exception for large data:
var transaction await_context.Database.BeginTransactionAsync();
try
{
foreach (var item in getExcelData)
{
var countryId = await _context.Country.Where(x => x.CountryName == "Nepal").Select(y => y.CountryId).SingleOrDefaultAsync();
Entities.Agent.Agent newAgents = new()
{
CountryId = countryId,
AgentOfficeName = item.Name,
Branch = item.Branch,
ContactPerson = item.ContactPerson,
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
};
await _context.Agent.AddAsync(newAgents);
await _context.SaveChangesAsync();
var addressTypeListItemId = _context.ListItem.FirstOrDefault(x => x.ListItemSystemName == "Permanent").ListItemId;
Address newAddress = new Address
{
AddressTypeListItemId = addressTypeListItemId,
City = item.City,
State = item.State,
District = item.District,
StreetName = item.Address,
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
};
await _context.Address.AddAsync(newAddress);
await _context.SaveChangesAsync();
AgentAddress agentAddress = new AgentAddress
{
AgentId = newAgents.AgentId,
AddressId = newAddress.AddressId,
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
};
await _context.AgentAddress.AddAsync(agentAddress);
await _context.SaveChangesAsync();
var contactTypePhoneId = await _context.ListItem.Where(x => x.ListItemSystemName == "PhoneNumber").Select(y => y.ListItemId).SingleOrDefaultAsync();
ContactInfo phoneNumbers = new ContactInfo
{
ContactNumber = item.Telephone,
ContactTypeListItemId = contactTypePhoneId,
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
};
await _context.ContactInfo.AddAsync(phoneNumbers);
await _context.SaveChangesAsync();
await _context.AgentContactInfo.AddAsync(new AgentContactInfo
{
AgentId = newAgents.AgentId,
ContactInfoId = phoneNumbers.ContactInfoId,
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
});
await _context.SaveChangesAsync();
}
await transaction.CommitAsync();
}
My Entities:
public class Agent : BaseEntity
{
[Key]
public int AgentId { get; set; }
[Required]
public string AgentOfficeName { get; set; }
public DateTimeOffset? OpenedDate { get; set; }
...
...
[Required]
[ForeignKey(nameof(Country))]
public int CountryId { get; set; }
public Country Country { get; set; }
[ForeignKey(nameof(ListItem))]
public int? AgentTypeListItemId { get; set; }
public ListItem ListItem { get; set; }
}
[Index(nameof(AgentId), nameof(AddressId), IsUnique = true)]
public class AgentAddress : BaseEntity
{
[Key]
public int AgentAddressId { get; set; }
[Required]
[ForeignKey(nameof(Agent))]
public int AgentId { get; set; }
public Agent Agent { get; set; }
[Required]
[ForeignKey(nameof(Address))]
public int AddressId { get; set; }
public Entities.Address.Address Address { get; set; }
}
[Index(nameof(AgentId), nameof(ContactInfoId), IsUnique = true)]
public class AgentContactInfo : BaseEntity
{
[Key]
public int AgentContactInfoId { get; set; }
[Required]
[ForeignKey(nameof(Agent))]
public int AgentId { get; set; }
public Agent Agent { get; set; }
[Required]
[ForeignKey(nameof(ContactInfo))]
public int ContactInfoId { get; set; }
public ContactInfo ContactInfo { get; set; }
}

Short Version
Remove all attempts to save, commit or manually control transactions and just call SaveChangesAsync at the end.
Don't try to retrieve primary keys one by one. EF Core will take care of creating the relations and fixing up foreign keys. Just create valid objects and let EF Core figure out the insertion order.
Don't load lookup data inside the loop either. That just wastes time loading the exact same data multiple times.
In the common Blog and Posts example used by most tutorials, you only need to create a Blog with some Posts, and EF Core will take care of insertions and primary keys:
using(var ctx=new SomeContext())
{
var blog=new Blog { Url = "http://blogs.msdn.com/adonet" }
blog.Posts.Add(new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
blog.Posts.Add(new Post { Title = "Second Post", Content = "..." });
blog.Blogs.Add(blog);
await ctx.SaveChangesAsync();
}
Explanation
Normally, an ORM shouldn't be used for ETL jobs like data loading. There are no entities in a data loading job, or rather the entities are Row, Column and Transformation, not Address and Name.
10K rows is very little data though and EF Core can easily handle this if used properly. A DbContext is a Unit-of-Work that tracks all changes. Those changes are committed when SaveChanges is called only once at the end of the work. There's no reason to use an explicit transaction because SaveChanges uses a transaction internally.
Furthermore EF Core already batches all changes but can be configured to use a specific batch size.
This means that all you need to do is remove code. Simply creating the DbContext, adding the classes and calling SaveChangesAsync at the end is enough to insert data in batches, in a single transaction:
using(var context = new MyContext(...))
{
var countryId = await context.Country
.Where(x => x.CountryName == "Nepal")
.Select(y => y.CountryId)
.SingleOrDefaultAsync();
foreach (var item in getExcelData)
{
var newAgents = new Agent()
{
AgentOfficeName = item.Name,
...
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
};
var newAddress = new Address
{
AddressTypeListItemId = addressTypeListItemId,
City = item.City,
...
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
};
var agentAddress = new AgentAddress
{
Address = newAddress
InsertPersonId = personId,
InsertDate = DateTimeOffset.UtcNow
};
agent.Addresses.Add(agentAddress);
context.Agents.Add(newAgent);
}
await context.SaveChangesAsync();
}
The call context.Agents.Add(newAgent); will add newAgent and all related objects to the DbContext in the Added state. When SaveChanges is called, EF Core will insert them in the proper order. First all child entities in batches, retrieving their PKs. Then it will fix up the parent entities and insert all of them in batches.
EF Core won't just INSERT one row after the other either, it will use an INSERT ... OUTPUT inserted.ID with multiple value rows to insert multiple objects and return their IDs in the same query.

Related

Problem when Updating value (PUT) in ASP.NET core ef

I'm currently learning ASP.NET Core and having some problem. Basically, I have a model like this:
public class WorkRoom
{
[Key]
public int WorkRoomId { get; set; }
[ForeignKey("Room")]
[Required]
public int RoomId { get; set; }
public Room Room { get; set; }
[ForeignKey("Employee")]
[Required]
public string Id { get; set; }
public virtual ApplicationUser Employee { get; set; }
}
and a Dto for this model cause i only want to pass two value is RoomId and Id(using identityUser).
WorkRoomDto.cs
public class WorkRoomDto
{
public int RoomId { get; set; }
public string Id { get; set; }
}
I can write POST, GET and DELETE method normally as i wish, but I have some problems writing PUT.
public async Task<Response> PutWorkRooms(int id, WorkRoomDto workRoom)
{
var respone = new Response();
var workRoomId = _context.WorkRooms.Any(i => i.WorkRoomId == id);
var userId = await userManager.FindByIdAsync(workRoom.Id);
if (workRoomId)
{
if (userId == null)
{
respone.Status = false;
respone.Message = "User not exist";
return respone;
}
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
_context.Entry(newWorkRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
respone.Status = true;
respone.Message = "Successfully updated.";
respone.Data = workRoom;
}
else
{
respone.Status = false;
respone.Message = "WorkRoomId no exist.";
}
return respone;
}
But it showed Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s).
Before the autoMappter I had tried
_context.Entry(workRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
But it says the Dto is not in the DbContext. How can I fix this?
From your code, WorkRoomId is 0 in newWorkRoom. You need set the value for it. Just change like below:
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
newWorkRoom.WorkRoomId = id; //add this line...
_context.Entry(newWorkRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
Automapper give you a brand new object that created locally (not taking back from the database), therefore, not tracked by the dbContext.
So, when you try to update newWorkRoom by forcing the state to modified, the dbContext know it want to see an update operation, but unfortunately, nothing got tracked, then execute nothing.
Try this
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
if (_context.Entry(newWorkRoom).State == EntityState.Detached)
{
var entityFromDb = await _context.WorkRooms.FindAsync(id);
_context.Entry(entityFromDb).CurrentValues.SetValues(newWorkRoom);
}

.NetCore Querying - Joining Many-to-Many Tables And Returning Result and Including a Key With Array of Strings

Please, I’m having some challenges working with many to many relationship when working with .netCore .
I have this case; a user can belong to many departments and a department can have many users. Initially, I had some challenges implementing the many to many relationships. But after much research, I was able to get a solution. Now, joining the tables to get all users that has Administration as part of their department. I'm a bit confused on how to get the users department(s) into its own array when querying
Here is my classes so far
// User Class
public class User
{
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<UserDepartment> UserDepartments { get; set; }
}
// Department Class
public class Department
{
public int Id { get; set; }
public string DepartmentName { get; set; }
public virtual ICollection<UserDepartment> UserDepartments { get; set; }
}
// UserDepartment Class (Intermediary Table)
public class UserDepartment
{
public long UserId { get; set; }
public int DepartmentId { get; set; }
public User User { get; set; }
public Department Department { get; set; }
}
This is my desire output;
[
{
"userId": 1,
"fName": "John",
"lName": "Doe",
"depts": ["Administration"]
},
{
"userId": 2,
"fName": "Simon",
"lName": "Simpson",
"depts": ["Administration", "Support"]
},
]
And here is my own try of the query
public async Task<IEnumerable<object>> Test()
{
var result = await _context.Users
.Select(p => new
{
UserId = p.Id,
FName = p.FirstName,
LName = p.LastName,
Depts = //TODO - To fix getting user depts p.UserDepartments.Any(d => d.Department.DepartmentName == "Administration")
}).ToListAsync();
return result;
}
Now, joining the tables to get all users that has Administration as
part of their department.
Try with:
public async Task<IEnumerable<object>> Test()
{
var result = await _context.Users
.Select(user => new
{
User = user,
Departments = user.UserDepartments.Select(userDepartment => userDepartment.Department.DepartmentName)
})
.Where(tuple=> tuple.Departments.Contains("Administration"))
.Select(tuple=> new
{
UserId = tuple.User.Id,
FName = tuple.User.FirstName,
LName = tuple.User.LastName,
Depts = tuple.Departments
}).ToListAsync();
return result;
}
Another approach would be to find the matching department entity and from then on access its associated users through the nav. properties:
public async Task<IEnumerable<object>> Test()
{
var adminDep = await _context.Departments
.Include(dep => dep.UserDepartments)
.ThenInclude(userDep => userDep.User.UserDepartments)
.SingleOrDefaultAsync(dep => dep.DepartmentName == "Administration");
if(adminDep == null) throw new InvalidOperationException();
var result = adminDep.UserDepartments
.Select(userDep => userDep.User)
.Select(user => new
{
UserId = User.Id,
FName = user .FirstName,
LName = user .LastName,
Depts = user.UserDepartments.Select(userDep => userDep.Department.DepartmentName)
}).ToList();
return result;
}
You could use the include method provided in the .net core to fix getting user depts,just like this:
public async Task<IEnumerable<object>> Test()
{
var result = await _context.Users.Include(s => s.UserDepartments).ThenInclude(n => n.Department).Select(p => new
{
UserId = p.Id,
FName = p.FirstName,
LName = p.LastName,
Depts = p.UserDepartments.Select(s=>s.Department).Select(k => k.DepartmentName)
}).ToListAsync();
return result;
}

Joins with Dapper return null

I have the following classes:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public List<Event> Events { get; set; }
}
public class Event
{
public int Id { get; set; }
public int UserId { get; set; }
public string EventText { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public string Day { get; set; }
public string ColorIdentifier { get; set; }
public int Week { get; set; }
}
I'm trying to get all the users and their events with Dapper like this:
var sql = "SELECT u.Id, e.UserId, e.EventText FROM cpc.PLANNING_TOOL_USERS u LEFT JOIN cpc.PLANNING_TOOL_EVENTS e ON u.Id=e.UserId";
var result = SqlMapper.Query<User, Event, User>(connection, sql, (u, e) =>
{
if (u.Events == null)
u.Events = new List<Event>();
u.Events.Add(e);
return u;
}, splitOn: "Id, UserId");
The Id for the user is returned back, but the list of events is not populated. I have looked at many examples here on Stack Overflow regarding this, but I can't see what I'm doing wrong.
To omit the situation that SQL returns no data I have just mocked two user rows with SQL union.
User with Id=1 and one Event, and User with Id=2 and two Events.
SqlMapper.Query returns flat results that are best for 1 to 1 relation. You have one user to many events relation, so some helper storage needed to maintain that relation as a mapping thru the results. I have used .NET dictionary for that.
My code sample below:
// introducing temporary storage
var usersDictionary = new Dictionary<int, User>();
var sql = #"SELECT 1 Id, 1 UserId, 'EventText1' EventText
union SELECT 2 Id, 2 UserId, 'EventText2' EventText
union SELECT 2 Id, 2 UserId, 'Another EventText2' EventText";
var result = SqlMapper.Query<User, Event, User>(connection, sql, (u, e) =>
{
if (!usersDictionary.ContainsKey(u.Id))
usersDictionary.Add(u.Id, u);
var cachedUser = usersDictionary[u.Id];
if (cachedUser.Events == null)
cachedUser.Events = new List<Event>();
cachedUser.Events.Add(e);
return cachedUser;
}, splitOn: "UserId");
// we are not really interested in `result` here
// we are more interested in the `usersDictionary`
var users = usersDictionary.Values.AsList();
Assert.AreEqual(2, users.Count);
Assert.AreEqual(1, users[0].Id);
CollectionAssert.IsNotEmpty(users[0].Events);
Assert.AreEqual(1, users[0].Events.Count);
Assert.AreEqual("EventText1", users[0].Events[0].EventText);
Assert.AreEqual(2, users[1].Events.Count);
I hope that helped you solving your mapping issue and events being null.

Insert new Entity with one to many relationship not creating records in both tables

I'm using MVC5 and EF6 Code First to create a new Company entity that can have many Contact entities. However, only the Company record is being written to the database.
Models:
public class Company
{
public virtual int CompanyId { get; set; }
[Required]
public virtual string Name { get; set; }
[Required]
public virtual Address Address { get; set; }
[Required]
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
// can have many Contacts
public virtual IEnumerable<Contact> Contacts { get; set; }
}
public class Contact
{
public virtual int ContactId { get; set; }
[Required]
public virtual string Title { get; set; }
[Required]
public virtual string Forename { get; set; }
[Required]
public virtual string Surname { get; set; }
[Required]
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
// belongs to one Company
public virtual Company Company { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create_POST([Bind(Include = "CallerType,Name,Address,Phone,Email,Contacts")] CompanyViewModel viewModel)
{
if (ModelState.IsValid)
{
// tried commented code to make sure it wasn't a problem with my repository and contact records aren't saved to database...
//var context = new EfDbContext();
//var company = new Company
//{
// Name = viewModel.Name,
// Phone = viewModel.Phone,
// Address = viewModel.Address,
// Email = viewModel.Email
//};
//context.Companies.Add(company);
//var contact = new Contact
//{
// Company = company,
// Title = "mr",
// Forename= "f",
// Surname = "s",
// Phone = "132"
//};
//var contact2 = new Contact
//{
// Company = company,
// Title = "mr",
// Forename = "for",
// Surname = "sur",
// Phone = "987"
//};
//var contacts = new List<Contact> {contact, contact2};
//company.Contacts = contacts;
//context.SaveChanges();
var contacts = viewModel.Contacts.Select(c => new Contact
{
Title = c.Title,
Forename = c.Forename,
Surname = c.Surname,
Phone = c.Phone,
Email = c.Email
});
var company = new Company
{
Name = viewModel.Name,
Phone = viewModel.Phone,
Address = viewModel.Address,
Email = viewModel.Email,
Contacts = contacts
};
await _companyRepository.CreateAsync(company);
var redirectUrl = new UrlHelper(Request.RequestContext).Action("Create", "Enquiry", new { id = company.CompanyId, callerType = viewModel.CallerType });
return Json(new { Url = redirectUrl });
}
Response.StatusCode = 400;
return PartialView("~/Views/Company/_Create.cshtml", viewModel);
}
Repository:
public async Task<TEntity> CreateAsync(TEntity entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
DbContext.Set<TEntity>().Add(entity);
await DbContext.SaveChangesAsync();
return entity;
}
I was under the impression that the DbContext would 'know' that because it's Contacts collection is populated it would create Contact records that link to the created Company.
Or do I have to create a Contact instead as this is the table with the foreign key in it and EF will 'know' to create the Company first?
Or is it necessary to actually save the Company entity first using SaveChanges() and then assign it to the Contact records and perform a second SaveChanges()?
If this is the case then would I need to use Database.BeginTransaction()?
Any guidance on this would be great as this is my first time using Entity Framework.
Contacts must be an ICollection. EF doesn't support IEnumerable as navigation properties, because it's impossible to add items to them.
This also means that you should change the code that creates contacts:
var contacts = viewModel.Contacts.Select(c => new Contact
{
Title = c.Title,
Forename = c.Forename,
Surname = c.Surname,
Phone = c.Phone,
Email = c.Email
}).ToList(); // <= ToList() added
Your relationships should be set up within your DbContext by overriding the OnModelCreating method
I'm new to Entity Framework myself, but found that the methods names aid in learning how to set up relationships
Hopefully the below can help
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasMany(c => c.Contacts)
.WithRequired(co => co.Company)
.HasForeignKey(co => co.CompanyId);
}
You can also set up cascading within your relationship, so that when one entity is removed, the related entities are also deleted, which may be something to consider if you ever need to delete a Company

Navigation Property null when I do context.Users.Create(). Is that correct?

I'm developing my first class library that uses Entity Framework Code First as Data access layer.
I have this class:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<User> Friends { get; set; }
public virtual ICollection<User> FromWhomIsFriend { get; set; }
}
And now I testing my code with a Console application:
static void Main(string[] args)
{
Database.SetInitializer(
new DropCreateDatabaseAlways<AdnLineContext>());
insertUsersAndFriends();
}
private static void insertUsersAndFriends()
{
using (var context = new AdnLineContext())
{
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
var user2 = context.Users.Create();
user2.Name = "User2";
user2.Age = 26;
user2.City = "City2";
user2.Country = "Country2";
user2.Email = "email_2#email.com";
user2.InterestIn = "User2's interests";
var user3 = context.Users.Create();
user3.Name = "User3";
user3.Age = 27;
user3.City = "City3";
user3.Country = "Country3";
user3.Email = "email_3#email.com";
user3.InterestIn = "User3's interests";
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends.Add(user2);
user3.Friends.Add(user1);
context.SaveChanges();
}
}
I'm testing, so the database is empty.
This is my UserConfiguration class:
public UserConfiguration()
{
Property(d => d.Name).IsRequired();
Property(d => d.Age).IsRequired();
Property(d => d.City).IsRequired();
Property(d => d.Country).IsRequired();
Property(d => d.Email).IsRequired();
Property(d => d.InterestIn).IsRequired();
HasMany(d => d.MessagesSent).WithRequired(l => l.SentByUser).WillCascadeOnDelete(false);
HasMany(d => d.MessagesReceived).WithRequired(l => l.SentToUser).WillCascadeOnDelete(false);
HasMany(d => d.Friends).
WithMany(d => d.FromWhomIsFriend).
Map(c =>
{
c.ToTable("UserFriends");
c.MapLeftKey("UserId");
c.MapRightKey("FriendId");
});
HasMany(d => d.WantsToDo).
WithMany(a => a.Users).
Map(t =>
{
t.ToTable("UserActivities");
t.MapLeftKey("UserId");
t.MapRightKey("ActivityId");
});
}
But I get a null pointer exception here user1.Friends.Add(user2); because Friends is null.
What am I doing wrong? How can I fix this problem?
Entity Framework seems to be smart in this case. You are adding a new User to the context:
var user1 = context.Users.Create();
//...
context.Users.Add(user1);
//...
user1.Friends.Add(user2);
The entity is in Added state after that. Why should EF run a query with lazy loading to initialize the Friends collection? user1 is the principal in the relationships and because the state is Added it cannot exist yet in the database, hence there can't be any dependent in the database refering to it that could be loaded. So, EF does not try to load the collection at all (which is good in order to avoid unnecessary database roundtrips).
You could apply tricks to make it work - by attaching the new users before you add them to the context:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.UserId = 1;
context.Users.Attach(user1);
user2.UserId = 2;
context.Users.Attach(user2);
user3.UserId = 3;
context.Users.Attach(user3);
// ...because we cannot attach three users with the same key
user1.Friends.Add(user2);
user3.Friends.Add(user1);
// Lazy loading will run twice here based on the `UserId` which is 1,2,3
// and returns nothing, but the Friends collection will be initialized
// as empty collection
// This MUST be AFTER accessing the Friends collection
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
// the three "dummy UserIds" are ignored because state is Added now
context.SaveChanges();
Now, just forget this solution again. It's nonsense to force lazy loading (= expensive database query) to create an empty collection. C# has the new operator for this:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.Friends = new List<User>();
user3.Friends.Add(user1);
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
context.SaveChanges();
You can also just use var user1 = new User() in this scenario where you only add new entities to the context. Creating dynamic proxies has no benefit here (unless you would set any foreign key properties to other existing entities and want to access their corresponding navigation properties after calling SaveChanges - which doesn't seem to be the case in your example).
You must declare your "List<>" Properties like this:
public virtual ICollection<User> Friends { get; set; }
If you don't use virtual keyword EF will not initialize collection for you.
In your case, you are creating new object, use private properties to initialize it for new objects:
private ICollection<User> _friends;
public ICollection<User> Friends {
get { return _friends ?? (_friends = new List<User>()); }
set { _friends = value; }
}
You have to initialize the member Friends like this:
using (var context = new AdnLineContext())
{
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.FromWhomIsFriend.Add(user1);
context.SaveChanges();
}
I think that you might want to consider how the database should model the relationship between users. It seems like you want to have a 1:N relationship between users and itself (IE, one user can have multiple other users associated with it). To be honest the only way I know how to achieve this is a lookup table that associates two UserIds together. You could do this in Entity Code First like this:
public class FriendDefinition
{
[ForeignKey("User")]
public int UserId { get; set; }
[ForeignKey("Friend")]
public int FriendUserId { get; set; }
public virtual User User { get; set; }
public virtual User Friend { get; set; }
}
Then you could update your User class like so:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<FriendDefinition> Friends { get; set; }
}
Finally, you would now use this as follows:
var user = db.context.Users.First();
var firstFriend = user.Friends.First().Friend;
It's a little clunky, but I think it would serve your purpose. Code first is an excellent tool but you still have to conceptualize how the data is actually being stored in the database to model what you need.
You should use the Create method of DbSet - it will give you a fully initialised proxy.
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
//....
context.Users.Add(user1);
Hard coding things like user1.Friends = new List<User>(); is hack not a solution.

Categories