LINQ to SQL: Insert into Primary and Foreign table - c#

I'm trying to insert data into a primary table and a dependent table at the same time. A database relationship does exist between the two tables, and table Employee have an Identity column as the Primary Key.
I'm trying:
Database.Employee emp = new Database.Employee()
{
x = DateTime.Now,
y = "xxx"
};
Database.Skill skill = new Database.Skill()
{
Skill = "Reading"
};
emp.Skills = skill; //Error on this line
dc.Employees.InsertOnSubmit(emp);
dc.SubmitChanges();
But I get the error that:
Cannot implicitly convert type 'Database.Skill' to
'System.Data.Linq.EntitySet'

It looks like Database.Employee.Skills is a collection, not a single object. I.e. in the model it would look something like:
public class Employee
{
...
public EntitySet<Skill> Skills { get; set; } // collection of skills
}
Your line of code
emp.Skills = skill;
is trying to instantiate a collection as a single instance, so you are getting conversion error.
As such the correct way to add a skill would be something like
emp.Skills.Add(skill);
Exact implementation depends on what Database.Employee.Skills is.

Related

Does Entity Framework Database First know about unique keys set in the database?

EF6.
I have a project using EF6 Database first.
My database table had a unique key on 6 columns. All was working well in the project. I then realized the unique key was incorrect and I didn't need it so I deleted it from the database.
I have a Product which contains an ICollection<ProdPrice> ProdPrices.
Now, when I use Product product = db.Products.Find(id); I get a product but the ProdPrices collection only contains 6 items when it should contain 12. (The first 6 items are in the list).
If I delete the first item from the db and run the code again, 6 items are returned - items 2 - 7.
If I change one of the values in a column that was in the unique key that item will come though in the code.
So I'm thinking EF is somehow remembering the unique key and only returning the first items that do not conflict with the unique key.
I tried to "update model from database" in the edml file - didn't resolve the issue. So I deleted the ProdPrice table from the edml and the "update model from database" - didn't work.
So my question is - Am I correct is saying EF is remembering the deleted unique key? If yes, how do I get it to forget about it? If I am incorrect, then can you explain what is actually happening?
EDIT: SQL generated by the call to the database - standard select statement which returns all 12 records when run in SSMS (columns removed for ease of reading) :
SELECT
[Extent1].[ProdPriceID] AS [ProdPriceID],
[Extent1].[ProdID] AS [ProdID],
[Extent1].[PermanentlyDelete] AS [PermanentlyDelete],
[Extent1].[DateCreated] AS [DateCreated]
FROM [dbo].[ProdPrice] AS [Extent1]
WHERE [Extent1].[ProdID] = 67577
Here are the results so you can see I get 12 records.
Code Result:
So I decided to just request the prices for the product - I get 12 results, this is really confusing me:
EDIT:
I thought I had resolved it...
To resolve (and test further) I decided to recreate the ProdPrice table in the db - I did this by generating the script using MSSSMS and including the data so I had an exact copy of the table, calling it ProdPrice2.
I had both ProdPrice and ProdPrice2 Entities in my system (This confirmed I'm still connected to the correct database). ProdPrice still only returned records that did not conflict with the original Unique Index. ProdPrice2 - returned all records!
Whoop, thought that was it - I then removed ProdPrice from my system, leaving ProdPrice2 - I ran the system, ProdPrice2 now only has 6 records, not the 12 I had previously!!!
I added ProdPrice back in. ProdPrice2 still has 6 records, ProdPrice now has all expected records.
I'M STUMPED!!!! This is really stopping my development! I can't continue until this is resolved!
After wrestling with this for a couple of days and some excellent help and suggestions from #AlbertoMonteiro I couldn't get the behaviour I expected.
After continuing to try various things, and debugging, I eventually hit some code I forgot I had in there...!
So the answer to the original question
Does Entity Framework Database First know about unique keys set in the database?
is, no Entity Framework Database First DOESN'T know about unique keys set in the database.
EF does however, use any overridden Equals() functions when creating lists of entities. In my code I had overridden the Equals functions to mimic the unique keys in the database meaning when I added to lists of Prices I could match "duplicates" and merge them together. This code resulted in my Prices list not being fully populated now that I had changed the way I wanted the system to work.
The answer is that I'm an idiot and I should have remembered the code I had written. I will leave this question here as it may stop someone losing a couple of days development in the future.
Entity Framework in this question, doesn't worry about your deleted Unique Key, also this Unique Key doesn't filter the data when you select something, the unique key prevents that you can not add another row with the same unique key.
So again, this unique key isn't a trouble, and EF doesn't care about it.
But you can debug the SQL generated from EF and check why you are getting only 6 items from property ProdPrices.
We can use the Database.Log property from context, to analyze the SQL generated.
You said that you have this code line Product product = db.Products.Find(id);
Lets modify it a little bit to analyze the sql.
//Add breakpoint in this line
Product product = db.Products.Find(id);
db.Database.Log = sql => System.Diagnostics.Debug.WriteLine(sql);
var prodPrices = product.ProdPrices.ToList();
//Next line
Add a breackpoint in first line(that find the product)
Press F10(Step over) 3 times
Breakpoint must be now in next line of code after the creation of prodPrices
Open the Output window
The SQL generated from ProdPrice, should be there
Analyze the SQL query, execute in SSMS and check the result.
I tried this in my machine, but it works fine:
public class Program
{
public static void Main(string[] args)
{
using (var ctx = new MyClass())
{
if (!ctx.Products.Any())
{
var product = new Product
{
ProdPrices = new List<ProdPrice>
{
new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,27)},
new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,28)},
new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,28)},
new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,29)},
new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,29,18,28,29)},
new ProdPrice {PermanentlyDelete = true, DateCreated = new DateTime(2015,12,30,19,08,38)},
new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,06)},
new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,06)},
new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,07)},
new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,07)},
new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,08)},
new ProdPrice {PermanentlyDelete = false, DateCreated = new DateTime(2015,12,31,10,18,08)},
}
};
ctx.Products.Add(product);
ctx.SaveChanges();
Console.WriteLine("Saved");
}
}
using (var ctx = new MyClass())
{
var product = ctx.Products.Find(1);
var count = product.ProdPrices.Count;
Console.WriteLine(count);
}
}
}
public class MyClass : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProdPrice> ProdPrices { get; set; }
}
public class Product
{
[Key]
public long ProdID { get; set; }
public virtual ICollection<ProdPrice> ProdPrices { get; set; }
}
public class ProdPrice
{
public long ProdPriceID { get; set; }
public bool PermanentlyDelete { get; set; }
public DateTime DateCreated { get; set; }
}
It seems you are creating the dbcontext only once, and that too is static.
When ever you are making a fetch/write call to db create a new context, that should do.
Again,
Create a base class which contains DbContext and inherit that class every time you need to talk to the db.
Something like this
public class TheDBEntities
{
private TheDBEntities _context = new TheDBEntities();
protected TheDBEntities Context { get; private set; }
protected TheDataContext()
{
Context = _context;
}
protected void SaveChanges()
{
Context.SaveChanges();
}
protected void SaveChangesAsync()
{
Context.SaveChangesAsync();
}
}
public class DBBusinessLayer: TheDBEntities
{
public DBBusinessLayer() { }
public void GetData(int id)
{
// use it here Context.<yourentities>
}
}

How to properly (deep) map complex objects with C# LINQ?

I have a database with owner and vehicle tables with a one-to-many relationship. I want to get all vehicle details and map each owner to each vehicle but I must map the query to a BDO. Not sure on the LINQ syntax but I have the code below.
using (var databaseContext = new DBConnection()) {
var vehicles = (from Vehicle in databaseContext.Vehicles
select Vehicle);
return vehicles.Select(x => new VehicleBDO() {
Id = x.Id,
// ... more simple data types
Owner = new OwnerBDO(
x.Owner.Id,
x.Owner.Name)
}).ToList();
}
Creating a new ownerBDO as shown is giving me a MethodNotSupportedException with details:
Only parameterless constructors and initializers are supported in LINQ
to Entities
I'm used to Java and new to LINQ so have no idea how to do it properly, any help would be much appreciated.
It wants you to do something like this:
Owner = new OwnerBDO() { Id = x.Owner.Id, name = x.Owner.Name},
(I don't know the property names for OwnerBDO so I guessed.)

DbMigration seed sequence contains more than one element

I have the following classes
public class Lookup
{
public int Id{get;set;}
public string Name{get;set;}
public int Order{get;set;}
}
public class CatalogType:Lookup // this was added on Add-migration "Second"
{
}
public class Catalog:Lookup
{
public int CatalogTypeId{get;set;} // this was added on add-migration "Second"
public CatalogType CatalogType{get;set;}
}
and I already have data in the database in the table Lookups that represent group of lookup classes like gender, marital statuses, catalog, etc. and the Lookups table contains a row with Name="General" that was used by Catalog(i.e Discriminator field="Catalog")
in the Configuration file inside the Seed function I wrote this code
context.Lookups.AddOrUpdate(p => new { p.Name, **p.GetType().FullName** },
new CatalogType
{
Name = "General",
IsActive = true,
Order = 1,
},
new CatalogType
{
Name = "Custom",
IsActive = true,
Order = 2,
});
context.SaveChanges();
My problem: I tried first context.Lookups.AddOrUpdate(p=>p.Name) and when I try to make update-database, the migration fails "sequence contains more than one element"
Then I tried to use p.GetType().Name the error was:
An anonymous type cannot have multiple properties with the same name.
Then I tried to use p.GetType().FullName and upon executing the update-database command, I got the following error:
The properties expression 'p => new <>f__AnonymousType18`2(Name =
p.Name, FullName = p.GetType().FullName)' is not valid. The expression
should represent a property: C#: 't => t.MyProperty' VB.Net:
'Function(t) t.MyProperty'. When specifying multiple properties use an
anonymous type: C#: 't => new { t.MyProperty1, t.MyProperty2 }'
VB.Net: 'Function(t) New With { t.MyProperty1, t.MyProperty2 }'.
I know that the problem caused because Lookups table contains already the Name="General" but how to tell the EntityFramework to take the column discriminator into consideration while trying to AddOrUpdate method?
in other words, i might have same data for 2 different objects and i want to add data on adding migration, how to achieve this if i have for example red car, red door and i want to add red apple for example? it will not allow me in my current situation, how to solve this issue?
Hope my explanation for this problem was clear.
try this :
//but two lines below in "OnModelCreating" method in your Context
modelBuilder.Entity<Lookup>().Map<Catalog>(m => m.Requires("IsCatalog").HasValue(true));
modelBuilder.Entity<Lookup>().Map<CatalogType>(m =>m.Requires("IsCatalog").HasValue(false));
// then :
context.Lookups.AddOrUpdate(p => new { p.Name , p.IsCatalog},
new CatalogType
{
Name = "General",
IsActive = true,
Order = 1,
},
new CatalogType
{
Name = "Custom",
IsActive = true,
Order = 2,
});
//context.SaveChanges(); //if you used base.OnModelCreating(modelBuilder);
base.OnModelCreating(modelBuilder); // then you don't need to save
after searching about this subject i came to this result
TPH will work with discriminator field to distinguish between derived classes
TPC does not depend on discrimintor field because the primary key of the inherited class is the same primary key of the derived class
when trying to add the data to the Catalog and i am putting constraint ( if Name repeated then make update else create), the EF failed to set the discriminator='Catalog' since it is TPC so the update will fail because table contains other data 'General'
when trying to add mapping conditions, this is not allowed by EF to use same inherited class for TPC and TPH at the same time.
hope this will help others fell in the same problem like me

C# Linq is removing a value from my entity

So, in a desperate attempt to wrangle EntityFramework into being usable. I am here..
private MyEntity Update(MyEntity orig)
{
//need a fresh copy so we can attach without adding timestamps
//to every table....
MyEntity ent;
using (var db = new DataContext())
{
ent = db.MyEntities.Single(x => x.Id == orig.Id);
}
//fill a new one with the values of the one we want to save
var cpy = new Payment()
{
//pk
ID = orig.ID,
//foerign key
MethodId = orig.MethodId,
//other fields
Information = orig.Information,
Amount = orig.Amount,
Approved = orig.Approved,
AwardedPoints = orig.AwardedPoints,
DateReceived = orig.DateReceived
};
//attach it
_ctx.MyEntities.Attach(cpy, ent);
//submit the changes
_ctx.SubmitChanges();
}
_ctx is an instance variable for the repository this method is in.
The problem is that when I call SubmitChanges, the value of MethodId in the newly attached copy is sent to the server as 0, when it is in fact not zero if I print it out after the attach but before the submit. I am almost certain that is related to the fact that the field is a foreign key, but I still do not see why Linq would arbitrarily set it to zero when it has a valid value that meets the requirements of the constraint on the foreign key.
What am I missing here?
You should probably set Method = orig.Method, but I can't see your dbml, of course.
I think you need to attach the foreign key reference
var cpy = new Payment()
{
//pk
ID = orig.ID,
//other fields
Information = orig.Information,
Amount = orig.Amount,
Approved = orig.Approved,
AwardedPoints = orig.AwardedPoints,
DateReceived = orig.DateReceived
};
//create stub entity for the Method and Add it.
var method = new Method{MethodId=orig.MethodId)
_ctx.AttachTo("Methods", method);
cpy.Methods.Add(method);
//attach it
_ctx.MyEntities.Attach(cpy, o);
//submit the changes
_ctx.SubmitChanges();

nHibernate Select statement for specific fields

I'm using nHibernate with c# to get a list of records or strings from the database as show in the first couple of lines of code below. This works fine. What I want to do is select a few specific fields from the record and not the entire record. I have tried various techniques and can't seem to find any examples on how to do this. Could someone have a look at the code below and let me know if I am going off in the wrong direction.
Thanks!
// THIS WORKS - Retrieve a list of my records from the table.
Ilist<MyClass> classList = db.Session.CreateQuery("FROM MyTable WHERE t.Name='AName'").List<MyClass>();
// THIS WORKS - Retrieve a list of strings from the table
IList<string> stringList = db.Session.CreateQuery("SELECT c.ConstName FROM MyTable c WHERE c.Name='AName'").List<string>();
// THIS DOES NOT WORK (RUN-TIME ERRORS). HOW CAN I SELECT ONLY A FEW FIELDS FROM EACH RECORD?
// This class contains only the records I want.
public struct MyClassB
{
private string Name;
private string Address;
public string Name
{
get { return Name; }
set { Name = value; }
}
public string Address
{
get { return Address; }
set { stationName = Address; }
}
}
IList<MyClassB> classListB = db.Session.CreateQuery("SELECT t.Name, t.Address FROM MyTable t WHERE t.Name='AName'").List<MyClassB>();
Have a look at the AliasToBeanResultTransformer - usage is demonstrated here.
You are trying to cast an Anonymous type into your MyClassB which isn't valid. Instead create a mapping for MyClassB.
Or just use:
var specificFields = db.Session.CreateQuery("SELECT t.Name, t.Address FROM MyTable t WHERE t.Name='AName'").List();
var specificFields = db.Session.CreateQuery("SELECT t.Name, t.Address FROM MyTable t WHERE t.Name='AName'").List<Tuple<string,string>>();
The objects in the list will have the two properties.
As long your class has a contructor you should be able to do the following:
IList<MyClassB> classListB = db.Session.CreateQuery("SELECT new MyClassB(t.Name, t.Address) FROM MyTable t WHERE t.Name='AName'").List<MyClassB>();

Categories