I can't seem to find documentation or examples for my problem (been searching a while now). I think my problem is pretty straightforward, so here goes.
I have two tables. My primary table is called Persons and the secondary table is PersonEntries. For each person in Person table, i can have 0 or more entries in the PersonEntries table. Like this.
Table: Person
Id
Name
Table: PersonEntry
PersonId
CheckinTime
CheckoutTime
I have two objects like this
public class Person {
public string Name;
public List<PersonEntry> PersonEntries;
}
public class PersonEntry {
public DateTime CheckinTime;
public DateTime CheckoutTime;
}
If i was to get it from the database into my c# classes how would i do it? I can map a single table into my c# class and do it for each table, but then i'm left to match what entries maps to what person.
I've seen several examples of mapping ONE PersonEntry to ONE Person, the problem here is that i have a zero-to-many relation. My Person have a LIST of PersonEntry items.
You can do something like this (see https://www.tritac.com/blog/dappernet-by-example):
public class Shop {
public int? Id {get;set;}
public string Name {get;set;}
public string Url {get;set;}
public IList<Account> Accounts {get;set;}
}
public class Account {
public int? Id {get;set;}
public string Name {get;set;}
public string Address {get;set;}
public string Country {get;set;}
public int ShopId {get;set;}
}
var lookup = new Dictionary<int, Shop>()
conn.Query<Shop, Account, Shop>(#"
SELECT s.*, a.*
FROM Shop s
INNER JOIN Account a ON s.ShopId = a.ShopId
", (s, a) => {
Shop shop;
if (!lookup.TryGetValue(s.Id, out shop)) {
lookup.Add(s.Id, shop = s);
}
if (shop.Accounts == null)
shop.Accounts = new List<Account>();
shop.Accounts.Add(a);
return shop;
}
).AsQueryable();
var resultList = lookup.Values;
Related
I've got a relatively basic model - Users and Tags. There is a fixed list of Tags. A User can have multiple Tags and a Tag can be used by multiple users.
I had gone with structure below and finding performance issues when returning results.
public class User
{
public string Id {get; set;}
public virtual List<UserTag> UserTags {get; set}
}
public class UserTag
{
public string UserId { get; set; }
public User User { get; set; }
public int TagId { get; set; }
public Tag Tag{ get; set; }
}
public class Tag
{
[Key]
public int TagId { get; set; }
public string Name { get; set; }
public virtual List<UserTag> UserTags { get; set; }
}
I have the following query which is takings a long time (several seconds):
var x = db.Users.Include(u => u.UserTags).ThenInclude(u => u.Trait).ToList<User>();
I have tried writing it as such, which has improved the time, however it is still taking too long:
db.UserTags.Load();
db.Tags.Load();
var x = db.Users.ToList<User>();
Is there any other way to speed this up? Running a query directly in SQL SMS is almost instant (e.g.
select * from Users u left outer join UserTags t on t.UserId = u.Id)
In terms of data rows, it is apx Tags: 100, UserTags:50,000, Users: 5,000
First you can check how EF translates your request to SQL Server - therefore use the "SQL Server Profiler"
Then you could use the genereated query to check if there might be an missing index which speeds up the query
You also can try to write a Join instead of ThenInclude and see how the query then behaves
best regards
Jimmy
What I'm trying to achieve is like this:
Fill up a form in view
Save data into table1 with columns:
Id|OperationNumber|Name|ContactNo
table1 Id is the primary key
Save data into table2 with columns:
ReferenceId|OperationNumber
ReferenceId is the primary key of datatype uniqueidentifier
"OperationNumber" column in both table is related. when data is saved in table1, OperationNumber will also save in table2 together with autogenerated ReferenceId (uniqueidentifier)
Retrieving process is:
input ReferenceId as search
Display all the details from table1 and table2 in result view
So that is my problem, how do i save and retrieve data in two tables?
Here's what I currently have:
Controller:
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(RefViewModel myViewModel)
{
db.Table1.Add(myViewModel.Ref);
db.SaveChanges();
return RedirectToAction("Index");
}
Model:
public class RefViewModel
{
public Table1 Ref { get; set; }
public string OperationNumber { get; set; }
public string Name { get; set; }
public string ContactNo { get; set; }
}
In the above code can save only in table1. so what to do in order for me to save two tables?
(oh and btw. as you may have noticed the "Bind(Include =" is nowhere, I have disabled it because I'm getting null values when saving into database. so, anyways. that's not the problem anymore here. just mentioning)
Database is SQL Server
Edit: as for retrieving data
SearchController:
public ActionResult Search(string searchString)
{
var myRef = (from x in db.Table1
where x.OperationNumber.Contains(searchString)
select x).FirstOrDefault();
return View(myRef);
}
That returns details when I input the operationnumber. So the problem is how to retrieve details from two tables when I input only referenceId?
You can easily do this by using stored procedures. Define two property classes to represent two tables and use inheritance as follows.
public class Tablel
{
public string ID { get; set; }
public string OperationNumber { get; set; }
public string Name { get; set; }
public string ContactNo { get; set; }
}
public class Table2 extend Table1
{
public string ReferenceID { get; set; }
public string OperationNumber { get; set; }
}
Now you can use stored procedures to save and retrieve data from database. Use join when you retrieve data from database and you can use two separate queries when save data to two tables.
From c# metadata of DbContext.SaveChanges():-
Returns:
// The number of objects written to the underlying database.
so basically you will do something like this:-
public ActionResult Create(RefViewModel myViewModel)
{
db.Table1.Add(myViewModel.Ref);
var t1 = db.SaveChanges();
Table2 t2 = new Table2
{
OperationNumber = t1.OperationNumber
};
db.Table2.Add(t2);
db.SaveChanges();
return RedirectToAction("Index");
}
when the SaveChanges is called, it returns all the objects saved to the database so when you call SaveChanges for the first time, it will return a Table1 object and with that Table1 object you will populate the OperationNumber property of Table2 and call SaveChanges again.
if your operationnumber is unique then use this query on store procedure, you can found data using ReferenceID
Select * from table1 t1
inner join table2 t2 on t1.OperationNumber = t2.OperationNumber
where t2.ReferenceID = #pReferenceID
I'm using Entity Framework 6 Code First.
Right now my model looks the following:
public class Region
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<City> Cities { get; set; }
}
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Region Region { get; set; }
}
The thing is, I got a list of cities.
I need to lookup the region of each city (I have a local list of regions as well).
Now, I suppose I could do the following:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.Region.Id))
}
Here I will have to look up a region from the database for each city (lazy loading).
However, I only need the Id of the region, therefore it seems wasteful to me to look up the region row at every loop.
If I changed my City model to be the following:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Region")]
public int RegionId { get; set; }
[Required]
public virtual Region Region { get; set; }
}
I could do the following instead:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.RegionId)) //no region lookup at Im using the foreign id key
}
Is this correct? I mean it will save me a query for every city right?
If so, is there any reason NOT to include the foreign id keys in the model when doing Code First at all?
Is this correct? I mean it will save me a query for every city right?
It will save a join to the Regions table. You have to check it out with the SQL Profiler! For example:
List of cities with Id City1, City2 (Region not loaded City1= has RegionId 5)
Now you are looking for any City has a Region with Id 5.
// EF does not have to join the tables because you have the RegionId
if (myDbContext.Cities.Any(c => c.RegionId == 5))
{
}
If so, is there any reason NOT to include the foreign id keys in the
model when doing Code First at all?
No for me this it is a good practice! Just keep it consistent and do it for all relationships of the type 1..n/1..0or1.
I have a BlobEntity table that contains paths to files for many other tables (tableX, tableY, tableZ, etc...) in my application.
The relation between all the other tables to BlobEntity table is one to many.
Example:
tableX -> BlobTable (OTM)
tableY -> BlobTable (OTM)
tableZ -> BlobTable (OTM)
and the relation is:
public virtual ICollection<BlobEntity> BlobEntity { get; set; }
I'm not sure if this is an issue, but entity framework Code First creates a new FK column in BlobEntity table for each source table.
In my case, BlobEntity contains three FK columns for tableX, tableY and tableZ.
In order to be efficiency, i rather create one column in BlobEntity that contains the FK for the source tables.
Is it reasonable?
Please advise...
Thanks.
No, you can't do this even in plain old SQL.
You can have a foreing key pointing to more than one table; that's why you need
three columns.
If you want to do a "trick" like this, you have to manually manage the relation (I mean, no real FK), but you can't map it into EF.
What about this?
public class EntityA
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class EntityB
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class MyFiles
{
public MyFiles()
{
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityA = new List<EntityA>();
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityB = new List<EntityB>();
}
public int Id { get; set; }
public int? EntityAId {get;set;}
public int? EntityBId {get;set;}
public virtual ICollection<EntityA> FilesForEntityA { get; set; }
public virtual ICollection<EntityB> FilesForEntityB { get; set; }
}
This way you can have the FK in place and you can easily manager multiple entities.
Obviously if you have many files for each entity, you can go with a N-to-N relationship, like this.
I have a autocompletebox that is used to select a destination for a car booking program. For the itemssource of the autocomplete box I am trying to set it to all the previous destinations entered. The problem is that I can't work out how to return a single column 'Destination' of distinct destination values from my Booking class, e.g.
var query = from bk in ObjectContext.Bookings select new DestinationDTO { Destination = bk.Destination };
return query.Distinct();
. I have tried creating a shared DestinationDTO class to return just the single column but can't work out how to get this to inherit from Entity!!
Any ideas?
You need to have a property with a [Key] attribute in your DestinationDTO class. Then RIA services will be able to generate a corresponding class on the client side.
public class DestinationDTO
{
[Key]
public Guid Id { get; set; }
public string Destination { get; set; }
}
Then just do this:
var query = from bk in ObjectContext.Bookings
select new DestinationDTO { Destination = bk.Destination, Id = Guid.NewGuid() };
return query.Distinct();