public ActionResult Edit(int id)
{
var productBrand = brandRepo.FindProductBrand(id);
ProductBrandModel model = Mapper.Map<ProductBrand, ProductBrandModel>(productBrand);
return View(model);
}
[HttpPost]
public ActionResult Edit(ProductBrandModel model)
{
if (ModelState.IsValid)
{
var productBrand = brandRepo.FindProductBrand(model.BrandId);
productBrand.Name = model.Name;
//How to persist that information?
}
}
I have a EF generate class ProductBrand and a model for views called ProductBrandModel.
How would I persist the information of an edit using Entity Framework? Should my brandRepo have a void method called SaveChanges where in it I would go:
public void SaveChanges()
{
dbEntities.SaveChanges();
}
As you correctly assume, you have to commit your changes to the database using the .SaveChanges() method. In your case, brandRepo.SaveChanges() would delegate to dbEntities.SaveChanges().
As a side note: In simple cases a separate repository class only introduces complexity without really providing any benefit. Entity Framework's DbContext pretty much resembles a simple repository itself, so you don't need one on top.
Of course, for the sake of testability an indirection layer might make sense.
Without the repository your code could look somewhat like this:
public ActionResult Edit(int id)
{
var productBrand = dbEntities.ProductBrands.Find(x => x.BrandId = id);
ProductBrandModel model = Mapper.Map<ProductBrand, ProductBrandModel>(productBrand);
return View(model);
}
[HttpPost]
public ActionResult Edit(ProductBrandModel model)
{
if (ModelState.IsValid)
{
var productBrand = dbEntities.ProductBrands.Find(x => x.BrandId = id);
// or something similar, I don't know the inner workings of your
// brandRepo.FindProductBrand(id)
productBrand.Name = model.Name;
dbEntities.SaveChanges();
}
}
I like to have a save method in my repository in conjunction with an entity framework helper method I got from the net. The SaveCustomer is my repository class method and below it is the helper class. In your case you would pass your model into
brandRepository.SaveProdctBrand(productBrand)
(helps to spell out the names for good naming conventions and fxcop rules)
public void SaveCustomer(Customer customer)
{
using (var ctx = new WebStoreEntities())
{
if (customer.CustomerId > 0)
{
//It's an existing record, update it.
ctx.Customers.AttachAsModified(customer);
ctx.SaveChanges();
}
else
{
//its a new record.
ctx.Customers.AddObject(customer);
ctx.SaveChanges();
}
}
}
The helper class is as follows
public static class EntityFrameworkExtensions
{
/// <summary>
/// This class allows you to attach an entity.
/// For instance, a controller method Edit(Customer customer)
/// using ctx.AttachAsModified(customer);
/// ctx.SaveChanges();
/// allows you to easily reattach this item for udpating.
/// Credit goes to: http://geekswithblogs.net/michelotti/archive/2009/11/27/attaching-modified-entities-in-ef-4.aspx
/// </summary>
public static void AttachAsModified<T>(this ObjectSet<T> objectSet, T entity) where T : class
{
objectSet.Attach(entity);
objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
}
/// <summary>
/// This marks an item for deletion, but does not currently mark child objects (relationships).
/// For those cases you must query the object, include the relationships, and then delete.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectSet"></param>
/// <param name="entity"></param>
public static void AttachAsDeleted<T>(this ObjectSet<T> objectSet, T entity) where T : class
{
objectSet.Attach(entity);
objectSet.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Deleted);
}
public static void AttachAllAsModified<T>(this ObjectSet<T> objectSet, IEnumerable<T> entities) where T : class
{
foreach (var item in entities)
{
objectSet.Attach(item);
objectSet.Context.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
}
}
}
Related
I try to learn repository.I created a class below,
public class Repository<T> where T : class
{
ObjectContext _context;
IObjectSet<T> _objectSet;
DBEntities db = new DBEntities ();
public void Delete(T entity)
{
_objectSet.DeleteObject(entity);
_context.SaveChanges();
}
}
}
I try to use void Delete in my controller below,
public ActionResult Index()
{
var something = new Repository<Department>();
something.Delete(.......); // What i must add to delete method ? How can i delete data from database ?
return View();
}
If i write int value to "something.Delete(2); it does not work, i do not know what to add here,how can i use repository ?
Try this article here:
The Repository Pattern Example in C#
MSDN ObjectContext.DeleteObject Method
I'm using MVC3 and currently i'm following a practice such that I declare one instance of DB Container for every controller. I use that container instance for every request coming to that controller. If I need to go to my models for a query or sth, I send that instance as a parameter to the model's function. So for the whole application, I create and use 4-5 different instances of DB Container class. My question is, does this have a good or bad effect on my database operations? Does it matter to create a seperate container instance? What is the proper way to use container classes?
I believe the mentioned class was called DBContext before.
I am not sure it is what you mean but I can give you an example of an approach I'm following rather often:
Create a sort of 'domainservice class' for the DBContext
public class MyDomainService : IDisposable
{
private MyDbEntities dbo;
private bool isDisposed;
public MyDomainService()
{
dbo = new MyDbEntities();
}
public User GetUser(string userName)
{
return (from usr in dbo.Users
where usr.UserName == userName
select usr).SingleOrDefault();
}
public void Dispose()
{
if (isDisposed)
return;
isDisposed = true;
dbo.Dispose();
}
}
Create a custom Controller class that extends Controller or AsyncController
and override the Initialize and Dispose methods:
public class MyController : Controller
{
protected MyDomainService DomainService { get; private set; }
protected override void Initialize(System.Web.Routing.RequestContext
requestContext)
{
base.Initialize(requestContext);
DomainService = new MyDomainService();
}
protected override void Dispose(bool disposing)
{
DomainService.Dispose();
base.Dispose(disposing);
}
}
Now you can use the following approach in per example the HomeController inheriting MyController
public class HomeController : MyController
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string username)
{
var user = DomainService.GetUser(username);
if (user != null)
return RedirectToAction("Account", "Information");
return View();
}
}
This will keep your controllers rather clean.
I use Ninject all the time with my MVC 3 applications, but I'm trying to change the Pattern for my Data Objects to use UnitOfWork and I'm having trouble figuring out how to get Ninject to handle this properly.
I know my implementation of classes work when they are constructed manually like this in my console application:
IDatabaseFactory factory = new DatabaseFactory();
IUnitOfWork worker = new UnitOfWork(factory);
IBlogCategoryDao dao = new BlogCategoryDao(factory);
IBlogCategoryService service = new BlogCategoryService(dao);
BlogCategory category = service.GetById(id);
try
{
if (category != null)
{
service.Delete(category);
worker.Commit();
Console.WriteLine("Category deleted successfully!");
}
else
{
Console.WriteLine("Entity doesn't exist.");
}
}
catch (Exception ex)
{
Console.WriteLine("Error deleting category: {0}", ex.Message);
}
In my MVC 3 application I'm using the Ninject.MVC3 NuGet package, and this is in the RegisterServices method.
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IDatabaseFactory>().To<DatabaseFactory>();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind<IBlogCategoryDao>().To<BlogCategoryDao>();
kernel.Bind<IBlogDao>().To<BlogDao>();
kernel.Bind<IBlogCategoryService>().To<BlogCategoryService>();
kernel.Bind<IBlogService>().To<BlogService>();
}
While this works for the most part, Get requests, all POST requests (Insert, Update, Delete) don't get executed. There is no exception thrown and when I step through it, it goes through the SaveChanges() method without a problem and returns back up the stack, but nothing is executed. So I know I must be missing something with my Ninject configuration.
Here's my Unit of Work class.
public class UnitOfWork : IUnitOfWork
{
private Database _database; <-- DbContext derived class
private readonly IDatabaseFactory _databaseFactory;
public UnitOfWork(IDatabaseFactory databaseFactory)
{
this._databaseFactory = databaseFactory;
}
public Database Database
{
get
{
return _database ?? (_database = _databaseFactory.Get());
}
}
public void Commit()
{
Database.Commit();
}
}
Here's the DatabaseFactory class:
public class DatabaseFactory : Disposable, IDatabaseFactory
{
private Database _database;
public DatabaseFactory()
{
}
public virtual Database Get()
{
if (_database == null)
{
_database = DataObjectFactory.CreateContext();
}
return _database;
}
protected override void DisposeCore()
{
if (_database != null)
{
_database.Dispose();
}
}
}
And my DataObjectFactory class:
public static class DataObjectFactory
{
private static readonly string _connectionString;
/// <summary>
/// Static constructor. Reads the connectionstring from web.config just once.
/// </summary>
static DataObjectFactory()
{
string connectionStringName = ConfigurationManager.AppSettings.Get("ConnectionStringName");
_connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
}
/// <summary>
/// Creates the Context using the current connectionstring.
/// </summary>
/// <remarks>
/// Gof pattern: Factory method.
/// </remarks>
/// <returns>Action Entities context.</returns>
public static Database CreateContext()
{
return new Database(_connectionString);
}
}
This is a similar pattern as used in the EFMVC CodePlex application, but I don't use AutoFac.
Any thoughts on this are appreciated.
Thanks.
I just do this:
kernel.Bind<IUnitOfWork>.To<EFUnitOfWork>().InRequestScope();
EFUnitOfWork.cs
public class EFUnitOfWork : DbContext, IUnitOfWork
{
// your normal DbContext plus your IUnitOfWork members that delegate to EF context
}
Since EF already implements a form of Unit Of Work, this allows you to use a more generic interface for it, and inject it easily.
Also, you can implement the EF constructors for connection strings and just pass them to base constructors. Then you can use the Ninject .WithConstructorArgument() to configure the connection string using your AppSettings code.
I am trying to implement an abstract repository pattern as described in THIS post. I'm getting the error message
'C' does not contain a definition for 'Set' and no extension method
'Set' accepting a first argument of type 'C' could be found (are you
missing a using directive or an assembly reference?)
where C is the DBContext
namespace Rental.Data.Entity.Repository
{
public abstract class GenericRepo<C, T> :
IGenericRepo<T> where T : class where C : RentalContainer, new()
{
private C _DBContext = new C();
protected C DBContext
{
get { return _DBContext; }
set { _DBContext = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _DBContext.Set<T>(); <-- here is gives the error
return query;
}
yet another update
public partial class RentalContainer : ObjectContext
{
#region Constructors
/// <summary>
/// Initializes a new RentalContainer object using the connection string found in the 'RentalContainer' section of the application configuration file.
/// </summary>
public RentalContainer() : base("name=RentalContainer", "RentalContainer")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// <summary>
/// Initialize a new RentalContainer object.
/// </summary>
public RentalContainer(string connectionString) : base(connectionString, "RentalContainer")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
ObjectContext does not have a Set method. It has CreateObjectSet method
public abstract class GenericRepo<C, T> : IGenericRepo<T>
where T : class
where C : RentalContainer, new()
{
private C _DBContext = new C();
protected C DBContext
{
get { return _DBContext; }
set { _DBContext = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _DBContext.CreateObjectSet<T>();
return query;
}
}
Add reference to EntityFramework.dll
http://msdn.microsoft.com/en-us/library/gg679544(v=vs.103).aspx
Press ctrl + . and use Microsoft.EntityFrameworkCore; I had to add. But I got this error when I accidentally created an empty class named DbContext.
The solution for me is to delete the empty DbContext class and add the correct using line to DbContext. (using Microsoft.EntityFrameworkCore;)
Make sure your DataContext class extends DBContext
While in search of trying to implement unique key validations for my db using EF CodeFirst/Mvc3 I came upon this post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx which gave an example on how to do it by using IValidateObject for my object model:
public class Category : IValidatableObject
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var testContext = (TestContext)validationContext.Items["Context"];
if (testContext.Categories.Any(
c => c.CategoryName == CategoryName && c.CategoryID != CategoryID))
{
yield return new ValidationResult("A category with the same name already exists!", new[] { "CategoryName" });
}
yield break;
}
}
and overriding DbEntityValidationResult ValidateEntity:
public class TestContext : DbContext
{
public DbSet<Test.Models.Category> Categories { get; set; }
protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
}
And the action on the controller
[HttpPost]
public ActionResult Create(Category category)
{
if (ModelState.IsValid) {
categoryRepository.InsertOrUpdate(category);
categoryRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
But I get the error: "The given key was not present in the dictionary." for the line
var testContext = (TestContext)validationContext.Items["Context"];
It seems like Validate on the object is getting called which accesses "Context" before its set in the override ValidateEntity code.
At first I thought it could have been ModelState.Isvalid triggering validate too early but it wasn't.
Anyone know what I'm missing here or what I'm doing wrong? Thanks in advance.
Model.IsValid definitely triggers it too early and perhaps something else. IValidatableObject is global interface used by both MVC and EF but your method in DbContext is called only when you call SaveChanges on the context so any usage of IValidatableObject prior to calling SaveChanges will result in the exception. You must use another approach if you want to validate your entity this way. For example store context in HttpContext.Items - you can create custom action filter and instantiate and store the context before the operation call and dispose it after operation call - hopefully it will cover all problems.
I was facing the same problem... Then after a lot of Googling I finally found this:
Exercise 3: Using IValidatableObject Custom Validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
MusicStoreEntities storeDB = new MusicStoreEntities();
if (storeDB.Albums.Any(
a => a.Title.Trim().ToUpper() == this.Title.Trim().ToUpper() &&
a.ArtistId == (int)this.ArtistId))
{
yield return new ValidationResult("Existing Album", new string[] { "Title" });
}
}
As you see in their example, they instantiate a new Context and as such there's no need for validationContext.Items["Context"];. Doing so we won't get this error anymore.