Inserting A Record To Lookup Table - c#

I refer to the answer here (https://stackoverflow.com/a/20846170/1753877) about how to insert a record into the Users_Friends lookup table in this type of relationship
The answer is :
var user = db.Users.Find(userID);
var friend = db.Friends.Find(friendID);
user.Friends.Add(friend);
db.SaveChanges();
However, to me it seems like a lot of overhead to have to retrieve two objects from the database just to insert a row comprising of just the two IDs that are already known to the application without the need for any queries.
Could we not just do a basic INSERT using userID and friendID as values (or pass them to a stored procedure to do the same).
As I am new to Entity Framework, I'm unsure if there are any advantages to using the code above that would be missed with a direct SQL INSERT, or if there is a valid reason to avoid using direct SQL with Entity framework for inserts
Thanks.

There are several ways to do this:
first is if User_Friends has its own entity you can just populate a new user_friends with the id's and save changes.
The second is to create a New friend and user and attach them (something similar to this, may take some tweaking):
var friend = new Friend{Id = friendID};
var user = new User{Id = userID};
using (var context = new Context())
{
context.Friends.Attach(friend);
user.Friends.Add(friend);
context.Entry(user).State = EntityState.Modified;
context.SaveChanges();
}
Entity Framework Add and Attach and Entity States
The third is to use Context.Database and either execute sql command or sql query (depending) to execute arbitrary SQL against the database.
What I have found the easiest is to include User_Friends as its own EF entity and create a new one. It makes adding to the union table very easy.

You could do a direct insert like:
db.Database.ExecuteSqlCommand(#"insert into Users_friends
(UserId, FriendId) values ({0}, {1})", userID, friendID);
I don't see an advantage first retrieving the values from db (except verifying both user and friend exists). However, in the above code there is no check if userID, friendID pair already exists which could be easily added to SQL.

You are correct that it is a lot needless overhead. In Entity Framework, it's enough to just pass a new instance of the class with the PK loaded:
var user = db.Users.Find(userID);
var friend = new Friend { Id = friendID };
user.Friends.Add(friend);
db.SaveChanges();

Create the bridge table as a model as well, like this
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public string PasswordSalt { get; set; }
public List<UserFriend> Friends { get; set; }
public User()
{
Friends = new List<UserFriend>();
}
}
public class Friend
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserFriend
{
public int Id { get; set; }
public int UserId { get; set; }
public int FriendId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
[ForeignKey("FriendId")]
public Friend Friend { get; set; }
}
Then you can create or get user friends like this
// add user friend
var userFriend = new UserFriend()
{
UserId = userId,
FriendId = friendId
};
db.UserFriends.Add(userFriend);
db.SaveChanges();
// get user friends
db.Users.Include("Friends").ToList();

Related

How to combine multiple tables in one dataGridView using Entity Framework?

I have a Windows Forms application and want to get information from multiple tables of my database in the single dataGridView area.
I am trying to do it without SqlConnection and SqlDataAdapter, so the connection will be proceed through Entity Framework
DBContext db = new DBContext
Is it possible to do so?
I have three tables:
User:
UserID,
Name
System:
SysID,
SysType
Activities:
ActivID,
UserID (FK)
SysID (FK)
Date,
Version,
Changes
My code:
using DBContext db = new DBCntext())
{
dataGridView.DataSource = db.Table.ToList<Table>();
}
So I would write in case of only one table, but would it be possible to concatenate two or more tables without do it right in the database?
At the end I want to get User+System+Activities tables within one dataGridView.
If your class is properly defined like this:
public class Activity
{
public int ActivityID { get; set; }
public string Version{ get; set; }
public DateTime Date { get; set; }
public string Changes { get; set; }
public int UserID { get; set; }
public User User { get; set; } // navigation property for User
public int SystemID { get; set; }
public System System { get; set; } // navigation property for System
}
Now you can write a query like:
using DBContext db = new DBCntext())
{
dataGridView.DataSource = db.Activitys
.Include(a => a.User)
.Include(a => a.System)
.Select(a => new MyModel {
MyModel.ActivityID = a.ActivityID,
MyModel.Version= a.Version,
MyModel.Date = a.Date ,
MyModel.Changes = a.Changes,
MyModel.UserName = a.User.Name,
MyModel.SysType= a.System.SysType
})
.ToList();
}
If you have not defined navigation properties, then you could use a LINQ Join.

Entity Framework Core: how to use result set of stored procedure and map it to object and object of object by maintaining async

I'm working on Entity Framework Core.
My task is to execute a stored procedure and populate the DTO based on the result of that stored procedure. I'm having difficult to map the properties of DTO class with stored procedure result especially when I've to map it object (another class type) property.
Consider the following DTO classes
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public Company Company { get; set; }
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
My SQL query for stored procedure is:
SELECT
Id = contact.id,
Name = LTRIM(RTRIM(contact.name)),
EmailAddress = LTRIM(RTRIM(contact.email)),
-- company information
CustomerId = contact.cust_id
CustomerName = customer.name
FROM
contact
INNER JOIN
customer ON contact.cust_id = customer.cust_id
I'm using NuGet package StoredProcedureEFCore to perform execution of stored procedure.
Here is the code to call the stored procedure:
Task<Contact> contactDetails = null;
_context.LoadStoredProc("usp_GetContactDetails")
.Exec(r =>
{
contactDetails = r.FirstOrDefaultAsync<Contact>();
});
// TODO: need to populate Company object
This code work for Id, Name, EmailAddress but not for Company property as I've to go company object and populate that object properties.
Based on my research on StoredProcedureEFCore, we can do this using ToDictionaryAsync or ToLookupAsync but I'm still not able to do that.
Plus, one more thing I've to do that as asynchronously.
First of all, I don't think it is possible to map relations in StoredProcedureEFCore using ToDictionaryAsync or ToLookupAsync.
But you can create your own mapping function like:
public static async Task<Contact> MapContact(DbDataReader dataReader){
if(await dataReader.ReadAsync() && dataReader.HasRows){
Contact contact = new Contact();
contact.Id = dataReader.GetInt32( dataReader.GetOrdinal("Id"));
contact.Name = dataReader.GetString(dataReader.GetOrdinal("Name"));
contact.EmailAddress = dataReader.GetString(dataReader.GetOrdinal("EmailAddress"));
Company company = new Company();
company.Id = dataReader.GetInt32(dataReader.GetOrdinal("CompanyId"));
company.Name = dataReader.GetString(dataReader.GetOrdinal("CompanyName"));
contact.Company = company;
return contact;
}
return null;
}
And path it as a parameter to your Exec call
Task<Contact> contactDetails = null;
_context.LoadStoredProc("usp_GetContactDetails")
.Exec(r =>
{
contactDetails = MapContact(r)
});

Entity Framework is trying to insert into wrong table?

I've built a test application with Entity Framework to simulate a database that contains friends lists.
I want the database to store the user's ID's and when I retrieve them (the "AcceptedFriends") I want Entity Framework to also return the friends "usermodel".
But every time I try to add 2 users as friends to the "AcceptedFriends" table it
throws an exception:
" Violation of PRIMARY KEY constraint 'PK_Users'. Cannot insert duplicate key in object 'dbo.Users'. The duplicate key value is (GUID value of a user's ID) "
Some attempted solutions:
Solution 1
Attempting to create 2 lists of the same friend list (received, sent) but that defeats the purpose of what I am trying to achieve.
Solution 2
Here are the code files:
"Users Model"
public class Users
{
#region Private fields
#endregion
#region Public properties
public string Username { get; set; }
public string ID { get; set; }
public string PasswordHash { get; set; }
public virtual List<AcceptedFriends> AcceptedFriendsList { get; set; }
// public virtual List<PendingFriends> PendingFriendsList { get; set; }
// public virtual List<RemovedFriends> RemovedFriendsList { get; set; }
#endregion
}
"Accepted Friends model"
public class AcceptedFriends
{
#region Public properties
public string RelationKey { get; set; }
public string RequestSenderID { get; set; }
public string RequestReceiverID { get; set; }
public virtual List<Messages> ChatList { get; set; }
public Users RequestSender { get; set; }
public Users RequestReceiver { get; set; }
#endregion
}
"Database model creation"
#region Users table
// Create primary key in Users table
modelBuilder.Entity<Users>().HasKey(property => property.ID);
// Map Username to be unique
modelBuilder.Entity<Users>().HasIndex(property => property.Username).IsUnique();
// Create a one to many relation with AcceptedFriends table
modelBuilder.Entity<Users>()
.HasMany(property => property.AcceptedFriendsList)
.WithOne(property => property.RequestReceiver)
.HasForeignKey(property => property.RequestReceiverID);
#endregion
#region Accepted friends table
// Create key for AcceptedFriends
modelBuilder.Entity<AcceptedFriends>().HasKey(property => property.RelationKey);
#endregion
Edit
Here is how I am inserting the friends
public static void AddFriends(AcceptedFriends friends)
{
using(Context context = ConnectToDatabase())
{
context.AcceptedFriends.Add(friends);
context.SaveChanges();
};
}
Edit 2
Here is where I add the friends/users
Plus I've noticed another odd behaviour When I add new users to the friends table
without adding them to the users table first it adds them both to the friends table and users table.
Console.WriteLine("Connecting to database");
DB.ConnectToDatabase();
Console.WriteLine("Connected to database successfully");
List<Users> userList = new List<Users>(DB.GetUsersList());
List<AcceptedFriends> friendsCount = new List<AcceptedFriends>(DB.GetAcceptedFriends());
if(userList.Count != 2)
{
DB.AddUser(new Users()
{
Username = "User1",
PasswordHash = "PasswordHash",
});
DB.AddUser(new Users()
{
Username = "User2",
PasswordHash = "PasswordHash",
});
userList = new List<Users>(DB.GetUsersList());
};
if(friendsCount.Count < 1)
{
Users user1 = userList[0];
Users user2 = userList[1];
DB.AddFriends(new AcceptedFriends()
{
RequestReceiver = user2,
RequestSender = user1,
});
};
Console.WriteLine("Server is great success!");
Console.ReadLine();
Edit 3
I might have found a solution.
It does return the models both for the user and friends,
But I can't accept this as a solution yet because it feels to hackey(?) for me
(Thanks to #wertzui, You helped me to get to this solution)
Basically everytime a new context is created it sets up the the friends and users to return thier usermodels
/// <summary>
/// Gets the friends user models
/// </summary>
/// <param name="context"> The database context that was created </param>
private static void SetupFriends(Context context)
{
// For every "AcceptedFriend"
foreach(AcceptedFriends friend in context.AcceptedFriends)
{
// Get sender and receiver usermodels
// by matching ID's
Users sender = context.Users.FirstOrDefault(user => user.ID == friend.RequestSenderID);
Users receiver = context.Users.FirstOrDefault(user => user.ID == friend.RequestReceiverID);
sender.AcceptedFriendsList.Add(friend);
receiver.AcceptedFriendsList.Add(friend);
friend.RequestSender = sender;
friend.RequestReceiver = receiver;
};
}
When you create new User Instances in your new AcceptFriends {...} Code, you are not setting their Id, so they keep their default which is 0. Now Entity Framework thinks, that you want to create a new Friendship with 2 new Users. Instead you should populate them with the Users, you created earlier.
if(friendsCount.Count < 1)
{
Users user1 = userList[0];
Users user2 = userList[1];
DB.AddFriends(new AcceptedFriends()
{
RequestReceiver = user1,
RequestSender = user2,
});
}

Creating two linked objects before linked field has been generated by database - Entity Framework

I would like to know if there is a way to add two linked objects to a database through entity framework before the linked field has been generated by the database.
I am still learning EF and I'm not exactly sure how to ask this question clearly so here is an example of what I am trying to achieve:
I have two classes:
class Sale
{
public int Id { get; set; } // generated by SQL Server
public string Foo { get; set; }
public string Bar { get; set; }
public virtual ICollection<SalesComment> Comments { get; set; }
}
class SalesComment
{
public int Id { get; set; }
public int SaleId { get; set; }
public string Comment {get; set; }
}
Using fluent api in my 'dbcontext' class I link the two objects like so:
modelBuilder.Entity<Sale>().HasMany(s => s.Comments).WithRequired().HasForeignKey(c => c.SaleId);
This works great, I can retrieve a Sale object from the database and I can loop through the comments linked to it by SaleId.
What I want to do is when creating a new Sale, add a Comment as I am creating it, with EF realising this is happening and adding the SaleId to the Comments table once it is generated by the database, something like:
using (MyDatabase db = new MyDatabase())
{
var sale = db.Set<Sale>();
sale.Add(new Sale
{
Foo = "Test",
Bar = "Test",
Comment.Add(new SalesComment .... //this is the line i'm not sure about
});
db.SaveChanges();
}
Is something like this possible? Or would I have to first save the Sale object to the database so that a SaleId is generated, then retrieve that object again so I can read the SaleId to use for a new SalesComment.
Try this.
using (MyDatabase db = new MyDatabase())
{
var sale = db.Set<Sale>();
var saleComment = new SalesComment{Comment="Hello"};
var saleToAdd = new Sale
{
Foo = "Test",
Bar = "Test",
Comments = new List<SalesComment> {saleComment}
});
sale.Add(saleToAdd);
db.SaveChanges();
// After saving changes, these 2 values will be populated and are equal
var saleID = saleToAdd.Id;
var saleCommentSaleId = saleComment.saleId;
}
You don't have to retrieve the object again if you cache it properly before adding it to the DbSet.

Include referenced table via LINQ under MVC3

How I can use Include of the LINQ properly under MVC3?
I created .edmx file and it has all tables.
Two of them have a relashionships
UserCategories 1..0 - * Users
I guessed to use
var userCategories = db.UserCategories.Include("Users");
in order to populate Users property. But it is always empty.
(Here there is a good example how to use it. But no success.)
How do I can fix it?
P.S. POCO class
public partial class UserCategory
{
public UserCategory()
{
this.Users = new HashSet<User>();
}
public string Name { get; set; }
public System.Guid ID { get; set; }
public virtual ICollection<User> Users { get; set; }
}
Ok, first if Users it's empty probably it's because your don't have the data in the database. Now to be specific refactor your expression like this
var userCategories = db.UserCategories.Select(x => x.Users).ToList();
This will retrieve all the users in your database which have a relation with the table UserCategory
If you just tried to obtain the users no matter the relation with the table
var users = db.Users.ToList(); // This retrieve all the users in your database
Disclaimer: note that this expressions are heave and bring all the records of your database used carefully
Class Model
{
private int userName;
.....
public UserName{get{return userName;} set{userName= value;}}
....
}
I am assuming that Model is the passing model and it has a UserName attribute. And there is a userName column in Users Table.
private dbContext db = new dbContext();
public List<Model> method(){
List<Model> m= List<Model>()
var userCategories = db.UserCategories.Include("Users");
return from item in userCategories select new Model
{
UserName = item.Users.userName
.......
}
}
This return value will be a IEnumerable<Model> it will be executed in foreach. So the

Categories