Invalid column name while updating table, split entity - mapping

When i try to SaveChanges() to insert a new entity, I get the following exception.
System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.UpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.SqlClient.SqlException : Invalid column name 'LD_Content'.
Invalid column name 'LD_File'.
I'm not using Code First nor Database First approach here. The database is preexisting, but the entity code is generated from custom templates.
These are the DB tables:
CREATE TABLE [dbo].[Licences](
[L_ID] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Licences] PRIMARY KEY NONCLUSTERED
(
[L_ID] ASC
))
GO
ALTER TABLE [dbo].[Licences] WITH CHECK ADD CONSTRAINT [FK_Licences_LicenceData] FOREIGN KEY([L_ID])
REFERENCES [dbo].[LicenceData] ([LD_ID])
GO
CREATE TABLE [dbo].[LicenceData](
[LD_ID] [uniqueidentifier] NOT NULL,
[LD_Content] [varbinary](max) NOT NULL,
[LD_File] [varbinary](max) NULL,
CONSTRAINT [PK_LicenceData] PRIMARY KEY NONCLUSTERED
(
[LD_ID] ASC
))
These are the entities:
[Table("Licences")]
public class Licence
{
[Required, Display(Name="ID"), Column("L_ID")]
public Guid ID { get; set; }
public virtual LicenceFile File { get; set; }
[Required, Display(Name = "Content"), Column("LD_Content")]
public virtual string Content { get; set; }
}
[Table("LicenceData")]
public class LicenceFile
{
[Required, Display(Name = "ID"), Column("LD_ID")]
public Guid ID { get; set; }
[Display(Name = "File"), Column("LD_File")]
public byte[] File { get; set; }
}
And this is the mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Licence>()
.Map(m =>
{
m.Properties(licence => new {licence.Content});
m.ToTable("LicenceData");
});
modelBuilder.Entity<Licence>().HasRequired(i => i.File).WithRequiredPrincipal();
base.OnModelCreating(modelBuilder);
}
The entity is created with its navigation property set:
new Licence
{
Content = "content",
File = new LicenceFile()
}
Summing up, there are two tables in one-to-one relationship. One of the entities, Licence, maps to its table (Licences) and also to one of the columns of the other table (entity splitting). The second entity, LicenceFile, maps to the ramaining columns of the other table (LienceData).
What has gone wrong here?

Related

EF6 Create Code First Table with column default value

I work WITH ASP.NET MVC 5 and EF6, I used code-first method to generate a database.
Entity class:
[Table("Simple")]
public class SimpleEntity
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Column("id")]
public long Id { get; set; }
[Column("name")]
public string name { get; set; }
[Column("deleted")]
public bool deleted { get; set; }
public SimpleEntity()
{
}
}
EntityTypeConfiguration class:
public class SimpleEntityConfig : EntityTypeConfiguration<SimpleEntity>
{
protected SimpleEntity()
{
HasKey(a => a.Id);
}
}
I want this strategy to generate a table with this query:
CREATE TABLE Simple
(
id int NOT NULL,
name varchar(255) NOT NULL,
deleted bit DEFAULT 'TRUE'
);
Important for me to generate a column in the table with DEFAULT value, what is the solution?
entity.Property(t => t.deleted)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
You can create a table with column having default value by simply adding defaultValue in generated migration code.
After creating SimpleEntity Class, run Add-Migration TestSimpleEntity command to generate migration code. You will get below code in Up() method:
public partial class TestSimpleEntity : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.SimpleEntities",
c => new
{
id = c.Long(nullable: false, identity: true),
name = c.String(),
deleted = c.Boolean(nullable: false),
})
.PrimaryKey(t => t.id);
}
public override void Down()
{
DropTable("dbo.SimpleEntities");
}
}
Just add defaultValue parameter in CreateTable method for deleted property:
public partial class TestSimpleEntity : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.SimpleEntities",
c => new
{
id = c.Long(nullable: false, identity: true),
name = c.String(),
deleted = c.Boolean(nullable: false, defaultValue: true),
})
.PrimaryKey(t => t.id);
}
public override void Down()
{
DropTable("dbo.SimpleEntities");
}
}
After that, run update-database -verbose command, you will observe that EF will generate query which will contain Default value.
Below is the Table Definition from Server Explorer:
CREATE TABLE [dbo].[SimpleEntities] (
[id] BIGINT IDENTITY (1, 1) NOT NULL,
[name] NVARCHAR (MAX) NULL,
[deleted] BIT DEFAULT ((1)) NOT NULL,
CONSTRAINT [PK_dbo.SimpleEntities] PRIMARY KEY CLUSTERED ([id] ASC)
);

HtmlHelper function to populate dropdown

I am trying to populate a dropdown control in ASP.NET MVC. I have a table named City and i am getting the records from this table to populate through entity data. I have used the ViewBag to add dynamically. However it throws a compilation error.
Code
- View
<h4>City through db</h4>
#Html.DropDownList(cities, "Select City");
Controller
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
ActionFilterdbEntities db = new ActionFilterdbEntities();
var cities = db.Cities;
ViewBag.cities = new SelectList(cities, "Id", "CityName");
return View();
}
}
Model
[Table("City")]
public class clCity
{
public int Id { get; set; }
public string CityName { get; set; }
}
}
Db
CREATE TABLE [dbo].[City] (
[Id] INT NOT NULL,
[CityName] NVARCHAR (50) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
You have to construct the dropdown like this:
#Html.DropDownList("nameOfTheField", (SelectList)ViewBag.cities, "Select city");
If you are putting this dropdown in a form and lets suppose you have a model like this:
public class SearchModel
{
public int CityId { get; set; }
//etc
}
then you would set the code like below:
#Html.DropDownList("CityId", (SelectList)ViewBag.cities, "Select city");
Your View should look like:
<h4>City through db</h4>
#Html.DropDownList("YourInputId", (SelectList)ViewBag.cities, "Select City")
First argument is a string that will renter as id and name attributes of select tag.
How about like this,
#{
SelectList cities = ViewBag.cities;
}
#Html.DropDownList("YourDropDownId", cities, "Select City")
#Html.DropDownList("cities", ViewBag.cities as SelectList,"Select City")
please use above code.

Many-to-many relationship throw an error after updating to Entity Framework 6

I have "many-to-many relationship", which worked perfectly on a local machine until it was time to move to a server. During the move a runtime error was thrown. After upgrade to Entity Framework 6 it worked, but... One of the "many-to-many relationship" was broken.
Models have many properties and they looks something like this:
public partial class CartItem
{
public int id { get; set; }
public virtual ICollection<ProductOptionValue> ProductOptionValues { get; set; }
}
public class CartItemMapping : EntityTypeConfiguration<CartItem>
{
public CartItemMapping() : base()
{
this.HasMany(e => e.ProductOptionValues).WithMany(e => e.CartItems)
.Map(e => e.ToTable("CartItemOptions").MapLeftKey("productOptionValueId").MapRightKey("cartItemId"));
}
}
public class ProductOptionValue
{
public int id { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
}
public class ProductOptionValueMapping : EntityTypeConfiguration<ProductOptionValue>
{
public ProductOptionValueMapping() : base()
{
this.HasMany(e => e.CartItems).WithMany(e => e.ProductOptionValues)
.Map(e => e.ToTable("CartItemOptions").MapLeftKey("cartItemId").MapRightKey("productOptionValueId"));
}
}
When getting earlier saved CartItems, ProductOptionValues has count = 0 and there is no way to save a new one.
I found this question on another forum, and only answer was that, this relationship will not work after update at all. But maybe there is a solution?
Or maybe some idea why runtime error was thrown on server with Entity Framework 5?
EDIT
Errors:
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_CartItem_CartItemOptions". The conflict occurred in database
"database", table "dbo.CartItems", column 'id'. The statement has been
terminated.
An error occurred while saving entities that do not expose foreign key
properties for their relationships. The EntityEntries property will
return null because a single entity cannot be identified as the source
of the exception. Handling of exceptions while saving can be made
easier by exposing foreign key properties in your entity types. See the InnerException for details.

MVC EF map many-to-many relationship table

In EF, there is no object that represents the bridge table. But I need to delete/add record to the bridge table so I want to create a class that represents the bridge table
For example:
User: userID, name
Group: groupID, name
tblUserGroup: userID, groupID (bridge table)
I have the following:
public class EFDbContext : DbContext
{
public DbSet<User> User { get; set; }
public DbSet<Group> Group { get; set; }
public DbSet<GroupUser> tblGroupUser { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().ToTable("User");
modelBuilder.Entity<Group>().ToTable("Group");
modelBuilder.Entity<GroupUser>().HasKey(a => new { a.UserID, a.GroupID });
modelBuilder.Entity<Group>()
.HasMany(u => u.User)
.WithMany(g => g.Group)
.Map(m =>
{
m.MapLeftKey("GroupID");
m.MapRightKey("NetworkUserID");
m.ToTable("tblGroupUser");
});
}
}
When I want to use the bridge table
context.tblGroupUser...(do sth)
The error says
Invalid object name 'dbo.GroupUsers'.
I think its saying that it doesn't know which table GroupUser map to
But when I add
modelBuilder.Entity<GroupUser>().ToTable("tblGroupUser");
The error change to
Each EntitySet must refer to a unique schema and table.
Now it doesn't allow me to map tblGroupUser 2 times. How can I fix this problem
Other info
public class EFGroupkUser
{
private EFDbContext context = new EFDbContext();
public IQueryable<GroupUser> tblGroupUser
{
get { return context.tblGroupUser; }
}
}
If your problem is only about add/delete from the junction table, you can simply do it and you don't need to tweak mappings of the model:
// ...
User u = db.Users.Find(id);
foreach (var g in u.Groups.ToList())
db.tblUserGroups.Remove(g);
Collection<GroupUser> gusers = new Collection<GroupUser>();
foreach (var item in model.GroupUsers)
{
tblGroupUser guo = new tblGroupUser();
guo.UserID = item.UserID;
guo.GroupID = item.GroupID;
gusers.Add(guo);
}
u.Groups = gusers;
db.SaveChanges();
You can perform any add/delete you want to a junction table like the above code. If your problem is just that, let the EF does its job by conventions and remove OnModelCreating
This question may be duplicate from this question.
If you want to add a group to a specific user.
var myUser = dbContext.Users.Find(id);
var myGroup= db.Groups.Single(group => group.GroupId == groupId);
myUser.Groups.Add(myGroup);
This will insert a record to tblUserGroup table.
You can do the vise versa too.
If you want to add a user to a specific group.
myGroup.Users.Add(myUser);
But if you really want tblUserGroup table to be generated in the edmx, just add an aditional column (Let's say IsActive) to the tblUserGroup table and then update the edmx. Now you can use the tblUserGroup class.

EF4 Code First - How to properly map Splitting an Entity Across Multiple Tables

I am using EF4 CTP5 to try to persist a POCO object that is split among two tables, the link being the ContactID. When i save a contact, i want the core contact information saved in one table (Contacts), and the link to the user who owns the contact saved in another (UserToContacts). I have the custom mapping defined below, but when I SaveChanges, i get the following error:
A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
Any ideas would be greatly appreciated!
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
/// Perform Custom Mapping
modelBuilder.Entity<Contact>()
.Map(mc =>
{
mc.Properties(p => new
{
p.ContactID,
p.FirstName,
p.MiddleName,
p.LastName
});
mc.ToTable("Contacts");
})
.Map(mc =>
{
mc.Properties(p => new
{
p.ContactID,
p.UserID
});
mc.ToTable("UserToContacts");
});
}
This is a bug that EF team have fixed it in their code base after CTP5 was released. Entity splitting should only result in identity being used on one of the tables but CTP5 configures it for all tables (if you look into your tables you'll see that ContactID is configured as an identity column in both).
The workaround for now is to not using identity with table splitting at all:
public class Contact
{
[DatabaseGenerated(DatabaseGenerationOption.None)]
public int ContactID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public int UserID { get; set; }
}
Which means you are responsible to provide valid PKs when creating a new Contact object.
Just don't specify the ID, it will be added automatically
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
/// Perform Custom Mapping
modelBuilder.Entity<Contact>()
.Map(mc =>
{
mc.Properties(p => new
{
p.FirstName,
p.MiddleName,
p.LastName
});
mc.ToTable("Contacts");
})
.Map(mc =>
{
mc.Properties(p => new
{
p.UserID
});
mc.ToTable("UserToContacts");
});
}

Resources