How to create foreign key relationships with the Entity Framework? - asp.net-mvc

I want to create a new row in my database on a table that has a couple of foreign key relationships and I haven't been able to get a handle on what order and what calls need to be made. This is what I have so far:
db.Models.Order order = DB.Models.Order.CreateOrder( apple );
order.CustomerReference.Attach( ( from c in db.Customer where c.Id == custId select c ).First() );
db.SaveChanges();
The code is failing on the second line there, saying:
Attach is not a valid operation when
the source object associated with this
related end is in an added, deleted,
or detached state. Objects loaded
using the NoTracking merge option are
always detached.
Any ideas?

(Thanks John for the grammar fixes)
So I figured it out. This is what you have to do:
db.Models.Order order = DB.Models.Order.CreateOrder( apple );
order.Customer = (from c in db.Customer where c.Id == custId select c).First();
db.SaveChanges();
I hope that helps people.

Why not use entity references? Your method will cause an extra SELECT statement.
A much nicer way is to use the CustomerReference class and an EntityKey.
order.CustomerReference = new System.Data.Objects.DataClasses.EntityReference<Customers>();
order.CustomerReference.EntityKey = new EntityKey("ModelsEntities.Customers", "Id", custId);

For update here is some sample code:
using (var ctx = new DataModelEntities())
{
var result = (from p in ctx.UserRole.Where(o => o.UserRoleId == userRole.UserRoleId)
select p).First();
result.RolesReference.EntityKey = new EntityKey("DataModelEntities.Roles",
"RoleId", userRole.RoleId);
result.UserRoleDescription = userRole.UserRoleDescription;
ctx.SaveChanges();
}

Related

Selecting list for drop down ASP.NET MVC

In my database, I have a table called Employee and it has columns EmpNames and EmpId which same EmpId created User table with user levels. I want to get a list of empNames and id's to who are user level equal to the 4.
This is how I got empname list for a drop down list
List<M_Employee> EmpList = db.CreateEmployee.Where(x => x.Status == true).ToList();
List<SelectListItem> EmpDropDown = EmpList.Select(x => new SelectListItem { Text = x.EmpName, Value = x.Id.ToString() }).ToList();
Same way I have tried to query the user level = 4 and tried to join emp table with user table to get the emp names who assigned user levels to 4 but it didn't work.
Here is my code for that
List<int> TopEmp = db.Master_Users.ToList().Where(r => r.EmpId == int.Parse(db.CreateEmployee.Where(x=> x.Id))).ToList().
Can you help me on this?
Firstly, you need to understand how ToList works.
When you call ToList it means that Entity framework will execute the sql statement constructed at that point and retrieve the results into memory.
You generally want to construct your entire query first and then have that query get all the data you want from the database in the format of an object you want by using .Select(x => x.whatever).ToList(). Otherwise you'll be making multiple calls to the database to get bits of data here and there and then joining them or working with them unnecessarily in memory which is slower than having the database do it.
So your first query where you get the select list items can be rewritten like this:
List<SelectListItem> EmpDropDown = db.CreateEmployee
.Where(x => x.Status == true)
.Select(x => new SelectListItem { Text = x.EmpName, Value = x.Id.ToString() })
.ToList()
And from what you've described you should be able to rewrite the 2nd query like this:
List<int> TopEmp = (from u in db.Master_Users
join e in db.CreateEmployee on u.EmpId equals e.Id
where u.Level == 4
select e.Id
).ToList();
This is using a different query syntax but allows to specify the key to join on easily as I don't know how your foreign keys and navigation properties are setup.
I can't see you dbcontext, maybe it is possible to use include too, but for the start try this
List<SelectListItem> EmpDroDown = (from emp in db.CreateEmployee
join usr in db.Master_Users on emp.Id equals usr.EmpId
where emp.Status == true && usr.UserLevel==4
select new SelectListItem { Text = em.EmpName,
Value = emp.Id.ToString() }).ToList();

EF6: Add Object with Foreign Key to Multiple Tables But What if Foreign Key Already Exist Somewhere? (+trying to have no round trips to check IDs)

I have table ProjekRumah and ProjekKMDetails.
When I use entities.SaveChanges() on ProjekRumah data, new row with ProjectID will yield its Identity. Suppose that number is 7.
Then I take this ProjekID=7, to create ProjekKMDetails data. Then, entities.SaveChanges(). Repeat same step Table1, Table2, Table3, Table 4 and so on with ProjectID=7 as Foreign Key.
All is fine on sunny day. What if ProjekID=7 already exists in one of these table, suppose hypothetically in Table3.
I understand this is not suppose to happen. But suppose its Friday the 13th an intern might drop all constraints and forgot to truncate Table3. Or some evil data corruption.
using (MyDBEntities entities = new MyDBEntities())
{
using (var dbContextTransaction = entities.Database.BeginTransaction())
{
try
{
var dataRumah = new ProjekRumah
{
ProjekStatus = 'SpecialScenario',
ProjekName = data.ProjekName, //data passed in from UI
};
entities.ProjekRumah.Add(dataRumah);
entities.SaveChanges(); // the first .SaveChanges to acquire Identity increment
var dataKMDetails = new ProjekKMDetails
{
ProjekID = dataRumah.ProjekID, //identity ID
KMJPBDNo = data.KMJPBDNumber, //data passed in from UI
};
entities.ProjekKMDetails.Add(dataKMDetails);
var table3Details = new Table3
{
ProjekID = dataRumah.ProjekID, //identity ID
Table3Stuff = data.ForTable3Stuff, //data passed in from UI
};
entities.Table3.Add(table3Details );
//..... No .SaveChanges anywhere
//..... more tables, Tables 4, 5 6 etc
//..... No round trips to database
entities.SaveChanges(); //the second and final .SaveChanges()
dbContextTransaction.Commit();
}
catch (Exception ex)
{
dbContextTransaction.Rollback();
}
}
My intent is to be defensive and safe side, is there a syntax to check if Table3 already have row with ProjectID = 7?
To my understanding attempting to delete anything from Table3 will require delete on same corresponding ID in ProjekRumah and ProjekKMDetails and Table4 and so on:
ProjekRumah kmContext = new ProjekRumah {
ProjekID = 7,
ProjekKMDetails = new ProjekKMDetails { ProjekID = 7 },
Table3= new Table3 { ProjekID = 7 }
};
entities.ProjekRumah.Attach(kmContext);
entities.ProjekRumah.Remove(kmContext);
entities.SaveChanges(); //this actually deletes from multiple tables
But at this stage there is no (should be) such record in ProjekRumah table because earlier on: entities.Table3.Add(dataTable3Details) would be illegal and caused rollback.
How do I write code to be on defensive side in case 'Foreign Key Already Exist' happens?
Or such a scenario can never ever 101% to happen?
If possible, I would like to cleanse with EF functions as succinct and as elegant as possible.
PS: I guess I can summarize my question as this:
Suppose an orphan row was found despite Foreign Key constrains (should never happen I believe), but how do I use EF to handle this in a single transaction (preferably with no round trips such as Count, Select, FirstorDefault etc)?

How do I use 2 include statements in a single MVC EF query?

I am trying to write a query that includes 2 joins.
1 StoryTemplate can have multiple Stories
1 Story can have multiple StoryDrafts
I am starting the query on the StoryDrafts object because that is where it's linked to the UserId.
I don't have a reference from the StoryDrafts object directly to the StoryTemplates object. How would I build this query properly?
public JsonResult Index(int userId)
{
return Json(
db.StoryDrafts
.Include("Story")
.Include("StoryTemplate")
.Where(d => d.UserId == userId)
,JsonRequestBehavior.AllowGet);
}
Thank you for any help.
Try to flatten your hierarchy if it works for you. Here is a sample, and you may want to customize it for your needs.
var result = from c in db.Customers
join o in db.Orders
on c equals o.Customers
select new
{
custid = c.CustomerID,
cname = c.CompanyName,
address = c.Address,
orderid = o.OrderID,
freight = o.Freight,
orderdate = o.OrderDate
};
If flattering does not meet your requirements then you need to use query that returns a Nested Group. Finally, look at the following link for more references - LINQ Query Expressions .

How to specify a condition in an Entity Framework join?

I have a Blogs table related to BlogComments table with a FK.
I need to get, through Linq, all the BlogComments items that match a certain flag
If i do:
db.Blogs.Where(b => b.BlogComments.Where(bc=>bc.Where(bc.Flag1==true));
I get "Cannot implicity convert type IEnumerable to bool"
Which is the best way to solve this problem?
Because this expression:
b.BlogComments.Where(...)
returns an IEnumerable (of BlogComments), but you are then passing it into this method:
db.Blogs.Where(...)
which expects a function that returns a bool, not an IEnumerable.
You probably need something like this:
var blogId = 5;
db.BlogComments.Where(bc => bc.BlogId == blogId && bc.Flag1 == true)
If you need to select comments from multiple blogs, then you could try using Contains:
var blogIds = new [] {1,2,3,4,5};
db.BlogComments.Where(bc => blogIds.Contains(bc.BlogId) && bc.Flag1 == true)
If you want to place criteria on the set of blogs, as well as the comments, then you could do this in one query using a join:
var query = from b in db.Blogs
join c in db.BlogComments on c.Blog equals b
where b.SomeField == "some value"
&& c.Flag1 == true
select c;
You could write it in LINQ form.
var blogs = from b in db.Blogs
join c in db.BlogComments
on b.BlogId equals c.BlogId
where c.Flag1
select b;
If you have a composite key you can write
on new { A = b.BlogKey1, B = b.BlogKey2 }
equals new { A = c.CommentKey1, B = c.CommentKey2 }
If it were me, I would just have another DbSet in your DbContext.
DbSet<BlogComment> BlogComments
and just search through there without going through Blogs.
db.BlogComments.Where(bc => bc.Flag1 == true);
If anyone knows if there's anything wrong in doing so, then I'm all ears :)

Entity Framework - Join on many to many

I have a simple many to many relationship and I am wondering how you get data out of it. Here is the setup
Tables
Media
Media_Keyword (many to many map)
Keyword
Here is the code I have:
public List<Keyword> GetFromMedia(int mediaID)
{
var media = (from m in Connection.Data.Media
where m.id == mediaID
select m).First();
var keys = (from k in media.Media_Keyword
select new Keyword {ID = k.Keywords.id, Name = k.Keywords.keyword});
return keys.ToList();
}
Is there a way to do this better?
Usually, I select right from the many-to-many map.
var keys = from k in Connection.Data.Media_Keyword
where k.MediaID == mediaID
select k.Keywords;
I've not used the entity framework specifically, but can't you just combine them like this?
public List<Keyword> GetFromMedia(int mediaID)
{
return (from m in Connection.Data.Media
from k in m.Media_Keyword
where m.id == mediaID
select new Keyword {ID = k.Keywords.id, Name = k.Keywords.keyword}).ToList();
}
Response to Kleinux (Don't know why i can't add a comment to your question)
Sure you can, but it's not necessarly a good things, because context giving you a new "keyword". Then, if you try to update this or something thinking that you will update, context gonna see it as a new keyword and would create a new one instead of updating it.
** UPDATE
Sorry for my english, i'm french, well not french but from Quebec. I'm giving my 110%!!

Resources