I use EF 3.5 and have a db with a category table. I've created a partial class to expand the class created by EF. I have CategoryId as a key in the db and it is set to Identity in the model. This is my partial class:
public partial class Category
{
public Category(string name, bool isChild)
{
this.CatName = name;
this.IsChild = isChild;
}
public bool Save()
{
try
{
using (var context = new PhonebookEntities())
{
context.AddToCategories(this);
context.SaveChanges();
}
return true;
}
catch (System.Exception)
{
return false;
}
}
}
But when I try to create a new Category object and save it..:
var category = new Category("Test", false);
category.Save();
I get this exception: "Violation of UNIQUE KEY constraint 'IX_Category'. Cannot insert duplicate key in object 'dbo.Category'.\r\nThe statement has been terminated."
I should mention that a category has a reference to itself because it can have a parent category through a nullable int which points to the categoryid to the parent category.
What is the contents of IX_Category? If it is not needed, delete it. Is there another category with "Test" as its CatName in your database? Is IX_Category a unique key constraint on the CatName field?
Related
I'm building an Asp.Net core Api project.
I have a Parent entity as Aggregate root. That parent entity has a collection of Owned types.
If instead of collection of Owned types I have collection of Entities, I would simply tell the parent to delete the entity by it's Id like this:
// Parent entity method
public void DeleteChild(int childId)
{
var existing = this._children.FirstOrDefault(x=>x.Id == childId);
...
this._children.Remove(existing);
}
How to delete the owned object in the collection of an aggregate (owned entity does not have an Id property)?
//Parent entity method
public void DeleteChild(?????????) //Don't know what to put here, a surrogate key?
{
...
}
The model is something like this:
public class Parent
{
public int Id {get;set;}
public string Name {get;set;}
public List<Child> Children {get;set;}
... //other non important props
}
public class Child
{
public string Name {get;set;}
... //other non important props
}
And the mapping:
public class ParentEntityConfiguration : IEntityTypeConfiguration<Parent>
{
public void Configure(EntityTypeBuilder<Device> builder)
{
builder.ToTable("Parents");
builder.HasKey(x => x.Id);
builder.OwnsMany<Child>("Children", cfg =>
{
cfg.ToTable("Children");
cfg.WithOwner().HasForeignKey("ParentId");
cfg.HasKey("ParentId", "Name");
cfg.HasIndex("ParentId", "Name").IsUnique();
});
}
}
So there is no "Id" propety in the Child nor the "ParentId" as they are shadowed.
The same goes for modifying the Owned object.
What is the best practice for this?
Thanks
P.S. I'm using EF Core 6.x
Edit: Given the added details in the question.
You are using a composite key for your child which is a combination between the child's name and the parentid. Since child is a owned type it will be automatically included when you retrieve the object of the parent. (If you didn't change the configuration AutoInclude)
So if you get your parent object like:
var parent = dbcontext.Parent.Where(p => p.id = idParent);
//find the one you want to eliminate by name.
var child = parent.children.First(item => item.name == "foo").value;
//remove the child from the list
parent.children.Remove(child);
//save changes
dbcontext.SaveChangesAsync();
================================================================
OLD:
You just have to find the parent first. Then you can iterate through the childrens and delete the ones that you want. Then just call the save changes and they should be removed.
Example:
// Parent entity method
public void DeleteChild(int childId, idParent)
{
var parent= dbcontext.Parent.FirstOrDefault(x=>x.Id == idparent);
for (int i = 0; i < parent.Children.length; i++)
{
if (collection[i].id = childId)
collection[i] = null;
}
dbcontext.SaveChangesAsync();
}
You need to reference the value-object by itself:
public void DeleteChild(Child child)
{
// redacted
}
I have simplified the code below to show the root of the problem. My real code is using GenericRepository and UnitOfWork pattern but I get the same exception with this simplified code too.
I am using Entity Framework 6, Code First
It uses the following POCO entities
public class Order
{
public int Id {get;set;}
public virtual List<OrderProducts> OrderProducts {get;set;}
...
}
public class Product
{
public int Id {get;set;}
...
}
public class OrderProduct
{
public int OrderId {get;set;}
public int ProductId {get;set;}
public int Quantity
public virtual Order Order { get; set; }
public virtual Product Product{ get; set; }
}
The user is able to create a new product and add it to the order products on the same screen.
//Pull an order from the database:
var existingOrder = db.Orders.FirstOrDefault(x => x.Id == inputModel.OrderId);
//Iterate the OrderProductInputModels (IMs) in the Inputmodel
foreach (var orderProductIM in inputModel.OrderProductIMs )
{
var orderProduct = existingOrder.OrderProducts.SingleOrDefault(o => o.Id == orderProductIM.Id);
//if its an existing order product (already in db)
if (orderProduct != null)
{
//just update its property values
}
//if it has been added
else
{
//we need to create a new product first
var newProduct= new Product() { <set some properties> };
orderProduct= new OrderProduct()
{
Product=newProduct,
Order=existingOrder
}
//Add the OrderProduct to the order
existingOrder.OrderProducts.Add(orderProduct);
}
db.SaveChanges();
On save changes, I get the following error.
[System.Data.Entity.Infrastructure.DbUpdateConcurrencyException] = {"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."}
Why is this?
I expected entity framework to see that the existingOrders nested properties were newly added and unattached, update the order and create the new OrderProduct and Product.
Should it not be other way around in your if clause as you are checking for null ( then only it is a new order product else update. Issue is here:
//if its an existing order product (already in db)
if (orderProduct == null)
{
//just update its property values
}
//if it has been added
else
{
When you are looping around all the OrderProducts, you are constantly updating the database but the existingOrder object is not getting refreshed. Update that or add all the objects first and then update the database.
Finally solved it by creating a test project and reverse code first engineering the database. Noticed that OrderProduct entity was not generated. On inspecting the database, the primary key was not set. Once I set the primary key in the database, the issue was resolved. Thanks for all the suggestions.
I'd like to create a generic C# class with a method that will add a row to a database using Entity Framework.
I have one table called Address. I've written the following code to add an address to the database:
public class AddressExchange
{
public int Insert(Address address)
{
using (var db = new DemoWebEntities())
{
//db.AddObject("Address", address);
db.Addresses.AddObject(address);
db.SaveChanges();
return address.Id;
}
}
}
I would like to write a generic class that will perform this operation for any entity in my EDMX. I think that it should look something like this:
public class EntityExchange<T, KeyType>
{
public KeyType Insert(T t)
{
using (var db = new DemoWebEntities())
{
// The entity set name might be wrong.
db.AddObject(typeof(T).Name, t);
// EF doesn't know what the primary key is.
return t.Id;
}
}
}
I think it may be possible to use the AddObject method to add the object to the database, but the entityset name is not necessarily the same as the type name, especially if it has been pluralized!
I also want to return the primary key to the caller, but I don't know how to tell which field contains the primary key.
I have a generic InsertOrUpdate method in a generic repository that also ensures proxies are created. (Proxies are required to support lazy loading and if you create an entity using "new", then proxies are not created). See the question here
public class RepositoryBase<T> : IRepository<T> where T : ModelBase
{
public virtual T InsertOrUpdate(T e)
{
DbSet<T> dbSet = context.Set<T>();
//Generate a proxy type to support lazy loading
T instance = dbSet.Create();
DbEntityEntry<T> entry;
if (e.GetType().Equals(instance.GetType()))
{
//The entity being added is already a proxy type that
//supports lazy loading just get the context entry
entry = context.Entry(e);
}
else
{
//The entity being added has been created using the "new" operator.
//Attach the proxy
//Need to set the ID before attaching or we get
//The property 'ID' is part of the object's key
//information and cannot be modified when we call SetValues
instance.ID = e.ID;
entry = context.Entry(instance);
dbSet.Attach(instance);
//and set it's values to those of the entity
entry.CurrentValues.SetValues(e);
e = instance;
}
entry.State = e.ID == default(int) ?
EntityState.Added :
EntityState.Modified;
return e;
}
}
public abstract class ModelBase
{
public int ID { get; set; }
}
Note that all the models inherit ModelBase so that handles the ID issue and I return the entity rather than just the ID. That is probably not strictly necessary since a reference to the entity is passed in and EF performs fixup on the ID anyway so you can always access it from the refernce passed in.
This might be reliant on a particular version on Entity framework however this is how I do it
public void Create(T entity)
{
using (var db = new DemoWebEntities())
{
db.Set<T>().Add(entity);
}
}
For the primary key issue, can you use partial classes to make your entities implement an interface, something like this:
public interface IEntity
{
Guid PrimaryKey { get; }
}
Your entity classes would then return the appropriate value:
public partial class EntityType : IEntity
{
public Guid PrimaryKey
{
get
{
return this.WhateverId; // Return the primary key
}
}
}
Then, constrain your method to only accept IEntity:
public class EntityExchange<T, KeyType> where T : IEntity
And finally return the primary key after the insert:
return t.PrimaryKey;
May be it can help you.
public T Add(T model)
{
using (BigConceptEntities entity = new BigConceptEntities())
{
entity.Set<T>().Add(model);
entity.SaveChanges();
return model;
}
}
I wrote a Silverlight MVVM (SimpleMVVM toolkit) Ria Services App with EntityFramework Model generated from exsisting DB.
At first ViewModel of Page which display list parent entities I load parents, after selecting one, I can further go to edition page, where I do menagent of child entities. Before that, I pass parent Entity by pageDataHelper - key value pair Dictionary, which it holds, to ViewModel of Edition Page, and refresh it from database.
Next, I can load by entity primary key, it's childs to ListBox. By this, i can do CRUD's of childs. Anfortunatly, when I want to update one one of them, I got:
The UPDATE statement conflicted with the FOREIGN KEY constraint
"FK_Pozycje_Kosztorysy" The conflict occurred in database
"D:...\APP_DATA\EMS.MDF", table "dbo.Kosztorysy", column 'KosID'.
Aplication makes updates in DB, but entityframework always throws exception, which I handle...
Both tables in db have set On Update/Delete Cascade.
Entities looks:
internal sealed class KosztorysMetadata
{
public int KosID { get; set; }//Primary Key
public EntityCollection<Pozycja> Pozycje { get; set; } //Childs
}
}
internal sealed class PozycjaMetadata
{
public int KosID { get; set; }// Foreign Key
public int PozID { get; set; }//Primary Key
}
}
In App.Web, functins to get entities:
public IQueryable<Kosztorys> GetKosztorysByID(int id)
{
var query = from k in this.ObjectContext.Kosztorysy
where k.KosID==id
select k;
return query;
}
public IQueryable<Pozycja> GetPozycjeGlowne(Int32 id)
{
var query = from k in this.ObjectContext.Pozycje
where k.KosID == id && (k.NadPozID == null || k.TypRMS == 0 || k.TypRMS == TypRMSBVals.Dzial)
select k;
return query;
}
In ViewModel;
class KosztorysViewModel{
public Kosztorys WybranyKosztorys
{
get { return wybranyKosztorys; }
set{ WybranyKosztorys = value;}}
private ObservableCollection<Kosztorys> kosztorysy;
public ObservableCollection<Kosztorys> Kosztorysy{}}
public void OdsiwezKosztorys()//Refresh parent
{ this.serviceAgent.PobKosztorys(WybranyKosztorys.KosID, (encje, ex) => { kosztorysOdswiezony(encje, blad); });
}
void kosztorysOdswiezony(List<Kosztorys> encje, Exception exc) //Refreshed Parent
{ Kosztorysy = new ObservableCollection<Kosztorys>(encje);
WybranyKosztorys = Kosztorysy[0];}
public void PobDzialy()
{
if (WybranyKosztorys != null)
{
this.serviceAgent.PobGlownePozycje(WybranyKosztorys.KosID, (encje, ex) => dzialyPobrane(encje, ex));
}
}
void dzialyPobrane(List<Pozycja> encje, Exception exc) //callback
{
Dzialy.Clear();
Dzialy = new ObservableCollection<Pozycja>(encje);
}}
What's wrong? I use ;EF4.3, Silverlight 5, MS SQL Server 2008R
I'd like to define an enum for EF5 to use, and a corresponding lookup table. I know EF5 now supports enums, but out-of-the-box, it seems it only supports this at the object level, and does not by default add a table for these lookup values.
For example, I have a User entity:
public class User
{
int Id { get; set; }
string Name { get; set; }
UserType UserType { get; set; }
}
And a UserType enum:
public enum UserType
{
Member = 1,
Moderator = 2,
Administrator = 3
}
I would like for database generation to create a table, something like:
create table UserType
(
Id int,
Name nvarchar(max)
)
Is this possible?
Here's a nuget package I made earlier that generates lookup tables and applies foreign keys, and keeps the lookup table rows in sync with the enum:
https://www.nuget.org/packages/ef-enum-to-lookup
Add that to your project and call the Apply method.
Documentation on github: https://github.com/timabell/ef-enum-to-lookup
It is not directly possible. EF supports enums on the same level as .NET so enum value is just named integer => enum property in class is always integer column in the database. If you want to have table as well you need to create it manually in your own database initializer together with foreign key in User and fill it with enum values.
I made some proposal on user voice to allow more complex mappings. If you find it useful you can vote for the proposal.
I wrote a little helper class, that creates a database table for the enums specified in the UserEntities class. It also creates a foreign key on the tables that referencing the enum.
So here it is:
public class EntityHelper
{
public static void Seed(DbContext context)
{
var contextProperties = context.GetType().GetProperties();
List<PropertyInfo> enumSets = contextProperties.Where(p =>IsSubclassOfRawGeneric(typeof(EnumSet<>),p.PropertyType)).ToList();
foreach (var enumType in enumSets)
{
var referencingTpyes = GetReferencingTypes(enumType, contextProperties);
CreateEnumTable(enumType, referencingTpyes, context);
}
}
private static void CreateEnumTable(PropertyInfo enumProperty, List<PropertyInfo> referencingTypes, DbContext context)
{
var enumType = enumProperty.PropertyType.GetGenericArguments()[0];
//create table
var command = string.Format(
"CREATE TABLE {0} ([Id] [int] NOT NULL,[Value] [varchar](50) NOT NULL,CONSTRAINT pk_{0}_Id PRIMARY KEY (Id));", enumType.Name);
context.Database.ExecuteSqlCommand(command);
//insert value
foreach (var enumvalue in Enum.GetValues(enumType))
{
command = string.Format("INSERT INTO {0} VALUES({1},'{2}');", enumType.Name, (int)enumvalue,
enumvalue);
context.Database.ExecuteSqlCommand(command);
}
//foreign keys
foreach (var referencingType in referencingTypes)
{
var tableType = referencingType.PropertyType.GetGenericArguments()[0];
foreach (var propertyInfo in tableType.GetProperties())
{
if (propertyInfo.PropertyType == enumType)
{
var command2 = string.Format("ALTER TABLE {0} WITH CHECK ADD CONSTRAINT [FK_{0}_{1}] FOREIGN KEY({2}) REFERENCES {1}([Id])",
tableType.Name, enumProperty.Name, propertyInfo.Name
);
context.Database.ExecuteSqlCommand(command2);
}
}
}
}
private static List<PropertyInfo> GetReferencingTypes(PropertyInfo enumProperty, IEnumerable<PropertyInfo> contextProperties)
{
var result = new List<PropertyInfo>();
var enumType = enumProperty.PropertyType.GetGenericArguments()[0];
foreach (var contextProperty in contextProperties)
{
if (IsSubclassOfRawGeneric(typeof(DbSet<>), contextProperty.PropertyType))
{
var tableType = contextProperty.PropertyType.GetGenericArguments()[0];
foreach (var propertyInfo in tableType.GetProperties())
{
if (propertyInfo.PropertyType == enumType)
result.Add(contextProperty);
}
}
}
return result;
}
private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
{
while (toCheck != null && toCheck != typeof(object))
{
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur)
{
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
public class EnumSet<T>
{
}
}
using the code:
public partial class UserEntities : DbContext{
public DbSet<User> User { get; set; }
public EntityHelper.EnumSet<UserType> UserType { get; set; }
public static void CreateDatabase(){
using (var db = new UserEntities()){
db.Database.CreateIfNotExists();
db.Database.Initialize(true);
EntityHelper.Seed(db);
}
}
}
I have created a package for it
https://www.nuget.org/packages/SSW.Data.EF.Enums/1.0.0
Use
EnumTableGenerator.Run("your object context", "assembly that contains enums");
"your object context" - is your EntityFramework DbContext
"assembly that contains enums" - an assembly that contains your enums
Call EnumTableGenerator.Run as part of your seed function. This will create tables in sql server for each Enum and populate it with correct data.
I have included this answer as I've made some additional changes from #HerrKater
I made a small addition to Herr Kater's Answer (also based on Tim Abell's comment). The update is to use a method to get the enum value from the DisplayName Attribute if exists else split the PascalCase enum value.
private static string GetDisplayValue(object value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0)
? descriptionAttributes[0].Name
: System.Text.RegularExpressions.Regex.Replace(value.ToString(), "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 ");
}
Update Herr Katers example to call the method:
command = string.Format("INSERT INTO {0} VALUES({1},'{2}');", enumType.Name, (int)enumvalue,
GetDisplayValue(enumvalue));
Enum Example
public enum PaymentMethod
{
[Display(Name = "Credit Card")]
CreditCard = 1,
[Display(Name = "Direct Debit")]
DirectDebit = 2
}
you must customize your workflow of generation
1. Copy your default template of generation TablePerTypeStrategy
Location : \Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen.
2. Add custom activity who realize your need (Workflow Foundation)
3. Modify your section Database Generation Workflow in your project EF