I am doing Entity Frameworks Database first approach, using EF 5.0, visual studio 2012 and Sql server management studio. Both Vs and ssms are express version.
I have several tables named ex. "User" or "Product", but when autogenerating the table mapping between an entity and a table, EF insists on mapping "User" to dbo.Users and "Product" to dbo.Products.
I have tried setting tools->options->databasedesigner->O/R designer->Pluralization of names to false.
When working with the *.edmx file in the EF designer in VS2012, I have also tried to set "Pluralize New Objects" to false.
In the EF Designer it correctly shows that "User" maps to the "User" table, but when I inspect my dbcontext object in debugging mode during runtime and finds the sql it uses to access the tables it access dbo.Users.
If I were using Code first approach this seems to be fixable by "modelBuilder.Conventions.Remove();" in the ModelBuilder, but how is this problem solved using the Database first approach?
I should probably say that I use a IDbcontext for testing purpose
using System.Data.Entity.Infrastructure;
namespace XX.Core
{
public interface IDbContext : IDisposable
{
IQueryable<T> Query<T>() where T : class;
void Add<T>(T entity) where T : class;
void Delete<T>(T entity) where T : class;
void Update<T>(T entity) where T : class;
Boolean Any<T>() where T : class;
int SaveChanges();
void Dispose();
}
}
and my actual dbcontext when not testing
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
namespace XX.Core.Model
{
public class XXDb : DbContext, IDbContext
{
public XXDb()
: base("name=XX")
{
}
public DbSet<User> Users { get; set; }
public DbSet<YY> Products { get; set; }
public DbSet<Offer> YY { get; set; }
public DbSet<ZZ> ZZ { get; set; }
IQueryable<T> IDbContext.Query<T>()
{
return Set<T>();
}
void IDbContext.Add<T>(T entity)
{
Set<T>().Add(entity);
}
void IDbContext.Delete<T>(T entity)
{
Set<T>().Remove(entity);
}
void IDbContext.Update<T>(T entity)
{
Set<T>().Attach(entity);
SaveChanges();
}
Boolean IDbContext.Any<T>()
{
return Set<T>().Any();
}
}
}
Related
I am working with a database where I have more than 75 tables and I am using the repository and unit of work patterns with Entity Framework in an ASP.NET MVC project. I am little bit confused and some query in my mind about object creation. When UnitOfWork initializes, it creates object for all table's entity which is present in UnitOfWork. So it can be heavy for application load.
Here is the interface of unit of work:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Application.Repository;
using Application.Repository.General;
namespace Application.UnitOfWorks
{
public interface IUnitOfWork : IDisposable
{
IGeneralRegionMasterRepository GeneralRegionMasters { get; }
IGeneralSubRegionMasterRepository GeneralSubRegionMasters { get; }
IGeneralCountryMasterRepository GeneralCountryMasters { get; }
IGeneralStateMasterRepository GeneralStateMasters { get; }
IGeneralCityMasterRepository GeneralCityMasters { get; }
int Complete();
}
}
Implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Application.EntityFramework;
using Application.Repository;
using Application.Repository.General;
namespace Application.UnitOfWorks
{
public class UnitOfWork : IUnitOfWork
{
public readonly InventoryDbContext _context;
public UnitOfWork(InventoryDbContext context)
{
_context = context;
GeneralRegionMasters = new GeneralRegionMasterRepository(_context);
GeneralSubRegionMasters = new GeneralSubRegionMasterRepository(_context);
GeneralCountryMasters = new GeneralCountryMasterRepository(_context);
GeneralStateMasters = new GeneralStateMasterRepository(_context);
GeneralCityMasters = new GeneralCityMasterRepository(_context);
}
public IGeneralRegionMasterRepository GeneralRegionMasters { get; private set; }
public IGeneralSubRegionMasterRepository GeneralSubRegionMasters { get; private set; }
public IGeneralCountryMasterRepository GeneralCountryMasters { get; private set; }
public IGeneralStateMasterRepository GeneralStateMasters { get; private set; }
public IGeneralCityMasterRepository GeneralCityMasters { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
}
I want to know about performance effect of it on application. Thank you in advance for help.
I've run into the same problem that you are describing in the past. The structure of the code just feels really heavy since you are creating new instances of 70 repositories even though you may only need one of them. This is why I've just started to avoid adding my own UoW and Repositories when using EF directly because EF already has Repositories and UoW built in (DbSets = Repos, Save Changes does UoW save at the end of all DbSet changes). If you don't want to code directly against a DbContext, just have your DbContext implement the IUnitOfWork interface directly and go off of that. Also have all your DbSets exposed on that UnitOfWork. Then you could have it also implement IMyDbContext and have that expose the DbSets and have this interface also implement IUnitOfWork (or have DbContext -> IMyDbContext -> IUnitOfWork) or break them up if you don't want repo code having access to Save at the bottom. This just ends up making it easier in the long run. No weird code to maintain, no classes to create. If you switch to not use EF, you can still use those same interfaces behind the scenes and the only thing that would have to change would be the DbSet implementation (maybe you can even get that to be generic - create your on DbSets that implement another interface, too). Personally, I'm going down the CQS path so I don't have to worry about repos or UoW anymore. :)
Edit
Example the best I can here! :)
public interface IUnitOfWork
{
int Complete();
Task<int> CompleteAsync();
}
public interface IInventoryDbContext : IUnitOfWork
{
DbSet<GeneralRegionMaster> GeneralRegionMasters { get; }
DbSet<GeneralSubRegionMaster> GeneralSubRegionMasters { get; }
... etc
}
public class MyDbContext : DbContext, IInventoryDbContext
{
public DbSet<GeneralRegionMaster> GeneralRegionMasters { get; set; }
public DbSet<GeneralSubRegionMaster> GeneralSubRegionMasters { get; set;
}
public int Complete() => this.SaveChanges();
public Task<int> CompleteAsync() => this.SaveChangesAsync();
}
If you did a controller level only:
public class MyController : Controller
{
private readonly IInventoryDbContext _context;
public MyController(IInventoryDbContext context)
{
_context = context;
}
public JsonResult CreateGeneralRegionMaster(GeneralRegionMaster entity)
{
_context.GeneralRegionMaster.Add(entity);
var result = _context.Complete();
return Json(result == 1);
}
}
Again, you could do something different for the DbSets and do this instead:
public interface IRepo<T> where T: class
{
// Expose whatever methods you want here
}
public class MyDbSet<T> : DbSet<T>, IRepo<T> where T: class
{
}
Then this changes:
public interface IInventoryDbContext : IUnitOfWork
{
IRepo<GeneralRegionMaster> GeneralRegionMasters { get; }
IRepo<GeneralSubRegionMaster> GeneralSubRegionMasters { get; }
... etc
}
public class MyDbContext : DbContext, IInventoryDbContext
{
public MyDbSet<GeneralRegionMaster> GeneralRegionMasters { get; set; }
public MyDbSet<GeneralSubRegionMaster> GeneralSubRegionMasters { get; set; }
public IRepo<GeneralRegionMaster> GeneralRegionMastersRepo => GeneralRegionMasters;
public IRepo<GeneralSubRegionMaster> GeneralSubRegionMastersRepo => GeneralSubRegionMasters;
public int Complete() => this.SaveChanges();
public Task<int> CompleteAsync() => this.SaveChangesAsync();
}
Re:
When UnitOfWork initializes, it creates object for all table's entity which is present in UnitOfWork. So it can be heavy for application load.
You don't need to initialize all the repo instances in the UoW constructor.
You can create them when they are required in the corresponding getters (lazy initialization):
private IGeneralRegionMasterRepository _generalRegionMasters;
public IGeneralRegionMasterRepository GeneralRegionMasters {
get {
if (_generalRegionMasters == null) {
_generalRegionMasters = new GeneralRegionMasterRepository(_context);
}
return _generalRegionMasters;
}
}
In an MVC Project, I have a Page class and a Container class. I'm intending for each to have their own table in the database, so for each page, there will be a list of containers to choose from. A Page can have multiple containers and a single container can be used on multiple pages.
My problem is, I don't want anything to be stored in the Container class regarding the page it is on. A Page contains the the Containers but from the Container class, the page is is found on is irrelevant.
All of the Entity Framework examples I have seen for this type of relationship seem to store a foreign key on both classes.
public class Page
{
public int Id { get; set; }
public virtual ICollection<Container> Containers {get;set;}
...
}
public class Container
{
public int Id { get; set; }
public virtual ICollection<Page> Pages{get;set;}
...
}
Any help would be greatly appreciated. I've been stuck trying to find the correct way to do this for a few days.
Thanks.
I don't want anything to be stored in the Container class regarding the page it
is on.
This is simply a difference between OO modeling and relational modeling. In a pure OO model you can have a property of type
class Foo
{
List<Bar> Bars = new List<Bar>();
}
And the Bar objects don't have any knowledge about the Foo object. But the relational model simply doesn't work like that. Relationships are all implemented with foreign keys. So in the database foreign keys are required. And while EF supports having an entity without a foreign key property, it's discouraged.
However in your case this is a Many-to-Many relationship. And in the relational model that uses a linking table, and the foreign key columns are on the linking table, not on the entity tables. The only thing on the entity is a Navigation Property, which is not structural, and can be omitted. You just need to tell EF the cardinality of the relationship, since in your current model the relationship is defined by convention based on the Navigation Properties.
So something like
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Configuration;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ef6test
{
public class Page
{
public int Id { get; set; }
public virtual ICollection<Container> Containers { get; set; }
}
public class Container
{
public int Id { get; set; }
// public virtual ICollection<Page> Pages { get; set; }
}
public class Db : DbContext
{
public DbSet<Page> Pages { get; set; }
public DbSet<Container> Containers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Page>()
.HasMany(p => p.Containers)
.WithMany();
}
}
class Program
{
static void Main(string[] args)
{
using (var db = new Db())
{
db.Database.Create();
}
}
}
}
David
I want to implement a change log as advised in
Dev Express XAF T474899
I am using the security system generated by the XAF new solution wizard
I have defined some business objects to store the change log information.
One of these objects stores a link to the user
public virtual User User { get; set; }
On generating the code migration I am surprised to see the Up() method add the following
RenameTable(name: "dbo.UserRoles", newName: "RoleUsers");
DropPrimaryKey("dbo.RoleUsers");
AddPrimaryKey("dbo.RoleUsers", new[] { "Role_ID", "User_ID" });
On another occasion I found the following in an Up()
RenameTable(name: "dbo.EventResources", newName: "ResourceEvents");
// lots of other stuff
DropPrimaryKey("dbo.ResourceEvents");
AddPrimaryKey("dbo.ResourceEvents", new[] { "Resource_Key", "Event_ID" });
On both occasions the code that creates the entities is a Dev Express libary.
I have cross posted this question to Dev Express Support
The Dev Express business objects are defined in DevExpress.Persistent.BaseImpl.EF;
My DbContext context refers to them as
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
The meta data for Role shows
The meta data for User shows
My own business classes contain
namespace SBD.JobTalk.Module.BusinessObjects
{
[NavigationItem("Configuration")]
[DisplayName("Staff")]
[DefaultProperty("Summary")]
[ImageName("BO_Employee")]
[Table("Staff")]
public class Staff : BasicBo
{
public Staff()
{
Person = new Person();
}
public virtual Person Person { get; set; }
[StringLength(100, ErrorMessage = "The field cannot exceed 100 characters. ")]
[scds.Index("IX_Staff_UserName", 1, IsUnique = true)]
public string UserName { get; set; }
[NotMapped]
public string Summary => $"{Person.FirstName} {Person.LastName}";
//public virtual User User { get; set; }
}
}
public abstract class BasicBo : IXafEntityObject
{
[Browsable(false)]
[Key]
public virtual int Id { get; set; }
public virtual void OnCreated()
{
}
public virtual void OnSaving()
{
}
public virtual void OnLoaded()
{
}
}
If I un-comment the code to have the User property inside Staff, and generate a migration, the migration Up is
public override void Up()
{
RenameTable(name: "dbo.UserRoles", newName: "RoleUsers");
DropPrimaryKey("dbo.RoleUsers");
AddColumn("dbo.Staff", "User_ID", c => c.Int());
AddPrimaryKey("dbo.RoleUsers", new[] { "Role_ID", "User_ID" });
CreateIndex("dbo.Staff", "User_ID");
AddForeignKey("dbo.Staff", "User_ID", "dbo.Users", "ID");
}
[Update]
Interestingly there are more Dev Express tables than I first thought.
The primary keys are Identity.
I think am using Standard Authentication created before Dev Express added the Allow/Deny ability (V16.1)
[Update]
When I create a new project with the above settings, here is the DbContext.
using System;
using System.Data;
using System.Linq;
using System.Data.Entity;
using System.Data.Common;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.ComponentModel;
using DevExpress.ExpressApp.EF.Updating;
using DevExpress.Persistent.BaseImpl.EF;
using DevExpress.Persistent.BaseImpl.EF.PermissionPolicy;
namespace XafApplication1.Module.BusinessObjects {
public class XafApplication1DbContext : DbContext {
public XafApplication1DbContext(String connectionString)
: base(connectionString) {
}
public XafApplication1DbContext(DbConnection connection)
: base(connection, false) {
}
public XafApplication1DbContext()
: base("name=ConnectionString") {
}
public DbSet<ModuleInfo> ModulesInfo { get; set; }
public DbSet<PermissionPolicyRole> Roles { get; set; }
public DbSet<PermissionPolicyTypePermissionObject> TypePermissionObjects { get; set; }
public DbSet<PermissionPolicyUser> Users { get; set; }
public DbSet<ModelDifference> ModelDifferences { get; set; }
public DbSet<ModelDifferenceAspect> ModelDifferenceAspects { get; set; }
}
}
OK, I will take a stab :) Your Up() code is trying to rename the table UserRoles to RoleUsers. This means you have a prior migration where UserRoles was the table name - probably from your DevEx stuff. This could happen if they changed their models in an upgrade. The current models are expecting RoleUsers etc. so you need to get there.
So first option is let the migration do the renaming to match the underlying model. I assume this didn't work or causes other issues?
You might be able to 'fool' entity framework into using the old tables with fluent code or annotations, but if it has new columns or relationships that won't work.
What I would do is this:
1) Create a new test project with the same references you had and
copy your context and DbSets. Point the connection string to a
new database.
2) Add a migration and script it out:
update-database -Script.
3) Examine this script a use it to create
the objects needed in your database. Migrate data from the old
tables to new if needed.
4) Remove the old tables
5) In your actual
project add a migration to resync your models:
add-migration SyncDevExUpdate -IgnoreChange, update-database
Now you will have the tables your models expect.
You need to have a context class that derives from DbContext when you're using Entity Framework.
Asp.net Identity uses EF and the default template creates the below class:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
This class does not derive directly from DbContext. For my own data (my classes that I want to persist to the db) should I create my own db context class?
If I want to do an operation that will update both the identity user and one of my own classes, I need to use both contexts. So this does not feel very natural.
Should I keep using the ApplicationDbContext class as context for my own classes as well? Would that work?
What is the best method to use EF for my own classes while using identity?
Use a single Context class inheriting from IdentityDbContext. See this answer for more info.
You need to add DbSets for all your classes into the ApplicationDbContext.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", false)
{
}
//Public DBSets
public DbSet<LeaveApplication> LeaveApplications { get; set; }
public DbSet<LeaveStatus> LeaveStatus { get; set; }
public DbSet<Department> Departments { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
I have multiple models (tasks/servers/etc.) and all of these models need to support comments. I would like to have a centralized comments table that all of these models can reference (I am open to other designs though).
The comments table will have the following fields:
CommentID
RefTable
RefId
Text
I can use the following code in the task class (for example) to reference the task's comments.
IEnumerable<Comment> comments = Comment.Find(this.GetType().Name, this.TaskID)
However, I would prefer to create a HasMany mapping from tasks to comments so that I can use the following code.
this.Comments
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.ModelConfiguration;
namespace Models.Mapping
{
public class TaskMap : EntityTypeConfiguration<Task>
{
public TaskManagerItemMap()
{
// Primary Key
this.HasKey(t => t.TaskID);
...
this.Property(t => t.TaskID).HasColumnName("TaskID")
...
this.ToTable("Task");
}
}
}
using System;
using System.Collections.Generic;
namespace Models
{
public partial class Task
{
public int TaskID { get; set; }
....
public virtual IEnumerable<Comment> Comments { get; set; }
...
}
}
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.ModelConfiguration;
namespace Models.Mapping
{
public class CommentMap : EntityTypeConfiguration<Comment>
{
public CommentMap()
{
this.HasKey(t => new { t.RefID, t.RefTable });
this.ToTable("Comment");
this.Property(t => t.CommentID).HasColumnName("CommentID")
.IsRequired();
this.Property(t => t.RefID).HasColumnName("RefID")
.IsRequired();
this.Property(t => t.RefTable).HasColumnName("RefTable")
.IsRequired();
this.Property(t => t.Text).HasColumnName("Text")
.IsRequired();
}
}
}
using System;
using System.Collections.Generic;
namespace Models
{
public partial class Comment
{
public int CommentID { get; set; }
public int RefID { get; set; }
public string RefTable { get; set; }
public string Text { get; set; }
}
}
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Globalization;
using Vocus50.SiteManager.SiteManager2014.Models.Mapping;
namespace Models
{
public partial class DataContext : DbContext
{
static DataContext()
{
Database.SetInitializer<DataContext>(null);
}
public DataContext()
: base("Name=DataContext")
{
}
public DbSet<Task> Task{ get; set; }
public DbSet<Comment> Comment { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new TaskMap());
modelBuilder.Configurations.Add(new CommentMap());
}
}
}
If you use the above code, it would seem to me the issue would be in sending over the RefTable (or RefModel from above - are those supposed to be the same?) One option here (there may be many others) would be to override SaveChanges and inspect the value (or fill in) upon save.
So if you override SaveChanges (a rough method below, there are many samples on the net) you would fill in this table based on the entity type. It feels hacky, so there is probably a better solution
Is there an easy way to make EntityFramework use SQL default values?
That is of course if I understood this correctly : )
Have you looked into using Automapper. I believe you can use this for the functionality your looking for.