I am working on mvc project, with repository pattern and entity framework, now on my form i have a sample model
SampleModel
1) name
2) age
3) address
4) notes
5) date updated
I am displaying only following data on the edit form
1) name
2) age
3) address
now if i update the model with missing property values using the repository, the notes, dateupdated field goes null.
My question is how do i update only few selected properties using the repository ( tryupdatemodel not available in repository ) and i dont want to call the original object and map the properites with the updated model.
Is there any way, there must be.
You can update only subset of fields:
using (var context = new YourDbContext())
{
context.SamepleModels.Attach(sampleModel);
DbEntityEntry<SameplModel> entry = context.Entry(sampleModel);
entry.Property(e => e.Name).IsModified = true;
entry.Property(e => e.Age).IsModified = true;
entry.Property(e => e.Address).IsModified = true;
context.SaveChanges();
}
or in ObjectContext API:
using (var context = new YourObjectContext())
{
context.SamepleModels.Attach(sampleModel);
ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(sampleModel);
entry.SetModifiedProperty("Name");
entry.SetModifiedProperty("Age");
entry.SetModifiedProperty("Address");
context.SaveChanges();
}
This is an old thread, but if anyone is interested, to extend on Ladislav's solutions, we've come up with a helpful extension method for EF 4.1 and newer:
public static void SetModified<TEntity>(
this DbEntityEntry<TEntity> entry,
IEnumerable<Expression<Func<TEntity, object>>> expressions) where TEntity : class, IEntity
{
foreach (var expression in expressions)
entry.Property(expression).IsModified = true;
}
Obviously you'll need to take away the IEntity constraint unless you're using an interface by the same name for your POCOs.
Example usage would be:
var user = new User
{
Id = Request.Id,
UserName = Request.UserName,
FirstName = Request.FirstName
};
var expressions = new List<Expression<Func<User, object>>>
{
x => x.UserName,
x => x.FirstName
};
context.Entry(user).SetModified(expressions);
Related
I'm using EF Core and .NET 6 and I would like to essentially upsert an entity to a table - a fairly simple ask.
I have the following code:
var countries = GetCountries();
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
foreach (var c in countries)
{
// check if the country already exists.
var exists = dbContext.Countries.Where(d => d.Id == c.Id).FirstOrDefault();
// if already exists - update (rather than add).
if (exists != null)
{
exists.Name = c.Name;
exists.DisplayName = c.DisplayName;
... // omitted other prop updates.
dbContext.Countries.Update(exists);
}
else
{
dbContext.Countries.Add(c);
}
}
await dbContext.SaveChangesAsync();
}
I was wondering - is there was a more efficient way to update without manually looking up then updating (it's not very performant).
Preferably, I was hoping there was a Merge method in EF Core but can't find anything useful (that's free...). Rather than doing the manual lookup and update in this way.
I'm probably missing something very obvious here - thanks for any pointers in advance!
EF Core do not have Merge, or similar for Upsert.
You can improve performance of your query by selecting existng items in one batch. Also you do not need to call Update, just change properties.
var countries = GetCountries();
using (var scope = scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var countyIds = countries.Select(c => c.Id);
var existing = (await dbContext.Countries.Where(d => countyIds.Contains(d.Id))
.ToListAsync())
.ToDictionary(c => c.Id);
foreach (var c in countries)
{
// check if the country already exists.
if (existing.TryGetValue(c.Id, out var toUpdate))
{
// if already exists - update (rather than add).
toUpdate.Name = c.Name;
toUpdate.DisplayName = c.DisplayName;
... // omitted other prop updates.
}
else
{
dbContext.Countries.Add(c);
}
}
await dbContext.SaveChangesAsync();
}
The answer by user19087368 seems the most straight forward. I tested it on .NET 6 and it worked perfectly - adapted a little bit to my usecase:
Here is my version of it with the "guid" as the primary key:
public async Task CreateOrUpdateApplication(Application application)
{
var itemExists = _dbContext
.Application
.Any(i => i.ApplicationGuid == application.ApplicationGuid);
_dbContext.Entry(application).State = itemExists ?
EntityState.Modified : EntityState.Added;
await _dbContext.SaveChangesAsync();
}
public void InsertOrUpdate(Entity entity)
{
using (var context = new dbContext.Countries())
{
context.Entry(entity).State = entity.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
Goal:
I want to combine information from two tables (entityA & entityB) with different Properties to one unionDto. I am trying to implement this with a union operation to filter different entities in the Database at the same time.
But the structure I am using requires Versions which need to be filtered before the union query.
Some Additional Information:
So what I am trying to do in the last query is: First I project information from table "entityA" and "entityB" to a universal "anonymous" type (with union). Then I am trying to apply pagination and then I am trying to "project" the new anonymous result to a UnionDto. So this would result in one UnionDto which has "information from 2 different tables".
I have created an example of my problem with two entities which have Versions:
Entities:
class entityA {
public List<VersionA> Versions;
public Guid CreatedBy;
}
class entityB {
public List<VersionB> Versions;
public Guid CreatedBy;
}
class VersionA {
public string TitleA;
public Instant EffectiveTo;
}
class VersionB {
public string TitleB;
public Instant EffectiveTo;
}
class UnionDto{
public string Title;
public Guid Creator;
}
I am setting up the query like this:
var queryA = databaseContext.Set<entityA>()
.Select(entity => new
{
Versions = entity.Versions
.Where(version => version.EffectiveTo > now) /* Filtering newest entity Version */
.Select(versionDetail => new /* Selecting only the Title of this Version */
{
Title = versionDetail.TitleA
})
.ToList(),
Creator = entity.CreatedBy,
});
var queryB = databaseContext.Set<entityB>()
.Select(entity => new
{
Versions = entity.Versions
.Where(version => version.EffectiveTo > now)
.Select(versionDetail => new
{
Title = versionDetail.TitleB
})
.ToList(),
Creator = entity.CreatedBy,
});
Executing the query:
var unionDto = await queryA
.Union(queryB)
.Skip(0)
.Take(20)
.Select(x => new UnionDto
{
Title= x.Versions.FirstOrDefault() == null ? null :
x.Versions.FirstOrDefault().Title,
Creator= x.Creator,
})
.ToListAsync();
It seems like that i am not able to use sub selects inside a union query and I am getting the following error:
Set operations: support when placed after client evaluation in
projection #16243
I dont know, what I have to do to get around this issue, since I dont really want to go throught all of my entities seperatly with seperate Database queries.
Currently using Ef core Version: 5.0.100-preview
Now this is just an example. This would be required for at least 10 entities, which would result in high Database traffic if done for each entity seperatly.
Any ideas?
If you need only title of first version from each recordset, your query can be simplified and EF Core can translate this query.
var queryA =
from entity in databaseContext.Set<entityA>()
from version in entity.Versions
where version.EffectiveTo > now
select new
{
Title = version.Title,
Creator = entity.CreatedBy
}
var queryB =
from entity in databaseContext.Set<entityB>()
from version in entity.Versions
where version.EffectiveTo > now
select new
{
Title = version.Title,
Creator = entity.CreatedBy
}
var versions = queryA.Union(queryB);
var unionDto = await versions
.Skip(0)
.Take(20)
.Select(x => new UnionDto
{
Title = x.Title,
Creator = x.Creator,
})
.ToListAsync();
Lets say that I have a Repository with this function
public async Task<IEnumerable<Contacts>> GetAll()
{
return await _context.Contacts.ToListAsync();
}
Where the Contacts Entity is the same one returning the call. But I didn't want to use the same class because there's some fields that I like to keep out of the call. There's any way that I could "mirror" a second model called "ContactsModel" to return the data without using Anonymous calls like :
var result = context.t_validation.Where(a => a.isvalidated == 10).Select(x => new
{
x.date_released,
x.utoken,
x.Images,
x.images_key,
x.Type
});
Of putting into a loop and passing to this new Model :
foreach (var item in list)
{
decp.Add(new ValidationModel
{
uToken = item.utoken,
Date = item.date_released,
Images = bc.Decrypt(item.Images, item.images_key),
Type = item.Type
});
}
Thanks!
Because you are using custom method to decrypt an image, you will not be able to include it in the query, because EF will not be able to translate it into sql query.
Anonymous approach would be the best one
public async Task<IEnumerable<Contacts>> GetAll()
{
var models = await _context
.Contacts
.Select(contact => new
{
contact.date_released,
contact.utoken,
contact.Images,
contact.images_key,
contact.Type
})
.ToListAsync()
return models
.Select(item => new ValidationModel
{
uToken = item.utoken,
Date = item.date_released,
Images = bc.Decrypt(item.Images, item.images_key),
Type = item.Type
}
.ToList();
}
Of course you can wrap it with an extension methods, but if you are using this mapping only in one place you don't need to.
This is a method i have to update a single navigation property in a many-to-many relationship, but its too specific for the DAL. I was wondering if anyone could help make this method more generic so it can handle any entity being passed in.
I would pass in two parameters: the entity to be updated, and the navigation property to be modified.
The below method works with an item table and a property table that are involved in a many-to-many relationship. An item can be assigned to many properties and a property can have many items.
Thanks for any help you can provide.
public void UpdateItems(Property property)
{
using (var context = new PropertyManagementDBEntities())
{
var customerInDb = context.Properties.Include("Items")
.Single(c => c.propertyId == property.propertyId);
// Remove types
foreach (var itemInDb in customerInDb.Items.ToList())
if (!property.Items.Any(t => t.itemId == itemInDb.itemId))
customerInDb.Items.Remove(itemInDb);
// Add new types
foreach (var item in property.Items)
if (!customerInDb.Items.Any(t => t.itemId == item.itemId))
{
context.Items.Attach(item);
customerInDb.Items.Add(item);
}
context.SaveChanges();
}
}
You can do it like this.
public void UpdateItems<TEntity,TRelated>(
// Entity with new list of related items
TEntity entity,
// Selector for the key of the entity element
Func<TEntity,object[]> entityKeySelector,
// Selector for the related items of the Property
Expression<Func<TEntity,ICollection<TRelated>>> relatedItemsSelector,
// Comparer of related items
Func<TRelated, TRelated, bool> relatedItemsComparer)
where TEntity : class
where TRelated : class
{
using (var context = new TCtx())
{
// get the Keys for the entity
object[] entityKeyValues = entityKeySelector.Invoke(entity);
// gets the entity entity from the DB
var entityInDb = context.Set<TEntity>().Find(entityKeyValues);
// loads the related entities from the DB
context.Entry(entityInDb).Collection(relatedItemsSelector).Load();
// gets the list of properties in the passed entity
var newRelatedItems
= relatedItemsSelector.Compile().Invoke(entity);
// Gets the list of properties loaded from the DB
var relatedItemsInDb
= relatedItemsSelector.Compile().Invoke(entityInDb);
// Remove related elements
foreach (var relatedInDb in relatedItemsInDb)
if (!newRelatedItems.Any(item => relatedItemsComparer
.Invoke(relatedInDb, item)))
{
// If the related intem in DB is not in the entity, remove it
relatedItemsInDb.Remove(relatedInDb);
}
// Add new types
foreach (var item in newRelatedItems)
if (!relatedItemsInDb.Any(itemInDb => relatedItemsComparer
.Invoke(itemInDb, item)))
{
// Attach the item to the Set
context.Set<TRelated>().Attach(item);
// If the related item is not in the DB add it
relatedItemsInDb.Add(item);
}
context.SaveChanges();
}
}
If your entities have only one key field, you can change the entity key selector to this one: Func<TEntity,object> entityKeySelector which makes it easier to use (or implement both signatures).
For example if you have items which can have different colors, you can invoke it like this
ManyToManyHandler<MyDbContext>.UpdateItems(
item,
i => new object[] {i.ItemId},
i => i.Colors,
(c1, c2) => c1.ColorId == c2.ColorId
);
with the second overload:
ManyToManyHandler<MyDbContext>.UpdateItems(
item,
i => i.ItemId,
i => i.Colors,
(c1, c2) => c1.ColorId == c2.ColorId
);
NOTE: I'm using the class ManyToManyHandler which is a generic static class with TCtx as a generic parameter, and UpdateItems as an static method
We have a lot of tests such as this:
using (new TransactionScope())
{
var repository = _unitOfWork.DataFieldSetRepository;
var entity = new DataFieldSet {Label = "test", Title = "test"};
repository.Add(entity);
_unitOfWork.Commit();
entity = repository.GetAll().Single(x => x.Id == entity.Id);
entity.IsClosed = true;
repository.Update(entity);
_unitOfWork.Commit();
repository.Delete(entity);
_unitOfWork.Commit();
}
There are no Asserts because the test is passed unless an exception is thrown.
Instead of copying this code and altering a few details, I would like to generalize it.
The only bits that vary are
1) The entity type and the corresponding repository (here DataFieldSet and DataFieldSetRepository)
2) The contents of the newly created entity (typically the minimal contents to make the test pass, here Label and Title cannot be null)
3) The update operation (typically just one random property has its value changed)
So far I have this:
public void AssertCrudOperationsWork<T>(T entity, Func<T, T, bool> keyComparer, Action<T> updateAction) where T : class
{
using (new TransactionScope())
{
var repository = (IRepository<T>)_unitOfWork.GetType().GetProperty(typeof(T).Name + "Repository").GetValue(_unitOfWork);
repository.Add(entity);
_unitOfWork.Commit();
//var keyProperties = typeof(T).GetProperties().Where(prop => prop.IsDefined(typeof(KeyAttribute), false));
Expression<Func<T, bool>> keyEqualsKey = x => keyComparer(x, entity);
entity = repository.GetAll().Single(keyEqualsKey);
updateAction(entity);
repository.Update(entity);
_unitOfWork.Commit();
repository.Delete(entity);
_unitOfWork.Commit();
}
}
[TestMethod]
public void CRUDTest_DataFieldGroup()
{
AssertCrudOperationsWork(new DataFieldSet {Label = "test", Title = "test"}, (a, b) => a.Id == b.Id, x => x.IsClosed = true);
}
The problem is, it fails in the call to Single() with: The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
I guess my keyEqualsKey predicate is not exactly the same as the original x => x.Id == entity.Id.
Is there a way to fix this?
The commented line //var keyProperties ... gets all the key properties of the entity. Is there a dynamic way to build the predicate comparing the entity keys so that the keyComparer and the keyEqualsKey can be removed altogether?