The project I'm in is using Entity Framework 6 with code first and has central database and a local database. The central DB is the same as the Local DB with the exception that all relationships (Foreign keys, primary keys etc) have been removed.
So lets imagine that I have the following classes:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int ProductTypeId { get; set; }
public ProductType ProductType { get; set; }
}
public class ProductType
{
public int ProductTypeId { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
if the database has a foreign key in the Product table I can do
IEnumerable<Product> products = ctx.Produts.Include(x => x.ProductType);
If the datbase doesnt have the foreign key, will EF still load the ProductType object using the code above?
Without foreign key, EF will not load the ProductType object.
You will need to do a new query. Something like this:
IEnumerable<Product> products = ctx.Produts;
foreach (var product in products )
{
ProductType type = (from t in ctx.ProductType where t.ProductTypeId == product.ProductTypeId select t).Single();
product.ProductType = type;
}
I was curious about this so I mocked up a quick test, which I ran both with and then without the foreign key in the database. The answer seems to be yes, it will work, provided you have configured your keys still in Entity Framework. Note however, of course, that the database will obviously not enforce reference constraints without the keys, although it looks like EF will.
Related
I have three tables Car Makes, Car Model and CarsandModel. I have the Carsand Model table because a model could be built by multiple manufactures. I want to return a list of car makes that has a list of car models. The longic I have now is not filtering the list on car makes for the car model.
I have tried to add a where statement but I am still not getting the correct return
public class CarMake
{
public int CarMakeId { get; set; }
public string CarMakeName { get; set; }
public List<CarModel> CarModel { get; set; }
}
public class CarModel
{
public int CarModelId { get; set; }
public string CarModelName { get; set; }
}
public class CarsandModel
{
public int CarMakeId { get; set; }
public int CarModelId { get; set; }
}
var CarModel = (from cmake in db.CarModel
select new CarModel
{
CarModelId = cmake.CarMakeId,
CarModelName = cmake.CarMakeName,
CarMake= (from cmake in db.Carmake
join cam in db.CarsandModel on cmake.CarMakeId equals cam.CarMakeId
where (camodel.CarMakeId == cmake.CarMakeId)
select new Asset
{
CarMakeId = cmodel.CarMakeId,
CarMakeName = cmodel.CarMakeName
}).ToList()
}
).ToList();
When using entity framework people tend to (group-)join the tables themselves, while it is much easier to use the ICollections instead.
First of all: stick to the Entity Framework Code-First Conventions!
For example:
Every CarMake has zero or more CarModels. You decided to declare this as a List<CarModel>. Are you sure that CarModel[4] has a defined meaning? And I wonder what CarModel.Insert(4, new CarModel()) would mean in your code. Better stick to the interface that your database can really handle: ICollection<CarModel>.
The relation between CarMakes and CarModels seems to be a many-to-many: every CarMake has zero or more CarModels, Every CarModel is made by zero or more CarMakes.
In relational databases a many-to-many relationship is implemented using a junction table. Your class CarsAndModel represents a row in this junction table. However, when using entity framework you don't need to mention the junction table. When you design your classes properly, entity framework recognizes the relations between your tables and creates the proper junction table for it.
class CarMake
{
public int CarMakeId { get; set; }
public string CarMakeName { get; set; }
// every CarMake makes zero or more CarModels (many-to-many)
public virtual ICollection<CarModel> CarModels { get; set; }
}
class CarModel
{
public int CarModelId { get; set; }
public string CarModelName { get; set; }
// every CarModel is made by zero or more CarMakes (many-to-many)
public virtual ICollection<CarMake> CarMakes {get; set;}
}
In entity framework the columns of the tables are represented by non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
For completeness the DbContext:
class CarContext : DbContext
{
public DbSet<CarMake> CarMakes {get; set;}
public DbSet<CarModel> CarModels {get; set;}
}
This is all that entity framework needs to know to detect the many-to-many relationship. Entity framework will create the junction table for you and will keep this table up-to-date as needed.
Only if you have good reasons to deviate from the code-first conventions (and you can convince your project leader) you'll need attributes or fluent API to inform entity framework about your deviations.
But how am I supposed to join the tables if I can't access the junction table?
Answer: don't do a (group-)join, use the ICollections!
Requirement: Give me the CarModels, each with their CarMakes
var result = dbContext.CarModels
.Where(carModel => ...) // only if you don't want all CarModels
.Select(carModel => new
{
// Select only the properties you actually plan to use!
Id = carModel.CarModelId,
Name = carModel.CarModelName,
...
CarMakes = carModel.CarMakes
.Where(carMake => ...) // only if you don't want all CarMakes of this model
.Select(carMake => new
{
// again: select only the properties you plan to use
Id = carMake.CarMakeId,
Name = carMake.Name,
...
})
.ToList(),
}
Entity Framework knows your many-to-many relation and will create the proper (group-)join for you.
I am trying to insert an object into a database table with Entity Framework and using code first (fluent api). Whilst doing this I keep running into one of the following errors:
1) InvalidOperationException: A dependent property in a
ReferentialConstraint is mapped to a store-generated column. Column:
'Id'
2) Cannot insert value into identity column with IDENTITY_INSERT set
to OFF
My relationship is a one-to-one however perhaps I can rework or structure the database to accomplish what I am wanting. I have also thought about utilizing a one to zero or zone even though the other object will always be required.
So I have the following database tables mapped into these C# objects (with virtual for the mapping):
public class test
{
[Key]
public long Id { get; set; }
public DateTime ResultDate { get; set; }
public virtual test_additional test_additional { get; set; }
public virtual test_status test_status { get; set; }
}
public class test_additional
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to test
...
public virtual test test { get; set; }
}
public class test_status {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to Test
public long TestFormId { get; set; } //this is the object I want to insert, Foreign key to the Primary key of test_form
...
public virtual test test { get; set; }
public virtual test_form test_form { get; set; } //object mapping
}
public class test_form {
[Key]
public long Id { get; set; } //Primary Key
public string FileName { get; set; }
public virtual test_status test_status { get; set; }
}
So some pretty simple objects, I've stripped members/columns that are necessary for the functionality for ease of readability.
So there are test objects that have an optional test_additional or test_status .
These are generated with a one to zero-or-one relationship. Which are working fine and I have the relationship defined as:
modelBuilder.Entity<test>()
.HasOptional(e => e.test_additional)
.WithRequired(e =>e.test);
modelBuilder.Entity<test>()
.HasOptional(e => e.test_status)
.WithRequired(e => e.test);
Now the entity I am having trouble with is the test_form, if a test_status is defined there should always be a test_form associated with that. I currently have a relationship defined as:
modelBuilder.Entity<test_form>()
.HasRequired(e => e.test_status)
.WithRequiredDependent(e => e.test_form);
In addition I have tried appending this config:
modelBuilder.Entity<test_status>()
.HasKey(e => e.TestFormId);
--
Here is a simple implementation of inserting this object in the database:
try {
test UserTest = new test { ResultDate = DateTime.Now; }
UOW.test.Insert(UserTest);
UOW.Save();
test_additional ta = new test_additional { TestId = UserTest.Id; }
test_form tf = new test_form { FileName = "Testing.pdf"; }
UOW.test_additional.Insert( ta );
UOW.test_form.Insert( tf );
UOW.Save(); //This is where it will throw that error.
test_status status = new test_status {
TestId = UserTest.Id;
TestFormId = tf.Id;
}
UOW.test_status.Insert( status );
UOW.Save();
} catch {
throw;
}
--
I have used BreakPoints before the Unit of Work saves and I can confirm that the Id in the test_form object is the default of long which is 0. So I am not setting the Identity Column explicitly. Upon removing of test_form (in the implemented method) I can insert into the test_additional category and save with no issue.
So my question is really... are my entity relationships defined correctly? Would it be smarter to use an additional One to Zero-or-One for the test_form object? Why can I not insert this simple object into my database?
I have also thought about defining the virtual test_form object in test_status as an ICollection, then I could use .HasMany(e => e.test_form).HasForeignKey(e => e.TestFormId); so it would bind to the Foreign Key even though I would only be using 1 item for the test_status.
Opinions? Am I close?
Thanks again for taking the time to read my question!
i had your problem. just do delete your database and migration files. after do it add the new migration to create the new database.
I have a solution which uses Entity Framework to insert invoices to a database table. These invoices reference an order, which in turn also references an order item collection.
In this instance I am trying to add an order to the database, however the code is inside a new DbContext and so I need to attach the order and order items to the context, as these already exist in the database and shouldn't be re-added.
I've cut down the model properties for the sake of demonstration:
public class Invoice {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int InvoiceId { get; set; }
public string OrderNumber { get; set; }
...
public virtual List<InvoiceLineItem> LineItems { get; set; }
}
public class InvoiceLineItem {
[Key]
public int Id { get; set; }
...
public ShopifyOrderItem { get; set; }
}
public class ShopifyOrder {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
public int OrderNumber { get; set; }
...
public OrderInvoiceStatus InvoiceStatus { get; set; }
public virtual List<ShopifyOrderItem> OrderItems { get; set; }
}
public class ShopifyOrderItem {
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
...
[Required]
public virtual ShopifyOrder ShopifyOrder { get; set; }
}
In the invoice engine, I'm running the following code for each invoice to add it to the database:
ShopifyOrder order = await db.ShopifyOrders.SingleOrDefaultAsync(x => x.OrderNumber.ToString() == inv.OrderNumber);
if (order != null) {
// Attach marketplace entity to the invoice to avoid duplicate primary key exceptions
db.Marketplaces.Attach(inv.Marketplace);
db.Invoices.Add(inv);
order.InvoiceStatus = OrderInvoiceStatus.InProgress;
}
I've tried a number of methods to try and attach the states, however they all throw errors.
inv.LineItems.ForEach(li => {
db.Entry(li).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem).State = EntityState.Unchanged;
db.Entry(li.ShopifyOrderItem.ShopifyOrder).State = EntityState.Modified;
});
The above code returns the following error on save:
EntityFramework: Saving or accepting changes failed because more than one entity of type 'TorroModels.ShopifyOrder' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model.
What is the best way to attach the LineItems/ShopifyOrderItems without trying to attach the ShopifyOrder connected property multiple times?
Sorry to say but it seems that you need to follow the best practice first when constructing a relationship. You may follow this link :
http://www.entityframeworktutorial.net/entity-relationships.aspx
In short :
Avoid using only "Id" in every entity, or you can use attributes to map between the physical name and the property name
It seems that you have circular references here, so maybe you could simplify it first
Next, you can read this link :
http://www.entityframeworktutorial.net/EntityFramework5/attach-disconnected-entity-graph.aspx
if you need to know more about what's the best practice of attaching entities, but in my opinion, just don't abuse this feature, because using normal CRUD should be sufficient most of the time.
I'm sorry I cannot help you more than this, because of lack of information I may need, and with my reputation I still cannot comment directly in your post to ask for it.
I am using SQLite-Net PCL together with SQLite-Net extensions for the development of an application using Xamarin.
In my model I have an entity (let's call it A) which is connected to other four entities through one-to-many relationships (that are represented as lists in the model). In order to populate the tables recursively when inserting an object of A in the database I have defined the relations to use Cascade on both read, insert and delete.
In order to test if I did everything correctly I created an object of type A and populated the including lists, and finally I have inserted it into the database. The strange thing is that, for 2 of the 4 including lists the insertion went well, and all the connected objects are inserted. For other 2, instead, only the first object of the list is inserted in the database. To be clear, I am checking the database content directly with a db browser.
The following is an example of one of the objects for which only the first element of the list is inserted.
public class Username : Entity
{
public string Name
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
public Username(string username)
{
Name = username;
}
}
This is instead one of the objects for which the insertion is correct.
public class AnAddress: Entity
{
public string Address
{
get;
set;
}
public AddressType Type
{
get;
set;
}
[ForeignKey(typeof(A))]
public int AId
{
get;
set;
}
}
To be clear, the base object Entity contains the definition of the primary key:
public abstract class Entity
{
[PrimaryKey, AutoIncrement]
public int Id
{
get;
set;
}
public Entity()
{
Id = -1;
}
}
And this is the way the relationships are defined:
public class A : Entity
{
public string Name
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<AnAddress> Addresses
{
get;
set;
}
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Username> Usernames
{
get;
set;
}
}
I then create an A object by initialising it with two lists (List and List) in the same identical way.
I finally insert the object in the database with
c.InsertWithChildren(entity, recursive: true));
Where entity is of type A and c is the connection object.
Do you have any clue about the motivation of this strange behaviour?
I am trying to use Code First to create an SQL CE 4 database. When running the sample code below, Entity Framework is inserting new records for product each time, even though the data is exactly the same. What do I need to do to make Entity Framework not create duplicate associated products? The values in the ForeignID1 and the Product object are values that already exist in the database, but Entity Framework is wiping the ID I give it and adding a new ID.
namespace MyApp.Model
{
public class MyThing
{
public int ID { get; set; }
[ForeignKey("Product")]
public int ForeignID1{ get; set; }
public virtual Product Product { get; set; }
}
}
// Data.DataManager.cs
public class DataManager : DbContext
{
public DbSet<Model.MyThing> Things{ get; set; }
public DbSet<Model.Product> Products { get; set; }
}
These are the values it has entered. There should only be one value in the table that is referenced by multiple MyThings's
In order to avoid the duplication you must attach the related entity to the context:
context.Products.Attach(myThing.Product);
context.Things.Add(myThing);
Or...
myThing.Product = null;
context.Things.Add(myThing);
...will work as well if you have set myThing.ForeignID1 to an existing Product ID.