I have the following database tables:
Document
Country (never added to)
DocumentLanguage
DocumentLanguage is a link table between Country and Document only containing the ID's. Entity designer (correctly) adds this a a navigation property.
My code is as follows:
public void IdentifyLanguages(List<Country> languages)
{
foreach (Country c in languages)
{
foreach (var languageIdentifier in c.LanguageIdentifiers)
{
if (document.DocumentText.ToLower().Contains(languageIdentifier.LanguageIdentifier1.ToLower()))
{
document.Countries.Add(c);
break;
}
}
}
}
and later save the object:
context.Document.AddObject(_document);
context.SaveChanges();
And i get the following error:
The EntityKey property can only be set when the current value of the property is null.
There is probably a very obvious solutution, i have tried using attach, using stub objects, but there must be a simple solution as am sure i am not the first person who has had this requirment!
When you save the object using AddObject, does your document already exist in your database? AddObject is only for adding new objects, not updating existing ones
Related
I have the following
class Human
int id
string gender
List<PhysicalAttribute> attributes
class PhysicalAttributes
int id
string description
string type
List<Human> humans
When I add the first human with attributes, the tables are created and the data is populated properly.
The problem is when I add the next humans with similar attributes. Let's say I have attributes
type:"body"
description:"slim"
for both the first and second human. When I create new and add the second human, another entry with the same type and description is added to the PhysicalAttributes table.
Is there some way of doing this so that the existing entry will be used?
Do I have to do a lookup on the PhysicalAttributes table first to see that the entry has been created?
Is there some way of doing this so that the existing entry will be used?
Yes. Make (type,description) the Entity Key for PhysicalAttributes. You seem to have introduced a meaningless ID key property that allows multiple PhysicalAttributes to have the same type and description.
It is also possible to not fix the model, and fetch the existing PhysicalAttributes from the database to discover if any of the ones you need already exist. But that's just a tedious workaround for having the wrong entity key structure.
If you are loading from JSON it's going to be inconventient to attach existing entities, and instead you can do the following.
Override SaveChanges and fetch all the PhyscialAttribute values from the database. Then for the PhysicalAttribute entities that already exist in the database, Detach them from the DbContext, and EF won't attempt to insert them.
After you call PhysicalAttributes.Load(), you will have two entries in the ChangeTracker for every PhysicalAttribute on a new Entity that already exists in the database.
EG
public override int SaveChanges()
{
PhysicalAttributes.Load();
var entryGroup = from entry in ChangeTracker.Entries<PhysicalAttribute>()
group entry by new { entry.Entity.Description, entry.Entity.Type } into byKey
where byKey.Any( e => e.State == EntityState.Unchanged)
select byKey;
foreach (var eg in entryGroup)
{
foreach (var e in eg )
{
if (e.State == EntityState.Added)
{
e.State = EntityState.Detached;
}
}
}
return base.SaveChanges();
}
I'm trying to delete multiple rows from a table. But it gives the following error after the first iteration. I can see primary key Id as 0 on all the xTestType object. that might be the issue. Why is it always giving Id 0.
foreach (var temp in oldxDetails.TestTypes)
{
if (deleteTestTypes.Contains(input.Id))
{
var xTestType = new xTestType
{
xId = xId,
TestTypeMasterId = temp.Id
};
await _xTestRepository.DeleteAsync(xTestType);
}
}
Exception:
The instance of entity type 'xTestType' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.
When you fetch data from database, and iterate over it, like:
var dataObject = await dbContext.Table.where(x=>x.UserId == model.UserId).TolistAsync();
foreach(var item in dataObject)
{
}
do not create another object, pass the fetched object directly to Delete or use it to update, because DbContext is tracking the objects it has fetched, not the ones you create. for example:
//Wrong code
var dataObject = await dbContext.Table.where(x=>x.UserId == model.UserId).TolistAsync();
foreach(var item in dataObject)
{
var x=new DataObject()
{
x=item.Id
};
dbContext.Table.Remove(x);
}
you must pass the originally fetched instance to Remove() method, see:
var dataObject = await dbContext.Table.where(x=>x.UserId == model.UserId).TolistAsync();
foreach(var item in dataObject)
{
dbContext.Table.Remove(item);
}
The issue exists because the entity framework is tracking xTestType when you fetched all of them. There are two approaches to handle the situation.
Approach 1:
DbContext.Entry(xTestTypeOld).State = EntityState.Deleted; // where xTestTypeOldis record from which you are taking xId
Approach 2 :
DbContext.Entry(xTestTypeOld).State = EntityState.Detached;
DbContext.Entry(xTestType).State = EntityState.Deleted;
I would say the first approach is the best one.
C# rookie. Below is my code, been trying for hours now to get this to update some fields in my DB and tried many different implementations without luck.
// Select all fields to update
using (var db = new Entities())
{
// dbFields are trusted values
var query = db.tblRecords
.Where("id == " + f.id)
.Select("new(" + string.Join(",", dbFields.Keys) + ")");
foreach (var item in query)
{
foreach (PropertyInfo property in query.ElementType.GetProperties())
{
if (dbFields.ContainsKey(property.Name))
{
// Set the value to view in debugger - should be dynamic cast eventually
var value = Convert.ToInt16(dbFields[property.Name]);
property.SetValue(item, value);
// Something like this throws error 'Object does not match target type'
// property.SetValue(query, item);
}
}
}
db.SaveChanges();
}
The above code when run does not result in any changes to the DB. Obviously this code needs a bit of cleanup but i'm trying to get the basic functionality working. I believe what I might need to do is to somehow reapply 'item' back into 'query' but I've had no luck getting that to work no matter what implementation I try i'm always receiving 'Object does not match target type'.
This semi similar issue reaffirms that but isn't very clear to me since i'm using a Dynamic LINQ query and cannot just reference the property names directly. https://stackoverflow.com/a/25898203/3333134
Entity Framework will perform updates for you on entities, not on custom results. Your tblRecords holds many entities, and this is what you want to manipulate if you want Entity Framework to help. Remove your projection (the call to Select) and the query will return the objects directly (with too many columns, yes, but we'll cover that later).
The dynamic update is performed the same way any other dynamic assignment in C# would be, since you got a normal object to work with. Entity Framework will track the changes you make and, upon calling SaveChanges, will generate and execute the corresponding SQL queries.
However, if you want to optimize and stop selecting and creating all the values in memory in the first place, even those that aren't needed, you could also perform the update from memory. If you create an object of the right type by yourself and assign the right ID, you can then use the Attach() method to add it to the current context. From that point on, any changes will be recorded by Entity Framework, and when you call SaveChanges, everything should be sent to the database :
// Select all fields to update
using (var db = new Entities())
{
// Assuming the entity contained in tblRecords is named "ObjRecord"
// Also assuming that the entity has a key named "id"
var objToUpdate = new ObjRecord { id = f.id };
// Any changes made to the object so far won't be considered by EF
// Attach the object to the context
db.tblRecords.Attach(objToUpdate);
// EF now tracks the object, any new changes will be applied
foreach (PropertyInfo property in typeof(ObjRecord).GetProperties())
{
if (dbFields.ContainsKey(property.Name))
{
// Set the value to view in debugger - should be dynamic cast eventually
var value = Convert.ToInt16(dbFields[property.Name]);
property.SetValue(objToUpdate, value);
}
}
// Will only perform an UPDATE query, no SELECT at all
db.SaveChanges();
}
When you do a SELECT NEW ... it selects only specific fields and won't track updates for you. I think if you change your query to be this it will work:
var query = db.tblRecords.Where(x=>x.id == id);
I have a wpf application with a database made with Entity Framework code first.
I have a class Shoes.cs and two classes that inherit from shoes: Canvas.cs and Boots.cs
For mapping I've used tph method, so I have a table shoes that has a discriminator for indentifing if is canvas or boots
I have to show in datagrid all the shoes I have, with their type. I know that I can't query directly the discriminator column, so I was trying to do something like this
var allShoes = (from shoes in db.Shoes
select shoes).ToList();
List<string> typeOfShoes = new List<string>();
foreach (Shoes pairShoes in allShoes)
{
if (pairShoes.GetType() == typeof(Canvas))
{
typeOfShoes .Add("Canvas");
}
else if (pairShoes.GetType() == typeof(Boots))
{
typeOfShoes .Add("Boots");
}
else
{
typeOfShoes .Add("Wrong");
}
}
I can get everything in my datagrid, but displays only "Wrong" in column type.
How can I get the type of shoes?
You are currently asking the class Shoes for its type, you should instead ask the instance for its type. In addition you should also use is. This operator checks if an object is compatible with a given type, rather than relying on the exact type.
if (pairShoes is Canvas)
{
....
Model:
Code:
public partial class frmCotizaciones : Form
{
public frmCotizaciones()
{
InitializeComponent();
using (var ctx = new AefesaEntities1())
{
dataGridView1.DataSource = ctx.Productos.ToList();
}
}
}
Error:
I'm new to Entity Framework, and I want to know how to use it, since I can see the advantages of using it. What I'm trying to do is simply bind the Productos dataset to the datagrid, but it throws that exception, I'd really appreciate your help.
The problem is that your Producto class has not only simple properties (string, int) but also navigation properties that point to other classes the producto is in relation.
In this particular case the error shows that the producto has a list of DetalleCotizacione (whatever it is). The proxy class for the producto returned from EF has a lazy loaded property for this, which means that the property is not evaluated until some code asks for it.
And now comes the tricky part. Your client code asks for a list
ctx.Productos.ToList();
The list will be populated with productos where all simple properties (string, int) are initialized and all lazy loaded properties are not.
And then you dispose the data context.
Now the data grid binder inspects (by reflection) your objects and find a lot of public properties, including these that are initialized (string, int) and these that are not - navigation properties. The grid creates columns for all your public properties and starts to populate rows. It then asks for values of all public fields of the producto and in case of navigation propeties - it fails because your context is already disposed (at the end of the using block).
There are two workarounds:
initialize the list of columns of your grid in an explicit way rather than relying on auto generation of columns. This way you could create columns for all properties that are not lazy loaded navigation properties.
project your productos to whatever anonymous type you want thus making explicit loading of simple and navigation properties inside the block where context lives:
using (var ctx = new AefesaEntities1())
{
dataGridView1.DataSource = ctx.Productos
.Select( p => new { id = p.ID, whatever = p.Whatever, lazy = p.Lazy1, etc. }
.ToList();
}
you're going to get all members of entity "Productos" including "Cotizaciones" and "Detallecotizacionnes".
try to specify the members to be assigned to your datagrid columns.
example :
using (var ctx = new AefesaEntities1())
{
dataGridView1.DataSource = ctx.Productos.select(p=>new{p.IdProducto ,p.Descripcion ,p.PrecioActual}).ToList();
}