I have an solution with 2 projects, one domain class and one webUI.
In the domain class I have 2 models, a db context and a databas initializer.
List.cs:
namespace Todo.Domain
{
public class List
{
public int ListID { get; set; }
public string Day { get; set; }
public ICollection<Task> Tasks { get; set; }
}
}
Task.cs:
namespace Todo.Domain
{
public class Task
{
public int TaskID { get; set; }
public int ListID { get; set; }
public string TodoTask { get; set; }
}
}
EFDbContext.cs:
namespace Todo.Domain
{
public class EFDbContext : DbContext
{
public EFDbContext() : base("TodoList") { }
public DbSet<List> Lists { get; set; }
public DbSet<Task> Tasks { get; set; }
}
}
Initializer:
namespace Todo.Domain
{
public class TodoDbInit : System.Data.Entity.DropCreateDatabaseIfModelChanges<EFDbContext>
{
protected override void Seed(EFDbContext context)
{
var list = new List<List>
{
new List { Day="MÃ¥ndag" }
};
list.ForEach(s => context.Lists.Add(s));
context.SaveChanges();
var task = new List<Task>
{
new Task { TodoTask="Fisk" }
};
task.ForEach(s => context.Tasks.Add(s));
context.SaveChanges();
}
}
}
Now, when I start my application, I want the database to be created. I have placed a setInitializer Global.asax:
namespace Todo.WebUI
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
Database.SetInitializer(new TodoDbInit());
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
}
When I run my application, the database Is not created. I don't know why. Have I done something wrong?
You must make a call against your database in one of your controllers in order for it to be created (creation is on-demand). If you wish to manually create the database on application start see the answer posted here: Entity Framework code first, isn't creating the database
The following code is only setting the initializer that you wish to use against your database.
Database.SetInitializer(new TodoDbInit());
In order for it to actually be used you will need to create and access entities of your db context.
Related
Im currently trying to implement CRUD functionality with a dbfactory and generics with microsoft EF, but while listing entries is working, making changes to the db is currently not working.
public class AbstractDataModel
{
[Key]
public Guid gid { get; set; }
}
Model
class SalesOrder : AbstractDataModel
{
public int salesOrderID { get; set; }
public int productID { get; set; }
public int customerID { get; set; }
public Guid createdBy { get; set; }
public string dateCreated { get; set; }
public string orderDate { get; set; }
public string orderStatus { get; set; }
public string dateModified { get; set; }
}
A DBCore with some other functionality besides the ones listed here, which are not relevant for the factory
public class DBCore : DbContext
{
public static string connectionString = "myConnectionStringToDb";
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString);
}
Data Service which calls factory
class SalesOrderService : DBCore
{
public DbSet<SalesOrder> SalesOrders { get; set; }
public OkObjectResult GetAllSalesOrders()
{
DBFactory factory = new DBFactory();
return new OkObjectResult(JsonConvert.SerializeObject(factory.GetAll(SalesOrders)));
}
public OkObjectResult AddSalesOrder(SalesOrder order)
{
order.gid = Guid.NewGuid();
return DBFactory.AddOne(order);
}
public OkObjectResult UpdateSalesOrder(SalesOrder order)
{
return DBFactory.UpdateOne(order);
}
public OkObjectResult DeleteSalesOrder(SalesOrder order)
{
return DBFactory.DeleteOne(order);
}
}
simple CRUD-Factory,
class DBFactory : DBCore
{
public DbSet<UserModel> Users { get; set; }
public DbSet<SalesOrder> SalesOrders { get; set; }
public List<T> GetAll<T>(DbSet<T> dbset) where T : class
{
using (this)
{
return dbset.ToList();
}
}
public static OkObjectResult AddOne<T>(T data)
{
using (DBFactory factory = new DBFactory())
{
factory.Add(data);
factory.SaveChanges();
return new OkObjectResult("Entry was sucessfully added");
}
}
public static OkObjectResult UpdateOne<T>(T data)
{
using (DBFactory factory = new DBFactory())
{
factory.Update(data);
factory.SaveChanges();
return new OkObjectResult("Entry was sucessfully updated");
}
}
public static OkObjectResult DeleteOne<T>(T data)
{
using (DBFactory factory = new DBFactory())
{
factory.Attach(data);
factory.Remove(data);
factory.SaveChanges();
return new OkObjectResult("Entry was sucessfully removed");
}
}
}
Edit: Following the advices i changed the code so it should SaveChanges for the Factory, which also contains the context as a property. But it still doesnt seem to work for all database operations except listing all entries
Editv2: Thanks for the adivces it seems i have solved that problem, but a new one appeared :D
I can now do database operations like deleting entries, but now i cant list the entries anymore because the following error occurs, although the code there didnt really change:
"Executed 'GetAllOrders' (Failed, Id=5fb95793-572a-4545-ac15-76dffaa7a0cf, Duration=74ms)
[2020-10-23T14:33:43.711] System.Private.CoreLib: Exception while executing function: GetAllOrders. Newtonsoft.Json: Self referencing loop detected for property 'Context' with type 'FicoTestApp.Models.SalesOrder'. Path '[0].ChangeTracker'."
try adding
services.AddControllers().AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
to your
startup.cs
it should to the job
I have the following entities: PushTemplate and PushTemplateMessage. One PushTemplate can have many PushTemplateMessages. I have repositories for this. All works for the create operation. But problem starts when I try to update PushTemplate and set new text for messages. Insetad of an update I see new one PushTemplateMessage. I'll show my code.
Entity PushTemplate:
public class PushTemplate
{
public int PushTemplateId { get; set; }
[Required]
public List<PushTemplateMessage> Messages { get; set; }
public DateTime CreatedAt { get; set; }
public PushTemplate()
{
CreatedAt = DateTime.UtcNow;
}
}
Entity PushTemplateMessage:
public class PushTemplateMessage
{
public int PushTemplateMessageId { get; set; }
public string PushTitle { get; set; }
public string PushMessage { get; set; }
public PushTemplate PushTemplate { get; set; }
}
Repository PushTemplateRepository:
public class PushTemplateRepository : IPushTemplateRepository
{
private readonly ApplicationDbContext _applicationContext;
public PushTemplateRepository(ApplicationDbContext applicationContext)
{
_applicationContext = applicationContext;
}
public IQueryable<PushTemplate> PushTemplates => _applicationContext.PushTemplates;
public void Save(PushTemplate pushTemplate)
{
if (pushTemplate.PushTemplateId == 0)
{
_applicationContext.PushTemplates.Add(pushTemplate);
}
else
{
PushTemplate dbEntity = _applicationContext.PushTemplates.Find(pushTemplate.PushTemplateId);
dbEntity.Messages = new List<PushTemplateMessage>();
_applicationContext.SaveChanges();
dbEntity.Messages = pushTemplate.Messages;
}
_applicationContext.SaveChanges();
}
}
Database context:
public class ApplicationDbContext : IdentityDbContext
{
private readonly string _connectionString;
public DbSet<PushTemplate> PushTemplates { get; set; }
public DbSet<PushTemplateMessage> TemplateMessages { get; set; }
public ApplicationDbContext(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("MakeAppDb");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(_connectionString, b => b.MigrationsAssembly("MakeAppPushesNet_2"));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PushTemplate>()
.HasMany(x => x.Messages)
.WithOne(y => y.PushTemplate)
.OnDelete(DeleteBehavior.Cascade);
}
}
And finally call to update operation from my controller:
PushTemplate pushTemplate = new PushTemplate
{
Messages = pushTemplateMessages // new list of messages
};
_pushTemplateRepository.Save(pushTemplate);
After this operation I have the old PushTemplateMessage and new version of PushTemplateMessage. But I need only new one! As you can see, in the repository I have tried to 'clean' old PushTemplateMessages to set then new list. But it continues to merge old data with new! Where is the mistake?
Ok, I have solved it. This post was helpful. This is from the answer.
So, if you want to synchronize (not add), even if you want set new values as NULL you have to use include("EntityName") on your db context. Now my save/update repository code is:
public void Save(PushTemplate pushTemplate)
{
if (pushTemplate.PushTemplateId == 0)
{
_applicationContext.PushTemplates.Add(pushTemplate);
}
else
{
PushTemplate dbEntity = _applicationContext.PushTemplates
.Include(x => x.Messages)
.FirstOrDefault(x => x.PushTemplateId == pushTemplate.PushTemplateId);
dbEntity.Messages = pushTemplate.Messages;
}
_applicationContext.SaveChanges();
}
As you can see I just use Include(). And now I can set null or synchronize it with a new list of values.
I am trying to create a simple Asp.NET MVC database where a user can create an account, create categories for recipes, and then enter their recipe and file them into the category of their choosing. However, when I attempt to run the test to see if I can reach my list of categories(where I can also add a category), I get the following error message:
InvalidOperationException: Unable to resolve service for type 'LC101Project2017.Data.RecipeDbContext' while attempting to activate 'LC101Project2017.Controllers.CategoryController'.
I'm new to C# and am completely confused as to what I'm doing wrong. Here are my codes:
Controller: (CategoryController.cs)
public class CategoryController : Controller
{
private readonly RecipeDbContext context;
public CategoryController(RecipeDbContext dbContext)
{
context = dbContext;
}
public IActionResult Index()
{
List<RecipeCategory> categories = context.Categories.ToList();
return View(categories);
}
public IActionResult Add()
{
AddCategoryViewModel addCategoryViewModel = new AddCategoryViewModel();
return View(addCategoryViewModel);
}
[HttpPost]
public IActionResult Add(AddCategoryViewModel addCategoryViewModel)
{
if (ModelState.IsValid)
{
RecipeCategory newCategory = new RecipeCategory
{
Name = addCategoryViewModel.Name
};
context.Categories.Add(newCategory);
context.SaveChanges();
CategoryController: return Redirect("/Category");
};
return View(addCategoryViewModel);
}
}
Database (RecipeDbContext.cs)
public class RecipeDbContext : DbContext
{
public DbSet<Recipe> Recipes { get; set; }
public DbSet<RecipeCategory> Categories { get; set; }
}
MODEL (RecipeCategory.cs)
public class RecipeCategory
{
public int ID { get; set; }
public string Name { get; set; }
public IList<RecipeCategory> RecipeCategories { get; set; }
}
Check if you have configured to use your DbContext, RecipeDbContext, inside ConfigureServices method in Startup.cs
The method should look like this;
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<RecipeDbContext >(options =>
options.UseSqlServer(Configuration.GetConnectionString("ConnectionStringName")));
}
Updated the answer in response to the comment by #Mel Mason
You need to declare a constructor that accepts DbContextOptions<RecipeDbContext>.
public class RecipeDbContext : DbContext
{
public DbSet<Recipe> Recipes { get; set; }
public DbSet<RecipeCategory> Categories { get; set; }
public RecipeDbContext(DbContextOptions<RecipeDbContext> options)
: base(options)
{ }
}
You can also check the official documentation:
https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-strings
Hope this helps.
I have a problem when adding new values with a many to many mapping in Entity Framework. I know about the unit of work pattern but in our solution we would like to keep a simple repository pattern and not a unit of work class that contains everything. Is this possible or should I just implement Unit of Work right away?
If I don't use iSupplierRepository below a supplier will be added, but it will always add a new one even though there already exists one with that name.
Error:
The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects.
Repository example:
public class SupplierRepository : IntEntityRepository<Supplier, DbContext>, ISupplierRepository
{
public SupplierRepository(DbContext context) : base(context, context.Suppliers)
{
}
}
Inherited repositories:
public class IntEntityRepository<TEntity, TContext> : EntityRepository<TEntity, TContext, int>
where TEntity : class, IEntity<int>
where TContext : BaseIdentityDbContext
{
public IntEntityRepository(TContext context, IDbSet<TEntity> set) : base(context, set)
{
}
public override async Task<TEntity> GetAsync(int id)
{
return (await GetAsync(entity => entity.Id == id)).SingleOrDefault();
}
...
public abstract class EntityRepository<TEntity, TContext, TId> : IEntityRepository<TEntity, TId>
where TEntity : class, IEntity<TId>
where TContext : BaseIdentityDbContext
{
protected TContext Context { get; }
protected IDbSet<TEntity> Set { get; }
protected EntityRepository(TContext context, IDbSet<TEntity> set)
{
Context = context;
Set = set;
}
public abstract Task<TEntity> GetAsync(TId id);
...
Unity:
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
Controller:
private readonly IContactRepository iContactRepository;
private readonly ISupplierRepository iSupplierRepository;
public ContactsController(IContactRepository iContactRepository, ISupplierRepository iSupplierRepository)
{
this.iContactRepository = iContactRepository;
this.iSupplierRepository = iSupplierRepository;
}
[HttpPut]
[Route("UpdateContact/{id}")]
public async Task<IHttpActionResult> UpdateContact(ContactViewModel contactVm, int id)
{
try
{
var supplierList = new List<Supplier>();
foreach (var contactVmSupplier in contactVm.Suppliers)
{
var supplier = await iSupplierRepository.GetAsync(contactVmSupplier.Id);
supplierList.Add(supplier);
}
var contactOriginal = await iContactRepository.GetAsync(id);
var updatedContact = Mapper.Map<ContactViewModel, Contact>(contactVm, contactOriginal);
updatedContact.Suppliers = supplierList;
await iContactRepository.UpdateAsync(updatedContact);
return Ok();
}
catch (Exception e)
{
throw new Exception("Could not update a contact", e);
}
}
Viewmodels:
public class ContactViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SupplierViewModel> Suppliers { get; set; }
}
public class SupplierViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Models:
public class Contact : IEntity<int>
{
public Contact()
{
Suppliers = new List<Supplier>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public ICollection<Supplier> Suppliers { get; set; }
}
public class Supplier: IEntity<int>
{
public Supplier()
{
Contacts = new List<Contact>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
If you install the Unity bootstrapper for ASP.NET Web API package, a UnityHierarchicalDependencyResolver is available which will use a new child container for each IHttpController resolution effectively making all registrations with a HierarchicalLifetimeManager resolved per request so that all repository instances in a controller will use the same DbContext.
The NuGet package will also install some bootstrapping code in App_Start which uses WebActivatorEx. You can either use this approach or change to align with what you are using right now. Based on your posted code it would look something like:
public static void ConfigureUnity(HttpConfiguration config)
{
var container = new UnityContainer();
container.RegisterType<DbContext>(new HierarchicalLifetimeManager());
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
}
Solved it like this, dependency injection is from the tutorial Dependency Injection in ASP.NET Web API 2.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
App_Start -> WebApiConfig
public static void Register(HttpConfiguration config)
{
UnityConfig.ConfigureUnity(config);
...
UnityConfig:
public static void ConfigureUnity(HttpConfiguration config)
{
var context = new DbContext();
var container = new UnityContainer();
container.RegisterType<ISupplierRepository, SupplierRepository>(new InjectionConstructor(context));
container.RegisterType<IContactRepository, ContactRepository>(new InjectionConstructor(context));
config.DependencyResolver = new UnityResolver(container);
}
Update: Use Randy Levy's answer instead.
My recommendation here is not to use Repository or UoW at all. EF already has them implemented. You'll encounter a lot of issues trying to re-implement them.
As to specific issue you encounter with exception: you have to use the same DbContext for your entities. At the same time, you wouldn't like to use DbContext as Singleton and use it per-request instead. A possible solution for it might be found here.
Application_BeginRequest(...)
{
var childContainer = _container.CreateChildContainer();
HttpContext.Items["container"] = childContainer;
childContainer.RegisterType<ObjectContext, MyContext>
(new ContainerControlledLifetimeManager());
}
Application_EndRequest(...)
{
var container = HttpContext.Items["container"] as IUnityContainer
if(container != null)
container.Dispose();
}
I am using StructureMap for my dependency resolving. I am faced with an issue I don't know how to solve as the only way to make a form posting I understand will end up being without dependency injection which negate the whole essence of having StructureMap in my project.
The error is that I don't have Add method in my IdbContext Model. Below is my approach.
I have a Model named Module defined as below
public class Module
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual DateTime? DateCreated { get; set; }
public virtual string ModuleDescription { get; set; }
}
and an interface defined as below.
public interface ISolnetDataSource
{
IQueryable<Module> Modules { get; }
void Save();
}
A DbContext like below
public class CMSDB : DbContext, ISolnetDataSource
{
public CMSDB() : base("DefaultConnection")
{
}
public DbSet<Module> Modules { get; set; }
void ISolnetDataSource.Save()
{
SaveChanges();
}
IQueryable<Module> ISolnetDataSource.Modules
{
get { return Modules; }
}
}
and my controller defined as
[HttpPost]
public ActionResult Index(CreateModulesViewModels module)
{
ViewBag.ListModule = _db.Modules.ToList();
if (ModelState.IsValid)
{
var model = new CreateModulesViewModels();
var CreateModule = new Module();
CreateModule.Name = module.Name;
CreateModule.ModuleDescription = module.ModuleDescription;
CreateModule.DateCreated = DateTime.Now;
//_db.modules.Add(CreateModule);
_db.Save();
return View(model);
}
return View(module);
}
The challenge I have with this is that I could'nt do _db.modules.Add(CreateModule) (the commented line in the controller) so as to add a new record. What I'm doing wrong. I want to do this using the best approach applicable.
What about:
_db.modules.Entry(CreateModule).State = System.Data.EntityState.Added;