How can update a column by itself on db Level with Entity Framework and Unit of Work? - entity-framework

I'm using Entity Framework and Unit of Work.
I have a decimal column OrderBalance in the Person table and I have an Order table. I want to update orderbalance column by itself at the db level to support concurrent order creations.
I want to insert an order and update OrderBalance column with atomocity (all or nothing).
public override void Create(Order order)
{
_orderReposiory.Add(order);
var person = _personRepository.GetById(order.PersonId);
person.OrderBalance += order.Amount*order.Price;
_personRepository.Edit(person);
_unitOfWork.Commit();
}
As you can see, '+=' process is on object level. How can I do this on db level without breaking atomicity?

I'm using ExeceutSqlCommand with transactionscope and it's work.
public class PersonRepository : GenericRepository<Person>, IPersonRepository
{
public void UpdateOrderBalance(decimal amount,long personId)
{
Entities.Database.ExecuteSqlCommand("Update Person set OrderBalance=OrderBalance+#p0 where id=#p1", amount,personId);
}
}
I have changed my Create Method to this
public override void Create(Order order)
{
using (var scope = new System.Transactions.TransactionScope())
{
_orderReposiory.Add(order);
AddOrderBalancePerson(order);
_unitOfWork.Commit();
scope.Complete();
}
}
private void AddOrderBalancePerson(Order order)
{
_personRepository.UpdateOrderBalance(order.Amount*order.Price, order.PersonId);
}
Entities in PersonRepository and UnitofWork are using same Dbcontext

You need to use the same DbContext instance in both repositories. If you do then you can wrap any number of inserts/updates in a transaction which will give you all or nothing. EF statements will automatically enlist in any transaction pending.
using (var tran = dbContext.Database.BeginTransaction())
{
// your updates here
}

Related

Unit-testing EF6 non-query operations

I'm using Entity Framework 6 and I have a class with a method that adds some records to the database:
interface IRecordsContext
{
DbSet<MyRecord> MyRecords { get; }
int SaveChanges();
}
class MyService
{
public MyService(IRecordsContext ctx)
{
_context = ctx;
}
private readonly IRecordsContext _context;
public void AddRecords(int count)
{
_context.MyRecords.AddRange(
from id in Enumerable.Range(1, count)
select new MyRecord { Value = id }
);
_context.SaveChanges();
}
}
Now I am using Moq library to create unit tests:
void AddRecords_ShouldAddThemToDatabase()
{
var contextMock = new Mock<IRecordsContext>();
// ...
}
How can I write a test that ensures that the Records collection now contains 10 extra records using mocks, without modifying any actual database?
I'm also eager to listen to opinions on whether this architecture is un-testable and how it should be refactored.
Have one variable that is set to .Count() of the DbSet before the method is run, and check that against the value of .Count() afterwards.

Duplicate queries while removing multiple rows in one transaction in Entity Framework 6

I'm working with EF 6 and have the repository class such this:
public class EfRepository<T> : IRepository<T> where T : class
{
private readonly DbContext _context;
public EfRepository(DbContext context)
{
_context = context;
}
....
public void Delete(IEnumerable<T> entities)
{
// skip checks
using (var transaction = _context.Database.BeginTransaction())
{
try
{
_context.Set<T>().RemoveRange(entities);
_context.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
}
}
}
In my controller I have repository instance IRepository<Connection> _repository than binded with Autofac to EfRepository class.
Then I remove multiple items (and everything works fine!):
IEnumerable<Connection> connections = // get some connections;
_repository.Delete(connections); // everything fine - records was removed
But when I open my site with installed MiniProfiler it shows me duplicate sql-query warning:
My question is why I use transactions but still has duplicate sql warning?
Thank you.
This is because Entity Framework currently sends one query per item to be deleted. It does not batch them all into one query. So MiniProfiler is correctly reporting on what is happening - duplicate delete queries (with exception of the param value) are being submitted.
What is your transaction.Commit() doing? Maybe you can add the code of this method to your question.
I am also deleting entites from my database but more like this:
public virtual void Delete(TEntity entityToDelete)
{
if (Context.Entry(entityToDelete).State == EntityState.Detached)
{
DBSet.Attach(entityToDelete);
}
DBSet.Remove(entityToDelete);
}
I think there are no differences between Remove and RemoveRange, but maybe you should check the state first?

Generic repository to update an entire aggregate

I am using the repository pattern to provide access to and saving of my aggregates.
The problem is the updating of aggregates which consist of a relationship of entities.
For example, take the Order and OrderItem relationship. The aggregate root is Order which manages its own OrderItem collection. An OrderRepository would thus be responsible for updating the whole aggregate (there would be no OrderItemRepository).
Data persistence is handled using Entity Framework 6.
Update repository method (DbContext.SaveChanges() occurs elsewhere):
public void Update(TDataEntity item)
{
var entry = context.Entry<TDataEntity>(item);
if (entry.State == EntityState.Detached)
{
var set = context.Set<TDataEntity>();
TDataEntity attachedEntity = set.Local.SingleOrDefault(e => e.Id.Equals(item.Id));
if (attachedEntity != null)
{
// If the identity is already attached, rather set the state values
var attachedEntry = context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(item);
}
else
{
entry.State = EntityState.Modified;
}
}
}
In my above example, only the Order entity will be updated, not its associated OrderItem collection.
Would I have to attach all the OrderItem entities? How could I do this generically?
Julie Lerman gives a nice way to deal with how to update an entire aggregate in her book Programming Entity Framework: DbContext.
As she writes:
When a disconnected entity graph arrives on the server side, the
server will not know the state of the entities. You need to provide a
way for the state to be discovered so that the context can be made
aware of each entity’s state.
This technique is called painting the state.
There are mainly two ways to do that:
Iterate through the graph using your knowledge of the model and set the state for each entity
Build a generic approach to track state
The second option is really nice and consists in creating an interface that every entity in your model will implement. Julie uses an IObjectWithState interface that tells the current state of the entity:
public interface IObjectWithState
{
State State { get; set; }
}
public enum State
{
Added,
Unchanged,
Modified,
Deleted
}
First thing you have to do is to automatically set the state to Unchanged for every entity retrieved from the DB, by adding a constructor in your Context class that hooks up an event:
public YourContext()
{
((IObjectContextAdapter)this).ObjectContext
.ObjectMaterialized += (sender, args) =>
{
var entity = args.Entity as IObjectWithState;
if (entity != null)
{
entity.State = State.Unchanged;
}
};
}
Then change your Order and OrderItem classes to implement the IObjectWithState interface and call this ApplyChanges method accepting the root entity as parameter:
private static void ApplyChanges<TEntity>(TEntity root)
where TEntity : class, IObjectWithState
{
using (var context = new YourContext())
{
context.Set<TEntity>().Add(root);
CheckForEntitiesWithoutStateInterface(context);
foreach (var entry in context.ChangeTracker
.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.State);
}
context.SaveChanges();
}
}
private static void CheckForEntitiesWithoutStateInterface(YourContext context)
{
var entitiesWithoutState =
from e in context.ChangeTracker.Entries()
where !(e.Entity is IObjectWithState)
select e;
if (entitiesWithoutState.Any())
{
throw new NotSupportedException("All entities must implement IObjectWithState");
}
}
Last but not least, do not forget to set the right state of your graph entities before calling ApplyChanges ;-) (You could even mix Modified and Deleted states within the same graph.)
Julie proposes to go even further in her book:
you may find yourself wanting to be more granular with the way
modified properties are tracked. Rather than marking the entire entity
as modified, you might want only the properties that have actually
changed to be marked as modified.
In addition to marking an entity as modified, the client is also
responsible for recording which properties have been modified. One way
to do this would be to add a list of modified property names to the
state tracking interface.
But as my answer is already too long, go read her book if you want to know more ;-)
My opinionated (DDD specific) answer would be:
Cut off the EF entities at the data layer.
Ensure your data layer only returns domain entities (not EF entities).
Forget about the lazy-loading and IQueryable() goodness (read: nightmare) of EF.
Consider using a document database.
Don't use generic repositories.
The only way I've found to do what you ask in EF is to first delete or deactivate all order items in the database that are a child of the order, then add or reactivate all order items in the database that are now part of your newly updated order.
So you have done well on update method for your aggregate root, look at this domain model:
public class ProductCategory : EntityBase<Guid>
{
public virtual string Name { get; set; }
}
public class Product : EntityBase<Guid>, IAggregateRoot
{
private readonly IList<ProductCategory> _productCategories = new List<ProductCategory>();
public void AddProductCategory(ProductCategory productCategory)
{
_productCategories.Add(productCategory);
}
}
it was just a product which has a product category, I've just created the ProductRepository as my aggregateroot is product(not product category) but I want to add the product category when I create or update the product in service layer:
public CreateProductResponse CreateProduct(CreateProductRequest request)
{
var response = new CreateProductResponse();
try
{
var productModel = request.ProductViewModel.ConvertToProductModel();
Product product=new Product();
product.AddProductCategory(productModel.ProductCategory);
_productRepository.Add(productModel);
_unitOfWork.Commit();
}
catch (Exception exception)
{
response.Success = false;
}
return response;
}
I just wanted to show you how to create domain methods for entities in domain and use it in service or application layer. as you can see the code below adds the ProductCategory category via productRepository in database:
product.AddProductCategory(productModel.ProductCategory);
now for updating the same entity you can ask for ProductRepository and fetch the entity and make changes on it.
note that for retrieving entity and value object of and aggregate separately you can write query service or readOnlyRepository:
public class BlogTagReadOnlyRepository : ReadOnlyRepository<BlogTag, string>, IBlogTagReadOnlyRepository
{
public IEnumerable<BlogTag> GetAllBlogTagsQuery(string tagName)
{
throw new NotImplementedException();
}
}
hope it helps

GenericRepository TEntity change attribute value

I am using EF 5.0 and the model first approach. I have build a GenericRepository that has the basic get, insert, delete etc statements. Like:
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
My EF entities all have the attributes Modified and ModifiedBy. Now I want to change this values everytime I save an entity.
Is it possible to modify this two attributes (set the value) without writing an specific implementation all the time?
Thank you
I see two options for you to do this, but they both entail either introducing a base type or an interface for all of your entities to cover them in a generic function. I would prefer an interface, although each entity would have to implement it again and again.
Let's say you create
interface IAuditable
{
DateTime Modified { get; set; }
string ModifiedBy {get; set; } // User id?
}
Now you can do:
public virtual void Insert(TEntity entity)
where TEntity : IAuditable
{
entity.Modified = DateTime.Now;
entity.ModifiedBy = ???? // Whatever you get the name from
...
}
(Same for edit)
You can also subscribe to the context's SavingChanges event:
// In the constructor:
context.SavingChanges += this.context_SavingChanges;
private void context_SavingChanges(object sender, EventArgs e)
{
foreach (var auditable in context.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
.Select(entry => entry.Entity)
.OfType<IAuditable>)
{
auditable.Modified = DateTime.Now;
auditable.ModifiedBy = ????;
}
}
If you work with DbContext you can get to the event by
((IObjectContextAdapter)this).ObjectContext.SavingChanges
I'd like to add that more reliable time tracking can (and maybe should) be achieved by database triggers. Now you depend on a client's clock.
You can do this using the following code in your all methods of repository where you want to.
public virtual void Edit(TEntity entity)
{
entity.Modified=DateTime.Now;
entity.ModifiedBy=User.Identity.Name;
//Other saving to repository code
}

Entity framework CTP5 Violation of PRIMARY KEY constraint

i have started learning entity framework CTP5 by writing a windows application.
i have two models (Unit and Good) as following:
public class Unit : BaseEntity
{
public Unit()
{
Goods = new List<Good>();
}
public string name { get; set; }
public virtual ICollection<Good> Goods { get; set; }
}
public class Good : BaseEntity
{
public Int64 code { get; set; }
public string name { get; set; }
public virtual Unit Unit { get; set; }
}
i'm using a repository inteface named IRepository as below :
public interface IRepository
{
BaseEntity GetFirst();
BaseEntity GetNext(Int32 id);
BaseEntity GetPrevoius(Int32 id);
BaseEntity GetLast();
BaseEntity GetById(Int32 id);
void Update(int id, BaseEntity newEntity);
void Delete(int id);
void Insert(BaseEntity entity);
int GetMaxId();
IList GetAll();
}
every model has its own repository but maybe it is better to use a generic repository of BaseEntity type. A reference of GoodRepository is made in GoodForm and appropriate object of Good type is made by Activator object in common form methods like Insert/Update/Delete... as below :
private void InsertButton_Click(object sender, EventArgs e)
{
Unit unit = goodRepo.GetUnitById(Convert.ToInt32(UnitIdTextBox.Text));
if (unit == null)
{
unit = new Unit { Id = goodRepo.GetUnitMaxId(), Name = "Gram" };
}
var good = Activator.CreateInstance<Good>();
good.Id = string.IsNullOrEmpty(IdTextBox.Text) ? goodRepo.GetMaxId() : Convert.ToInt32(IdTextBox.Text);
IdTextBox.Text = good.Id.ToString();
good.Name = NameTextBox.Text;
good.Description = DescriptionTextBox.Text;
good.Unit = unit;
goodRepo.Insert(good);
}
and GoodRepository.Insert method is :
public void Insert(Model.BaseEntity entity)
{
using (PlanningContext context = new PlanningContext())
{
context.Goods.Add(entity as Good);
int recordsAffected = context.SaveChanges();
MessageBox.Show("Inserted " + recordsAffected + " entities to the database");
}
}
My problem is SaveChanges() generate an error "Violation of PRIMARY KEY constraint" and says it can not inset duplicate key in object 'dbo.Units.
but if i move my context to the form which i build Good object and insert it there everything is fine.
Can anybody guid me how to solve this issue?
thank in advance
The source of your problem is here:
using (PlanningContext context = new PlanningContext())
{
context.Goods.Add(entity as Good);
//...
}
You are adding the Good entity to a newly created and therefore initially empty context. Now, if you add an entity to the context EF will add the whole object graph of related entities to the context as well, unless the related entities are already attached to the context. That means that good.Unit will be put into the context in Added state as well. Since you don't seem to have an autogenerated identity key for the Unit class, EF tries to insert the good.Unit into the DB with the same key which is already in the database. This causes the exception.
Now, you could ad-hoc fix this problem by attaching the Unit to the context before you add a new Good:
using (PlanningContext context = new PlanningContext())
{
context.Units.Attach((entity as Good).Unit);
context.Goods.Add(entity as Good);
//...
}
But I would better rethink the design of your repository. It's not a good idea to create a new context in every repository method. The context plays the role of a unit of work and a unit of work is usually more a container for many database operations which belong closely together and should be committed in a single database transaction.
So, operations like your InsertButton_Click method should rather have a structure like this:
using (var context = CreateSomehowTheContext())
{
var goodRepo = CreateSomehowTheRepo(context); // Inject this context
var perhapsAnotherRepo = CreateTheOtherRepo(context); // Inject same context
Unit unit = goodRepo.GetUnitById(Convert.ToInt32(UnitIdTextBox.Text));
// unit is now attached to context
// ...
good.Unit = unit;
goodRepo.Insert(good); // should use the injected context and only do
// context.Goods.Add(good);
// It doesn't add unit to the context since
// it's already attached
// ...
context.SaveChanges();
}
Here you are working only with one single context and the repositories will get this context injected (in the constructor for instance). They never create their own context internally.
I suspect it's because GetUnitMaxId is returning the same value more than once. Is Id an auto-incrementing identity column? If so, you shouldn't try to make any assumptions about what that value might be in code.
Even if it's not an auto-incrementing identity column, you can only be sure of it's value when all others have been committed to the DB.
As a general design pattern, try to avoid the need to refer to Ids in code before they've been stored. EF can help with this by exploiting navigation properties (inter-entity object references).