Attach method on DbContext - entity-framework

My controller which uses the Attach method throws an error:
DbContext does not contain a definition of Attach.
How can I fix this problem? I appreciate every help
public class ApplicationController : Controller
{
ApplicationDbContext _context = new ApplicationDbContext();
[HttpGet]
public ActionResult Delete(int Id)
{
Brand brand = _context.Brands
.Include(e => e.Products)
.Where(a => a.brand_id == Id)
.FirstOrDefault();
return View(brand);
}
[HttpPost]
public ActionResult Delete(Brand brand)
{
_context.Attach(brand);
_context.Entry(brand).State = EntityState.Deleted;
_context.SaveChanges();
return RedirectToAction("Index");
}
}

Related

Asp.net core mvc migration database

When I add a column at Database, how to renew my DB.Context.
it is my Context.cs
modelBuilder.Entity<SensorModbusRtusetting>(entity =>
{
entity.ToTable("SensorModbusRTUSetting");
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.Baudrate).HasColumnName("baudrate");
entity.Property(e => e.Channel)
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("channel");
entity.Property(e => e.Comport)
.IsRequired()
.HasColumnName("comport");
entity.Property(e => e.Pid).HasColumnName("pid");
entity.Property(e => e.Slaveid).HasColumnName("slaveid");
entity.Property(e => e.Nane).HasColumnName("nane");
It is my model
namespace Sensormanager2V2
{
public partial class SensorModbusRtusetting
{
public string Comport { get; set; }
public int Baudrate { get; set; }
public string Channel { get; set; }
public int Slaveid { get; set; }
public int Pid { get; set; }
public long Id { get; set; }
public string Nane { get; set; }
}
}
it is my controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Sensormanager2V2;
namespace Sensormanager2V2.Controllers
{
public class SensorModbusRtusettingsController : Controller
{
private readonly postgresContext _context;
public SensorModbusRtusettingsController(postgresContext context)
{
_context = context;
}
// GET: SensorModbusRtusettings
public async Task<IActionResult> Index()
{
return View(await _context.SensorModbusRtusettings.ToListAsync());
}
// GET: SensorModbusRtusettings/Details/5
public async Task<IActionResult> Details(long? id)
{
if (id == null)
{
return NotFound();
}
var sensorModbusRtusetting = await _context.SensorModbusRtusettings
.FirstOrDefaultAsync(m => m.Id == id);
if (sensorModbusRtusetting == null)
{
return NotFound();
}
return View(sensorModbusRtusetting);
}
// GET: SensorModbusRtusettings/Create
public IActionResult Create()
{
return View();
}
// POST: SensorModbusRtusettings/Create
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Comport,Baudrate,Channel,Slaveid,Pid,Id,Name")] SensorModbusRtusetting sensorModbusRtusetting)
{
if (ModelState.IsValid)
{
_context.Add(sensorModbusRtusetting);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(sensorModbusRtusetting);
}
// GET: SensorModbusRtusettings/Edit/5
public async Task<IActionResult> Edit(long? id)
{
if (id == null)
{
return NotFound();
}
var sensorModbusRtusetting = await _context.SensorModbusRtusettings.FindAsync(id);
if (sensorModbusRtusetting == null)
{
return NotFound();
}
return View(sensorModbusRtusetting);
}
// POST: SensorModbusRtusettings/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(long id, [Bind("Comport,Baudrate,Channel,Slaveid,Pid,Id,Name")] SensorModbusRtusetting sensorModbusRtusetting)
{
if (id != sensorModbusRtusetting.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(sensorModbusRtusetting);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!SensorModbusRtusettingExists(sensorModbusRtusetting.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(sensorModbusRtusetting);
}
// GET: SensorModbusRtusettings/Delete/5
public async Task<IActionResult> Delete(long? id)
{
if (id == null)
{
return NotFound();
}
var sensorModbusRtusetting = await _context.SensorModbusRtusettings
.FirstOrDefaultAsync(m => m.Id == id);
if (sensorModbusRtusetting == null)
{
return NotFound();
}
return View(sensorModbusRtusetting);
}
// POST: SensorModbusRtusettings/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(long id)
{
var sensorModbusRtusetting = await _context.SensorModbusRtusettings.FindAsync(id);
_context.SensorModbusRtusettings.Remove(sensorModbusRtusetting);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool SensorModbusRtusettingExists(long id)
{
return _context.SensorModbusRtusettings.Any(e => e.Id == id);
}
}
}
If you update the structure of a table in your database, you need to ensure that your change is propagated to the data model, views, and controller.
For this tutorial, you will add a new column to the Student table to record the middle name of the student. To add this column, open the database project, and open the Student.sql file. Through either the designer or the T-SQL code, add a column named MiddleName that is an NVARCHAR(50) and allows NULL values.
Deploy this change to your local database by starting your database project (or F5). The new field is added to the table. If you do not see it in the SQL Server Object Explorer, click the Refresh button in the pane.
The new column exists in the database table, but it does not currently exist in the data model class. You must update the model to include your new column. In the Models folder, open the ContosoModel.edmx file to display the model diagram. Notice that the Student model does not contain the MiddleName property. Right-click anywhere on the design surface, and select Update Model from Database.
In the Update Wizard, select the Refresh tab and then select Tables > dbo > Student. Click Finish.
After the update process is finished, the database diagram includes the new MiddleName property. Save the ContosoModel.edmx file. You must save this file for the new property to be propagated to the Student.cs class. You have now updated the database and the model.
Build the solution.

Breeze EF SaveChanges() on a DTO

I've been struggling with Breeze to SaveChanges to a projection and admit I new to both EF and breeze. There were some similar questions earlier when I was trying to use WCF, but now I have abandoned WCF and added EF directly to my solution.
In my controller I return the DTO for the metadata to breeze along with the DTO and it binds perfectly.
After altering the data on the client my Breese Controllers [HttpPost] SaveChanges(save Bundle) is called and map contains the DTO and the changes.
How Do I persist the Changes? If I re-read the DTO projection for breeze to update then EF cant save a projection because it's not "tracked", if I read the Full entity, then Breeze error with "Sequence contains no matching element" because its looking for the DTO? Am I suppose to use AutoMapper?
Controller:
[BreezeController]
public class BreezeController : ApiController
{
readonly EFContextProvider<ManiDbContext> _contextProvider = new EFContextProvider<ManiDbContext>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpGet]
public IQueryable<ConsigneDTO> Consignee(string refname)
{
return _contextProvider.Context.consigneDTO(refname);
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
ManiDbContextProvider _mcontextProvider = new ManiDbContextProvider();
return _mcontextProvider.SaveChanges(saveBundle);
}
ManiDbContext (the main DBContext is CifContext which is Database First/EF Reverse Engineer)
public class ManiDbContext : DbContext
{
public CifContext CifDbContext = new CifContext();
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
Database.SetInitializer<ManiDbContext>(null);
modelBuilder.Configurations.Add(new ConsigneDTOMap());
}
public override int SaveChanges()
{
CifDbContext.SaveChanges();
return 1;
}
public IQueryable<ConsigneDTO> consigneDTO(string refname)
{
IQueryable<ConsigneDTO> q = this.CifDbContext.Consignes
.Where(x => x.Refname == refname)
.Select(f => new ConsigneDTO {Refname = f.Refname, Consignee = f.Consignee, Address1 = f.Address1, Address2 = f.Address2, Address3 = f.Address3});
return q;
}
ManiDbContextProvider
public class ManiDbContextProvider : EFContextProvider<CifContext>
// public class ManiDbContextProvider : EFContextProvider<ManiDbContext>
{
public ManiDbContextProvider() : base() { }
protected override void OpenDbConnection()
{// do nothing
}
protected override void CloseDbConnection()
{ // do nothing
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
var entity = entityInfo.Entity;
if (entity is ConsigneDTO)
{
return BeforeSaveConsignee(entity as ConsigneDTO, entityInfo);
}
throw new InvalidOperationException("Cannot save entity of unknown type");
}
private bool BeforeSaveConsignee(ConsigneDTO c, EntityInfo info)
{
var consdata = this.Context.CifDbContext.Consignes
.Where(x => x.Refname == c.Refname)
.FirstOrDefault(); // ENTITY
// var consdata = this.Context.consigneDTO(c.Refname); // DTO
return (null != consdata) || throwCannotFindConsignee();
}
CifContext (Full Columns - First/EF Reverse Engineer/ Consigne class contains Keys)
public partial class CifContext : DbContext
{
static CifContext()
{
Database.SetInitializer<CifContext>(null);
}
public CifContext()
: base("Name=CifContext")
{
}
public DbSet<Consigne> Consignes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); // Use singular table names
Database.SetInitializer<CifContext>(null);
modelBuilder.Configurations.Add(new ConsigneMap());
}
Regardless if I Read the Entity or the DTO - I'm Clueless on how breeze updates EF
Any Help greatly appreciated :)
Regards,
Mike

Updating and Deleting Associated Records in a One-To-Many Relationship using Entity Framework

Using EF 4.1 Code First, I have a Member entity and it in turn has two "one-to-many" relationships for a HomeAddress and WorkAddress. It also has a boolean property to state whether or not to use either of these addresses.
I have two issues that I can't figure out:
Whenever I update a member's address, a new record is added to the MemberAddresses table (with a new ID value) and the existing record is not deleted. Though it looks fine from the front-end perspective as the HomeAddressId and WorkAddressId in the parent Members table is updated with the new record, the old records are kept in the table (orhpaned). I don't want it to add a new address record when the address is updated. I only want it to update the existing record. If it has to add a new one, then I at least want it to clear out the old one.
There are times that I want to delete the address record from the table. For example, if the member previously had an associated HomeAddress and later the DontUseHomeAddress is set to true, I want the address to be deleted from the table. So far, I have tried setting it to null, but that just prevents any updates. It doesn't delete it.
I'm sure there just some code piece I'm missing, so I'm including all the relevant code below that I think might affect this.
public abstract class Entity
{
public int Id { get; set; }
}
public class Member : Entity
{
public string Name { get; set; }
public bool DontUseHomeAddress { get; set; }
public virtual MemberAddress HomeAddress { get; set; }
public bool DontUseWorkAddress { get; set; }
public virtual MemberAddress WorkAddress { get; set; }
//... other properties here ...
}
public class MemberMap : EntityTypeConfiguration<Member>
{
public MemberMap()
{
ToTable("Members");
Property(m => m.Name).IsRequired().HasMaxLength(50);
//TODO: Somehow this is creating new records in the MemberAddress table instead of updating existing ones
HasOptional(m => m.HomeAddress).WithMany().Map(a => a.MapKey("HomeAddressId"));
HasOptional(m => m.WorkAddress).WithMany().Map(a => a.MapKey("WorkAddressId"));
}
}
public class MemberAddressMap : EntityTypeConfiguration<MemberAddress>
{
public MemberAddressMap()
{
ToTable("MemberAddresses");
Property(x => x.StreetAddress).IsRequired().HasMaxLength(255);
Property(x => x.City).IsRequired().HasMaxLength(50);
Property(x => x.State).IsRequired().HasMaxLength(2);
Property(x => x.ZipCode).IsRequired().HasMaxLength(5);
}
}
Here is the InsertOrUpdate method from my repository class that my controller calls:
public class Repository<TEntity> : IRepository<TEntity> where TEntity : Entity
{
private readonly EfDbContext _context;
private readonly DbSet<TEntity> _dbSet;
public Repository(EfDbContext context)
{
_context = context;
_dbSet = _context.Set<TEntity>();
}
public bool InsertOrUpdate(TEntity entity)
{
if(entity.Id == 0)
{
_dbSet.Add(entity);
}
else
{
_context.Entry(entity).State = EntityState.Modified;
}
_context.SaveChanges();
return true;
}
//... Other repository methods here ...
}
EDIT: Adding in code for UnitOfWork and MemberServices
public class MemberServices : IMemberServices
{
private readonly IUnitOfWork _unitOfWork;
private readonly IRepository _memberRepository;
public MemberServices(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_memberRepository = unitOfWork.RepositoryFor<Member>();
}
public Member Find(int id)
{
return _memberRepository.FindById(id);
}
public bool InsertOrUpdate(Member member)
{
// if(member.HomeAddress != null)
// _unitOfWork.SetContextState(member.HomeAddress, EntityState.Modified);
//
// if(member.WorkAddress != null)
// _unitOfWork.SetContextState(member.WorkAddress, EntityState.Modified);
//
// if(member.DontUseHomeAddress)
// {
// //TODO: This is an attempted hack... fix it by moving somewhere (possibly to repository)
// var context = new EfDbContext();
// context.Set<MemberAddress>().Remove(member.HomeAddress);
// context.SaveChanges();
// }
_memberRepository.InsertOrUpdate(member);
return true;
}
}
public class UnitOfWork : IUnitOfWork
{
private readonly EfDbContext _context;
public UnitOfWork()
{
_context = new EfDbContext();
}
public IRepository<T> RepositoryFor<T>() where T : Entity
{
return new Repository<T>(_context);
}
public void Attach(Entity entity)
{
_context.Entry(entity).State = EntityState.Unchanged;
}
public void SetContextState(Entity entity, EntityState state)
{
_context.Entry(entity).State = state;
}
public void Save()
{
_context.SaveChanges();
}
}
Setting the state _context.Entry(entity).State = EntityState.Modified; doesn't affect the state of related entities. If you want to take care of changes of your related entities you must set their state to Modified as well:
if (member.HomeAddress != null)
_context.Entry(member.HomeAddress).State = EntityState.Modified;
if (member.WorkAddress != null)
_context.Entry(member.WorkAddress).State = EntityState.Modified;
_context.Entry(member).State = EntityState.Modified;
This is not generic anymore.
To delete an entity you have to call the appropriate method to delete an entity; setting the navigation property to null is not enough:
_context.MemberAddresses.Remove(member.HomeAddress);

Entity Framework Generic Repository

I am writing a generic repository to be used for my every model CRUD operation using entity framework CTP5 as following:
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : BaseEntity
{
public DbContext Context { get; set; }
public void Insert(TEntity entity)
{
if (Context.Entry<TEntity>(entity).State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Set<TEntity>().Add(entity);
Context.SaveChanges();
}
public void Delete(int id)
{
TEntity entity = Context.Set<TEntity>().Find(id);
if (Context.Entry<TEntity>(entity).State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Set<TEntity>().Remove(entity);
Context.SaveChanges();
}
public void Delete(TEntity entity)
{
Context.Set<TEntity>().Remove(entity);
Context.SaveChanges();
}
public void Update(TEntity entity)
{
TEntity status = Context.Set<TEntity>().Find(entity.Id);
status = entity;
Context.SaveChanges();
}
public TEntity GetFirst()
{
var entity = Context.Set<TEntity>().FirstOrDefault();
if (entity == null) return null;
return entity;
}
public TEntity GetNext(int id)
{
var entity = (from u in Context.Set<TEntity>()
where u.Id > id
select u).FirstOrDefault();
if (entity == null) return null;
return entity;
}
public TEntity GetPrevoius(int id)
{
var entity = (from u in Context.Set<TEntity>()
where u.Id < id
orderby u.Id descending
select u).FirstOrDefault();
if (entity == null) return GetFirst();
return entity;
}
public TEntity GetLast()
{
var entity = (Context.Set<TEntity>().OrderByDescending(u => u.Id)).FirstOrDefault();
if (entity == null) return null;
return entity;
}
public TEntity GetById(int id)
{
return Context.Set<TEntity>().Find(id);
}
public int GetMaxId()
{
var max = Context.Set<TEntity>().Count()+ 1;
return max;
}
}
everything works fine but Update method which nither doesnt generate any error nor save any changes back to database.
Can anybody guid me how to solve this issue?
You can use CurrentValues.SetValues:
public void Update(TEntity entity)
{
TEntity status = Context.Set<TEntity>().Find(entity.Id);
Context.Entry(status).CurrentValues.SetValues(entity);
Context.SaveChanges();
}
It updates scalar and complex properties but not navigation properties.
You're overwriting the variable status with a totally new object, taking the one from the database out of scope, but not actually modifying the object that is attached to the context, which is what you'll want to do.
The only way I can think off the top of my head is to use reflection to read all the properties of the type, and assign the values to the original object based on the new one, something like:
foreach (var prop in typeof(TEntity).GetProperties())
{
prop.SetValue(status, prop.GetValue(entity, null), null);
}

EF 4.1 Code-First and ASP MVC 3 problem when editing entities

I coded my entities with and created DbContext. Then I used MVC Scaffolding to create simple CRUD form for one entity. So far so good, it works as advertised.
Now, I decided to replace scaffolding-generated DbContext with simple Service wrapper over DbContext. All it does is delegate to DbContext.
However, now I have the problem on the following line when tried to edit the entity:
service.Entry(Book).State = EntityState.Modified;
“An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key”
I managed to resolve it like this:
PropertyInfo[] infos = typeof(Book).GetProperties();
foreach (PropertyInfo info in infos)
{
info.SetValue(internalBook, info.GetValue(book, null), null);
}
Basically, I get the entity again and copy properties from entity that was handed to me by View. I have also noted that when I obtain the entity, it is proxy, and the one handed to me is not.
What could be the problem?
Here is my service class:
public class BookService
{
private DbContext context;
private DbSet<Book> set;
public BookService(DbContext context, DbSet<Book> set) {
this.context = context;
this.set = set;
}
public IQueryable<Book> Query
{
get { return set; }
}
public virtual void Add(Book entity)
{
set.Add(entity);
}
public virtual void Remove(Book entity)
{
set.Remove(entity);
}
public virtual void SaveChanges() {
context.SaveChanges();
}
public List<Book> All() {
List<Book> books = set.ToList();
return books;
}
public DbEntityEntry<Book> Entry(Book book) {
return context.Entry(book);
}
}
Here is the Edit action Controller code. I have commented the original, scaffolding-generated code:
[HttpPost]
public ActionResult Edit(Book book)
{
Book internalBook = service.Query.Single(b => b.Id == book.Id);
if (ModelState.IsValid)
{
PropertyInfo[] infos = typeof(Book).GetProperties();
foreach (PropertyInfo info in infos)
{
info.SetValue(internalBook, info.GetValue(book, null), null);
}
service.Entry(internalBook).State = EntityState.Modified;
service.SaveChanges();
//context.Entry(book).State = EntityState.Modified;
//context.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}
Actually you don't need to query the Book, you can just use these two lines:
service.Entry(book).State = EntityState.Modified;
service.SaveChanges();
So this is the complete code:
[HttpPost]
public ActionResult Edit(Book book)
{
if (ModelState.IsValid)
{
service.Entry(book).State = EntityState.Modified;
service.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}
You can download a complete solution from this post: http://blog.jorgef.net/2011/04/ef-poco-proxies-in-mvc.html
You can't attach the book because you have loaded it in the same context. General approach is this:
[HttpPost]
public ActionResult Edit(Book book)
{
Book internalBook = service.Query.Single(b => b.Id == book.Id);
if (ModelState.IsValid)
{
service.Entry(internalBook).CurrentValues.SetValues(book);
service.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}