I have two tables Team_DATA and Driver_PROFILE_DATA in an SQL database. For every driver_profile there can be many teams.
So there's a one-to-many relation on the driver_profile to team_data table. I want to update a team_data foreign key reference in the Driver_profile table of an already existing record to another team_data record that already exists.
I want to do this using entity framework. Here what I want: having a list of teams to select from, finding the record in the team_data table and updating it's FK in the driver_profile appropriately.
So in the code below, the passed parameter is the newly selected team out of the team_data table.
Now I need it to update it FK reference in the Driver_profile table.
Here's what I've got:
UPDATE: Code Updated. It does not save it to database, even if I call savechanges. No errors.
public Driver_PROFILE_DATA GetSelectedTeam(string team)
{
ObjectQuery<Team_DATA> td = raceCtxt.Team_DATA;
ObjectQuery<Driver_PROFILE_DATA> drpr = raceCtxt.Driver_PROFILE_DATA;
var selteam = from t in td where t.Team_Name == team select t;
Team_DATA newteam = new Team_DATA();
newteam = selteam.Select(x => x).First();
// get driver_profile with associated team_data
var data = from a in raceCtxt.Driver_PROFILE_DATA.Include("Team_DATA") select a;
// put it in driver_profile entity
profileData = data.Select(x => x).First();
profileData.Team_DATAReference.Attach(newteam);
return profileData;
}
Entity Framework should give you a nice Association between the two classes, Update the references as you would using POCOs and stay away from the ID values.
Something like:
newTeam.Profile.Teams.Remove(profileData); // separate from old Profile
profileData.Teams.Add(newTeam);
EDIT:
I made a little test, it is sufficient to set the reference to the Parent object:
newTeam.Profile = profileData;
Related
I have a products table which contains thousands of products. I want to update only two columns (price, isAvailable) of this table. So is there is a way to select only those two columns from this table?
This is the code that I am using. But I don't want to select all columns.
var dbModels = await DbContext.Products
.Where(x => x.SellerId == sellerId)
.ToListAsync();
I have tried this
var db = await DbContext.ProductSkuDetail
.Where(x => x.SellerId == sellerId)
.Select(y => new
{
Price = y.Price,
IsAvailable = y.IsAvailable
}).ToListAsync();
But this is read-only. I want to update those columns.
You must include the primary key in the anonymous type:
var models = await context.Products
.Where(p => p.SellerId == sellerId)
.Select(p => new
{
Id = p.Id, // primary key
Price = p.Price,
IsAvailable = p.IsAvailable
})
.ToListAsync();
Then, when you need to save the data back to the database, you need to create entities with the same primary key and attach them to the context.
foreach (var x in models)
{
var product = new Product
{
Id = x.Id,
Price = newPrice, // get new price somehow
IsAvailable = false // get new availability somehow
};
context.Attach(product);
var entry = context.Entry(product);
entry.Property("Price").IsModified = true;
entry.Property("IsAvailable").IsModified = true;
}
await context.SaveChangesAsync();
More info about Attach
Yes, there is a way to specify exactly which columns you want.
No, you can't use that method to update data.
When fetching data using entity framework DbSet<...>, there are two methods: fetch the complete row of the table, or only fetch certain properties of the row.
The first method is used, if you execute the query without using Select. If you do this, the data is copied to the DbContext.ChangeTracker.
Other methods like DbSet.Find and IQueryble.Include will also copy the fetched data to the ChangeTracker.
If you use Select to specify the data that you want to fetch, then the fetched data will not be copied into the ChangeTracker.
When you call DbContext.SaveChanges, the ChangeTracker is used to determine what items are changed or removed and thus need to be updated.
The ChangeTracker keeps the original fetched data, and a copy of it. You get the reference to the copy as the result of your changes. So whenever you change the values of properties of your reference to the copy, they are changed in the copy that is in the ChangeTracker.
When you call SaveChanges, the copy is compared to the original in the ChangeTracker, to detect which properties are changed.
To improve efficiency, if you don't plan to update the fetched data, it is wise to make sure that the fetched data is not in the ChangeTracker.
When using entity framework to fetch data, always use Select and fetch only the properties that you actually plan to use. Only query without Select if you plan to change the fetched data.
Change = update properties, or remove the complete row. Also: only use Find and Include if you plan to update the fetched data.
You want to update the fetched row
Hence you have to fetch the complete row: don't use Select, fetch the complete row.
If you want to fetch the item by primary key, consider to use DbSet.Find. This has the small optimization that if it is already in the ChangeTracker, then the data won't be fetched again.
Consider to write SQL for this
Usually you don't have to update thousands of items on a regular basis. However, if you have to do this often, consider to update using sql:
using (var dbContext = new MyDbContext(...))
{
const string sqlText = #"Update products
SET Price = #Price, IsAvailable = #IsAvailable....
Where SellerId = #SellerId;";
var parameters = new object[]
{
new SqlParameter("#SellerId", sellerId),
new SqlParameter("#Price", newPrice),
new SqlParameter("#IsAvailable", newAvailability),
};
dbContext.DataBase.ExecuteSqlCommand(sqlText, parameters);
}
(You'll have to check the validity of the SQL command in my example. Since I use entity framework, my SQL is a bit rusty.)
By the way: although this method is very efficient, you'll lose the advantages of entity framework: the decoupling of the actual database from the table structure: the names of your tables and columns seep through until this statement.
My advice would be only to use direct SQL for efficiency: if you have to update quite often. Your DbContext hides the internal layout of your database, so make this method part of your DbContext
public void UpdatePrice(int sellerId, bool IsAvailable, decimal newPrice)
{
const string sqlText = ...
var params = ...
this.Database.ExecuteSqlCommand(sqlText, params);
}
Alas, you'll have to call this once per update, there is no SQL command that will update thousands of items with different prices in one SQLcommand
}
You can use the ExecuteSqlCommandAsync Command to write the query which will be executed in your SQL Server.
await dbContext
.Database
.ExecuteSqlCommandAsync("UPDATE Products SET Price = {0}, IsAvailable = {1} WHERE SelleriId = {2}", new object[] { priceValue, isAvailableValue, sellerId });
EF doesn't fit well for batch operations.
It works with objects, not tables, records or fields. If you want to update or delete object(s), you need to read it(them) first. This allows EF change tracker to track changes in field values or the whole object state, and generate appropriate SQL.
For batch operations consider using raw SQL queries, light-weight libraries like Dapper, or third-party packages like Entity Framework Plus.
I have a database table:
Correlation
------------
JobId (PK),
SomeId (PK),
Order (PK),
MapGuid
I am trying to add a new Correlation (1, 1, 1) into existing Correlation table. The order of existing entities will be updated after Add. The existing entity at (1, 1, 1) will be updated (1,1,2).
However, on add, I get an error:
The instance of entity type 'Correlation' cannot be tracked because another instance with the same key value for {'JobId', 'SomeId', 'Order'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
relevant piece of Code:
if (actionType == ActionType.Create)
{
_unitOfWork.Repository<Correlation>().Insert(
new Correlation
{
JobId = jobId,
SomeId = someId,
Order = order,
MapGuid = newGuid,
});
}
if (actionType == ActionType.Update)
{
// update the title, description, imagefile.
var coors = _unitOfWork.Repository<Correlation>()
.Get()
.Where(a => a.JobId == jobId && a.SomeId == someId && a.Order = order)
.FirstOrDefault();
// set updated values
if (coors != null)
{
coors.Order = newOrder;
}
}
}
How can I add a new entity and then update existing entities at the same time.
You've pretty much answered your own question.
"The order of existing entities will be updated after Add. The existing entity at (1, 1, 1) will be updated (1,1,2)."
EF cannot insert a 1,1,1 when one already exists, and in your case the context knows about it. Even if the context didn't know about it (detached) you'd still get potential errors about inserting duplicate rows.
The main issue you have here is in your design. You potentially have an existing record (1,1,1) that you want to insert a new (1,1,1) and make that old record (1,1,2). Of course that needs to cascade because any existing (1,1,2) would become (1,1,3) and so-forth. The design issue is that you've made the PK of your table a composite key, and a meaningful key at that, as opposed as a dumb, simple, meaningless key. Keys are an identifier to a unique record. By design they should be immutable. (Cannot change)
To solve your issue the easiest way, change your table design to:
Correlation
------------
CorrelationId (PK) Identity 1,1
JobId (FK),
SomeId (FK),
Order,
MapGuid
And create an index on JobId + SomeId + Order since that will be significant for whatever this table will probably be used for. You can even enforce no duplicates, but I'd be wary of that because there may be no guarantee that EF will update the existing rows before inserting the new one.
Now, when you go to do an insert:
var existingCorrelations = context.Correlations.Where(x => x.JobId == jobId && x.SomeId == someId).ToList();
foreach(var correlation in existingCorrelations)
{
correlation.Order += 1;
}
context.Correlations.Add( new Correlation { JobId = jobId, SomeId = someId, Order = 1, MapGuid = mapGuid } );
context.SaveChanges();
In this case the Correlation has a PK called correlation ID. It is a meaningless key in the sense that none of your business logic will ever depend on it except as an identifier for this specific record. Now you can change the Order of an existing record.
Agreeing with Panagoitis about the Generic repository. I don't see it adding anything of value in your case, it helps to keep things simple. The above example should be easy enough to revert back to the repository pattern if that is what you're stuck with.
If you cannot change the data schema, then you're pretty much painted into an ugly little corner of the bathroom. It can still be worked around, but that's a lot more fiddley work loading existing records and moving the non-key data along the chain all the way to a new row on the end, and replacing the first row with the new data. (don't go there, really. :)
I have a table User { UserId, Name, Age } and License { LicenseId, Name, IsActive }. Also I have a table UserLicense { UserId, LicenseId } which connects both of these and has the record for all the users who hold a license. A user can hold multiple license and those appear as different rows in the UserLicense table.
Now I need to remove a particular license from the license table. I do not want to delete it outright, so I mark IsActive = false. However I do want to delete rows from the UserLicense table for that license Id.
I am using Entity Framework.
If it is a direct table, I would have done something like :
var lic = db.Licenses.Where(l => l.Id== licenseId).FirstorDefault();
db.Licenses.Remove(lic);
db.SaveChanges();
However since UserLicense is a table of foreign keys, Entity Framework does not allow me to access it directly using
public void DeleteLicensedUsers(Guid LicenseId)
{
db.UserLicenses.Where()
}
because the model does not contain an independent table for UserLicense as it is only a table of foreign keys.
So how do I delete all rows of UserLicense table for a particular licenseId using Linq and EF6?
If your entity class (I guess User), has navigation properties of type ICollection<Licence> by removing Licence from this collection you are actually removing items from UserLicense table. This is how Entity Framework handles many-to-many relationships in databases.
I faced a similar issue and you can solve it this way. I almost tend to avoid tables with just foreign keys and instead add another column to fix this issue.
var license = db.Licenses.Where(l => l.ID == LicenseId).FirstOrDefault();
var users = license.Users.ToList();
if (users != null)
{
foreach (var user in users)
{
license.Users.Remove(user);
}
}
I'm a little confused by your question. So if you mark a license as isActive = false, and still want to see that license appear on user's page, then there should still a record in UserLicense table associating the two.
That said, I believe this answer from a similar question will help you.
so essentially:
//include licenses in the user entity
var result = ctx.User.Include(x=>x.Licenses).Where(t=>t.Id == _id).FirstOrDefault()
//get the licenses you are looking for, either equal or use .Contains
var licensesOfUser = result.Where(x=>x.Licenses.Where(l=>l.id == _licenseId);
//remove them from entity and save.
result.Licenses.RemoveRange(licenseOfUser);
ctx.SaveChanges();
[edit: if you have license]
//include users in license entities
var result = ctx.License.Include(x=>x.Users).Where(t=>t.Id == _id).FirstOrDefault()
//get the users you are looking for, either equal or use .Contains
var usersOfLicenses = result.Where(x=>x.Users.Where(l=>l.id == _userId);
//remove them from entity and save.
result.Users.RemoveRange(licenseOfUser);
ctx.SaveChanges();
After review the answers and comments:
My DB structure is
1) TABLE BASE_ID ( ID int PK (IDENTITY(1,1))
2) TABLE USERS (ID int PK, FK(BASE_ID.ID).. rest of fields)
3) TABLE THREADS (ID int PK,(IDENTITY(1,1), UserID FK (BASE_ID.ID) )
4) TABLE_POSTS (ID int PK,(IDENTITY(1,1), UserID FK(BASE_ID.ID), ThreadID FK(THREAD.ID) )
I need to insert new values in these tables in one transaction using EF(6.0)
I stuck that EF create two records in BASE_ID even if I do:
//Create new base entry
BASE_ID base = new BASE_ID();
//Create new User
USERS user = new USERS();
//Init fields
THREADS thread = new THREADS();
POSTS post = new POSTS();
post.Thread =thread;// linked property not (int ThreadID since don't yet know it
base.User = user; //linker property
base.Posts.Add(post); //also linked property
//base.Threads.Add(thread);//this was removed. But problem not solved.
oEntity.BASE_ID.Add(base);
oEntity.SaveChanges(); // And here i have already two new BASE_ID's
UPDATE:
DB after commit becomes:
Posts.UserID=n //Lets say 1
Threads.UserId=n,//same as above
Users.ID = n+1; // incremented so 2
I was trying to add in various combination, like
user.BASE_ID = base;
oEntity.USERS.Add(user);
oEntity.THREADS.Add(thread);
oEntity.POSTS.Add(post);
oEntity.SaveChanges(); // same result as above.
I am really stuck.
Question: How to force (or explain to) EF that I need only ONE BASE_ID used for all inserted entries in post thread and user.
Thanks in advance.
This line is making it double
base.Threads.Add(thread);
You already added it before with:
post.Thread =thread;
So it would just be:
//Create new base entry
BASE_ID base = new BASE_ID();
POSTS post = new POSTS() {Thread = new THREADS()};
base.Posts.Add(post);
base.User = new USERS();
oEntity.BASE_ID.Add(base);
oEntity.SaveChanges();
Entity Framework automatically keeps track of the related data, so when you add a new entity it's reference properties are tracked by EF (it's being marked as Added by the ObjectStateManager).
When you add a related entity, it's automatically being marked as added, so if you re-add it again it's added twice, so per your code, instead of adding each entity separately (I take it oEntity is your context), just add the central object, then set its reference properties (i.e. base.User etc.).
Thanks all for your help.
Actually even initial code was fine.
The cause was AfterInsert trigger on USERS table which was adding entry to BASE_ID.
So I mark your comments as Answer and close the thread.
Thanks again.
I have a Profile(as shown in below image) Table Which whenever I try to remove a row from it, I face this error which indicates there is still data in foreign key in Tempprice table which is related to the Id in Lots table .
the problem is when its Lots table turn to Delete , there is still rows in Tempprice which have different Userid but same lotsid that have Ownerid or Winnerid which I want to Delete.
And I don't know how to delete this rows!? or which query is needed?
Database Relationships and Tables:
Code:
while (checkbox.Checked)
{
// Retreive the ID
int ida = Convert.ToInt32(GridView1.DataKeys[row.RowIndex].Value);
// Pass the value of the selected Id(s) to the Delete //command.
//These numbers indicate in which order tables shoulde be deleted
/*1*/
new BLL.LoginBLL().Delete(ida);
/*2*/
new BLL.MessageBLL().Delete(ida);
/*3*/
new BLL.JointBLL().Delete(ida);
/*4*/
new BLL.TemppriceBLL().Delete(ida);
/*5*/
new BLL.LotsBLL().Delete(ida);
/*6*/
new BLL.AuctionBLL().Delete(ida);
/*7*/
new BLL.ProfileBLL().DeleteProfile(ida);
checkbox.Checked = false;
}
ShowUsers();
To delete references, you need to set the relation (by selecting the line connecting the two objects) to Cascade for the End 1 On Delete property.
Then you need to Load the referenced object or collection. Calling Delete in this case, will delete your object and all objects it references.