An entity object cannot be referenced by multiple instances of IEntityChangeTracker - asp.net-mvc-3

I'm getting this error when trying to add an "Entity" to site.
"An entity object cannot be referenced by multiple instances of IEntityChangeTracker"
The Relation Between Site and "Entity" is one to many.
My Models Looks as below:
Site:
public class Site : Model
{
// The collection of Entities belonging to this site
public virtual ICollection<Entity> Entities { get; set; }
}
Entity:
public class Entity : Model
{
public string Label { get; set; }
public string Name { get; set; }
public virtual Site Site { get; set; }
}
My Code in Controller:
[HttpPost]
public ActionResult Add(EntityModel _entityModel)
{
var model = _entityModel.ToEntity();
if (ModelState.IsValid)
{
model.Site = _siteRepository.Find(1);
model.Label = model.Name.Replace(' ','_').ToLower();
_entityRepository.Add(model);
}
return RedirectToAction("Index");
}
Code in EFEntityRepository.cs:
public void Add(Entity entity)
{
DateTime dateModified = DateTime.Now;
entity.CreatedOn = dateModified;
entity.LastUpdatedOn = dateModified;
this._context.Entities.Add(entity);
this._context.SaveChanges();
}
Please suggest a solution.

It sounds like somewhere you have multiple Context classes holding a reference to this object, possibly by key.
Ensure your context is disposed after every request and in addition, there is no other operation causing another context instance to hold onto this entity.

Related

Understanding APB services and repositories

I am learning the APB and I am analyzing and extending the code from the Introduction Part 2 tutorial (ASP.NET Core, EF). https://aspnetboilerplate.com/Pages/Documents/Articles/Introduction-With-AspNet-Core-And-Entity-Framework-Core-Part-2/index.html
First I introduced a new entity - Team. Team is a group of people. I added the foreign key to the Person entity.
[Table("AppPersons")]
public class Person : AuditedEntity<Guid>
{
public const int MaxNameLength = 32;
[Required]
[StringLength(MaxNameLength)]
public string Name { get; set; }
public Guid TeamId { get; set; }
[ForeignKey(nameof(TeamId))]
public Team Team { get; set; }
}
I would like to create an ApplicationService that will return the list of people for a specific team with the number of assigned tasks. I don't know how should I combine the Repository and Mapping infrastructure to achieve this.
public class FooAppService : ApplicationService, IFooAppService
{
private readonly IRepository<Task, Guid> _taskRepository;
public FooAppService(IRepository<Task, Guid> taskRepository)
{
_taskRepository = taskRepository;
}
public ListResultDto<PersonWithNumberOfTasksAssignedDto> FooMethod(Guid teamId)
{
...
}
}
I also don't know what is the best way to design the DTO object. Do I need a new DTO object for this service method or should I use a tuple? I do have a PersonDto object that is used to add/edit Person. Should I just wrap it with a count property?
public class PersonWithNumberOfTasksAssignedDto : EntityDto<Guid>
{
public PersonDto Person { get; set; }
public int NumberOfAssignedTasks { get; set; }
}
You can take reference from Abp free startup template.
public async Task<PersonDto> Create(PersonCreateInput input)
{
var person = ObjectMapper.Map<Person>(input);
await CurrentUnitOfWork.SaveChangesAsync();
return MapToEntityDto(person);
}
For example, UserAppService.Create() https://github.com/aspnetboilerplate/module-zero-core-template/blob/164a5c9e28cb29383551d0f3310986ab43d0ceed/aspnet-core/src/AbpCompanyName.AbpProjectName.Application/Users/UserAppService.cs#L55-L76
To retrieve list of items, you can leverage on the AsyncCrudAppService base class which provides sorting and pagination out of the box (via AsyncCrudAppService.GetAll().
See https://github.com/aspnetboilerplate/aspnetboilerplate/blob/14c4fe5a5408a66e913a434688b951815247827d/src/Abp/Application/Services/AsyncCrudAppService.cs#L112-L129

Using view model in MVC 6

I am trying to implement view model to get the data from multiple table. However, I am getting the following error
InvalidOperationException: The entity type 'RoleManagement.Models.RolePermissionsViewModel' requires a key to be defined.
Below is my view model
public class RolePermissionsViewModel
{
public List<LMS_RolePermissions> RolePermissions { get; set; }
public List<LMS_UserPermissions> UserPermissions { get; set; }
}
Where LMS_RolePermissions and LMS_UserPermissions are two different tables in the database. Basically I want to get the data from these two tables in view model. To get the data I have written below code
RolePermissionsViewModel rolemodel = new RolePermissionsViewModel
{
RolePermissions = dbContext.RolePermissions.ToList(),
UserPermissions = dbContext.UserPermissions.ToList()
};
and DBContext class
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
public DbSet<LMS_LocLanguage> LMS_LocLanguage { get; set; }
public DbSet<LMS_Permissions> Permissions { get; set; }
public DbSet<LMS_RolePermissions> RolePermissions { get; set; }
public DbSet<LMS_UserPermissions> UserPermissions { get; set; }
public DbSet<RolePermissionsViewModel> RoleUserPermission { get; set; }
}
I do not want to Key to be defined and table should NOT be created.
How can I solve this problem ?
it is advised to place seperately your domainmodels and viewmodels. all tables in the applicationcontext are created by entity framework convention if if i use DbSet<myclass> or mention it in another class that used with Dbset. your answer should be excluding types with data annotations NotMapped and with fluent api modelbuilder.ignore<RolePermissionsViewModel>();. (of course,you will remove DbSet firstly. if i read and understand correctly, you say to your codes "please dont create this,i beg on you" after you command database to set.)

Entity Framework: ViewModel to Domain Model

I'm building an MVC 3 website. I have a model looking like this:
public class Survey
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime DateStart { get; set; }
public DateTime DateEnd { get; set; }
// Not in view
public DateTime DateCreated { get; set; }
// Not in view
public DateTime DateModified { get; set; }
}
Based on this I also have a View Model to edit the survey information:
public class SurveyEditViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime DateStart { get; set; }
public DateTime DateEnd { get; set; }
}
When the user finishes editing I would like to persist the changes. Here's my controller post action:
[HttpPost]
public ActionResult Edit(SurveyEditViewModel model)
{
// Map the view model to a domain model using AutoMapper
Survey survey = Mapper.Map<SurveyEditViewModel, Survey>(model);
// Update the changes
_repository.Update(survey);
// Return to the overview page
return RedirectToAction("Index");
}
In my repository (it's a generic one for now) I have the following code:
public void Update(E entity)
{
using (ABCDataContext context = new ABCDataContext())
{
context.Entry(entity).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
}
When this executes I get the following error: "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
I guess this was to be expected. Mapping from the view model to the model doesn't give me a complete Survey object.
I could modify my controller to look like this. And then it works:
[HttpPost]
public ActionResult Edit(SurveyEditViewModel model)
{
// Map the model to a real survey
survey = _repository.Find(model.Id);
survey.Name = model.Name;
survey.Description = model.Description;
survey.DateStart = model.DateStart;
survey.DateEnd = model.DateEnd;
// Update the changes
_repository.Update(survey);
// Return to the overview page
return RedirectToAction("Index");
}
But I was wondering if a better way is available?
[HttpPost]
public ActionResult Edit(SurveyEditViewModel model)
{
// Fetch the domain model to update
var survey = _repository.Find(model.Id);
// Map only the properties that are present in the view model
// and keep the other domain properties intact
Mapper.Map<SurveyEditViewModel, Survey>(model, survey);
// Update the changes
_repository.Update(survey);
// Return to the overview page
return RedirectToAction("Index");
}

Code first DbContext with current user filter

I'm building an ASP.NET MVC3 website with an code first database and have the following question:
Is it possible to make an instance of MyDbContext class with an additional argument set which will be used for filtering the results of calls to mydbcontext.
I want to use this for restricting the resultset to the current user that is logged in on my asp.net mvc website.
Any directions would be great!
I don't see why that should be a problem. Something like this should work:
public class Northwind : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind : Northwind
{
public IQueryable<Products> GetFilteredProducts(string userRole)
{
return Products.Where(product => product.UserRole == userRole);
}
}
Update
To make it impossible for your MyDbContext to be abused, you could put all your database code and models into a separate project/assembly. Then make your DbContext an internal class (instead of public), then create a public class (FilteredDbContext) that wraps your MyDbContext and exposes methods that allow you to only grab the data your allowed to see. Then in your main assembly (your web project), you will only be able to use FilteredDbContext.
So, for example:
internal class Northwind : DbContext // note: internal class
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind // note: does not inherit from `Northwind`
{
private readonly _dbContext = new Northwind();
public IQueryable<Products> GetProducts(string userRole)
{
return _dbContext.Products.Where(product => product.UserRole == userRole);
}
}
If Northwind and FilteredNorthwind are in a separate assembly from your web app, you can instantiate only FilteredNorthwind from your web app.
Update 2
If you use a ViewModel, then your web app can't get back to the list of all products for a category because you extract out only the properties you need (and only the properties the user is allowed to see).
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public IEnumerable<Products> GetProducts(string userRole)
{
return _dbContext.Products
.Where(product => product.UserRole == userRole)
.Select(product => new ProductViewModel
{
Id = product.Id,
Name = product.Name,
Price = product.Price
};
}
You could make a layer above and hide the generated one and create a your own DbContext which derives from the generated MyDbContext. Just a wild guess but it seems logical to me and so you can implement your own argument set and still use the generated one.
I would do this:
public interface IUserContext {
string User { get; set; }
}
public class Database : DbContext {
public IDbSet<Product> Products { get; set; }
}
public class AuthorizedDatabase {
private readonly Database _database;
private readonly IUserContext _userContext;
public AuthorizedDatabase(Database database, IUserContext userContext) {
_database = database;
_userContext = userContext;
}
private bool Authorize<TEntity>(TEntity entity) {
// Some code here to look at the entity and the _userContext and decide if it should be accessible.
}
public IQueryable<Product> Products {
get {
return _database.Products.Where(Authorize);
}
}
}
This would allow me to cleanly abstract the actual logic around the authorization (and your IUserContext interface can be as simple or complex as required to suite your exact needs.)
To ensure that the user is unable is circumvert this protection using a navigation property (Product.Category.Products, for example.) you might need to turn off lazy loading and explicitly load the required related entities.
Have a look at this post from ADO.NET Team Blog for ideas: loading related entities

Convert a string value to an entity in Automapper

I am trying to figure out how to use Automapper when my entity has a field of type entity.
I've got 3 classes like these:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class Contact : Entity
{
public virtual string Name { get; set; }
public virtual Company Company { get; set; }
}
public class Company : Entity
{
public virtual string Name { get; set; }
}
My class Contact contain an element of type Company.
I've also created a ViewModel to trasfer some infos to my view:
public ContactViewModel()
{
public Guid Code { get; set; }
public int Version { get; set; }
[DisplayName("Contact")]
public string Name { get; set; }
[DisplayName("Company")]
public string Company { get; set; }
}
In my viewmodel I've defined a field Company of type string. This is going to contain a value the user will chose from a dropdown (list of companies).
I've defined a bootstrapper when my App starts:
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x => {
x.CreateMap<Domain.Contact, ViewModels.ContactViewModel>();
x.CreateMap<ViewModels.ContactViewModel, Domain.Contact>()
});
}
}
When I try to remap my ViewModel to my entity I get a conversion error (AutoMapper.AutoMapperMappingException).
Automapper can't figure out how to convert my Company (string) into an object of type Company, member of Contact.
Is it possible to define a rule so that Automapper know how to transform the string (company) into the code of my Company object, member of Contact?
You need to use a Resolver. Something like:
public class CompanyTypeResolver : ValueResolver<string, Company>
{
protected override Company ResolveCore(string name)
{
return new Company {Name = name};
}
}
Then in mapping code you call it like:
.ForMember(dto => dto.Company, opt => opt.ResolveUsing<CompanyTypeResolver>().FromMember(src => src.Name))

Resources