I'm new to EF and want to get an entry from my database (SQLite) in the following way:
Classes:
public class Customer
{
public Guid Id { get; set; }
public List<Month> Months { get; } = new List<Month>();
}
public class Month
{
public int Id { get; set; }
public string CacheId { get; set; }
public string Data { get; set; }
[Required]
public Customer Customer { get; set; }
}
DBContext:
public DbSet<Customer> Customers { get; set; }
public DbSet<Month> Months { get; set; }
Usage:
using (var context = new CustomerContext())
{
var customer = context.Customers.First();
context.Database.Log = Console.WriteLine;
var shouldContainOneEntry = context.Months.Where(x => x.Customer.Id == customer.Id).ToList();
}
shouldContainOneEntry is emtpy, but a test with a delegate and a static variable instead of the lambda expression worked:
private static Guid staticGuid;
public static bool DelegateTest(Month x)
{
return staticGuid == x.Customer.Id;
}
...
staticGuid = customer.Id;
var workingVersion = context.Months.Where(DelegateTest).ToList();
The generated SQL looks correct:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[CacheId] AS [CacheId],
[Extent1].[Data] AS [Data],
[Extent1].[Customer_Id] AS [Customer_Id]
FROM [Months] AS [Extent1]
WHERE [Extent1].[Customer_Id] = #p__linq__0
-- p__linq__0: '5cfde6e0-5b3f-437b-84c8-2845b077462d' (Type = AnsiStringFixedLength, IsNullable = false)
Why is the version with the lambda expression not working?
The solution was found by following the hints of IvanStoev.
SQLite stores the Guid by default in a binary format.
Therefor a query like
SELECT * FROM Months WHERE CustomerId = '5cfde6e0-5b3f-437b-84c8-2845b077462d'
delivers an empty result.
Using SQLite Adminstrator the Guid is shown with the binary format.
Using the server explorer in VS the Guid is shown with a string format which lead me to the wrong conclusion that this should work.
After setting the option BinaryGUID of the connection string to false the code works fine.
Based on your declarations
public class Customer
{
public Guid Id { get; set; }
public List<Month> Months { get; } = new List<Month>();
}
public class Month
{
public int Id { get; set; }
public Guid CustomerId{get;set;} // you need to add the foreign Key of the customer ( i will suppose that your foreign key in db called CustomerId
public string CacheId { get; set; }
public string Data { get; set; }
[Required]
[ForeignKey("CustomerId")] // and you need to tell EF which column is the foreign key
public Customer Customer { get; set; }
}
how to use it now
using (var context = new CustomerContext())
{
var customer = context.Customers.First();
context.Database.Log = Console.WriteLine;
var shouldContainOneEntry = context.Months.Where(x => x.CustomerId == customer.Id).ToList();
}
hope it will help you
Related
Hello I am new to servers and REST API and am trying to extract data from a dynamically created table and the data does not sync with the data in the database.
I have an sql database from which I extracted an entity database in asp.net web project.
This is an example for GET of one entity class (exists in database):
public class EmployeeBL
{
private FSProject1Entities db = new FSProject1Entities();
public List<Employee> GetEmployees(string fname, string lname, string depID)
{
return GetEmployeeSearchResult(fname, lname, depID);
}
}
And this is an example for a method from a class such as I created in order to combine data from 2 tables:
public class ShiftEmployeeDataBL
{
private FSProject1Entities db = new FSProject1Entities();
private List<ShiftEmployeeDataBL> GetEmployeeByShiftID(int id)
{
List<ShiftEmployeeDataBL> shiftEmpData = new List<ShiftEmployeeDataBL>();
foreach (Employee emp in db.Employee)
{//build list... }
return shiftEmpData;
}
My problem is that db.Employee via this GET request path (ShiftEmployeeData) is old data and via Employee GET request is good data (assuming the data was updated via Employee path).
And vice versa - it would appear that if I update Employee via ShiftEmployeeData class, it would appear as good data for ShiftEmployeeData class and not update for Employee.
I have APIcontrollers for both classes.
what is happening? I feel like I am missing something.
I tried closing cache options in browser.
update with code for elaboration:
entity Employee:
public partial class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int StartWorkYear { get; set; }
public int DepartmentID { get; set; }
}
employee update(auto generated by entity model code generation from db):
public void UpdateEmployee(int id, Employee employee)
{
Employee emp= db.Employee.Where(x => x.ID == id).First();
emp.FirstName = employee.FirstName;
emp.LastName = employee.LastName;
emp.StartWorkYear = employee.StartWorkYear;
emp.DepartmentID = employee.DepartmentID;
db.SaveChanges();
}
employeeshiftdata class (not a db table but still in the models folder):
public class EmployeeShiftData
{
public int ID { get; set; } //EmployeeID
public string FirstName { get; set; }
public string LastName { get; set; }
public int StartWorkYear { get; set; }
public string DepartmentName { get; set; }
public List<Shift> Shifts { get; set; }
}
employeeshift GET part of the controller:
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class EmployeeShiftDataController : ApiController
{
private static EmployeeShiftDataBL empShiftDataBL = new EmployeeShiftDataBL();
// GET: api/EmployeeShiftData
public IEnumerable<EmployeeShiftData> Get(string FirstName = "", string LastName = "", string Department = "")
{
return empShiftDataBL.GetAllEmployeeShiftData(FirstName, LastName, Department);
}
//...
}
Would need to see the code that interacts with the database, especially the code that makes the updates.
If the changes are written with Entity Framework, are the models themselves properly related with navigational properties?
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public List<EmployeeShift> EmployeeShifts { get; set; }
// etc.
}
public class EmployeeShift
{
public int Id { get; set; }
public Int EmployeeID { get; set; }
public Employee Employee { get; set; }
// etc.
}
If those are good, and both models are covered by Entity Framework's context tracking, then both should be updated.
I have 4 classes like that:
Organization1:
Contact 1:
Phones:
phone1
phone2
Addresses:
address1
address2
Bankdata:
bankdata1
bankdata2
Contact 2:
Phones:
phone1
phone2
Addresses:
address1
address2
Bankdata:
bankdata1
bankdata2
Contact 3:
Phones:
phone1
phone2
Addresses:
address1
address2
Bankdata:
bankdata1
bankdata2
Organization 2:
... and so on...
In order to grab the list of Organizations with all its contacts and each contact including JUST the phone1 (where main == true), address1 and bankdata1 I have written the following inside Entity Framework a query:
...
public class ContactManagementRepository : IContactManagementRepository
{
public IEnumerable<Organization> getAllOrganizations()
{
return _context.Organizations
.OrderBy(o => o.organizationName)
.Include(o => o.Contacts)
.ToList();
}
...
But I just get ONE Organization, and no contacts.
A tried a second attempt like this:
public class ContactManagementRepository : IContactManagementRepository
{
public IEnumerable<Organization> getAllOrganizations()
{
return _context.Organizations.ToList();
}
...
It returns all the organizations, but not data under the tree (contact->phones, etc)
A third attempt (very bad for performance) was to get ALL the organizations and loop through using getorganizationsbyID and get the info of each one like this:
...
public Organization GetOrganizationById(Guid Id)
{
return _context.Organizations
.Include(o => o.Contacts)
.ThenInclude(c => c.Phone.main == true)
.Where(o => o.Id == Id)
.FirstOrDefault();
}
...
But it just get the organization with JUST ONE CONTACT (the first one) and not phones, address or bankdata. The method "ThenInclude" does not accept a second call. It means accept just one child under contact, but not several children.
Here are my model classes:
Organization class:
public class Organization
{
public Organization() { }
private Organization(DateTime dateCreated)
{
this.dateCreated = DateTime.Now;
Contacts = new List<Contact>();
}
public Guid Id { get; set; }
public DateTime dateCreated { get; set; }
public string organizationName { get; set; }
public string organizationType { get; set; }
public string web { get; set; }
// Contacts
public ICollection<Contact> Contacts { get; set; }
}
Contact class:
public class Contact
{
public Contact() { }
private Contact(DateTime dateCreated)
{
this.dateCreated = DateTime.Now;
Phones = new List<Phone>();
Addresses = new List<Address>();
Bankdatas = new List<Bankdata>();
}
public Guid Id { get; set; }
public DateTime dateCreated { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public Guid OrganizationId { get; set; }
[ForeignKey("OrganizationId")]
public virtual Organization Organization { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Bankdata> Bankdatas { get; set; }
}
Phone class (same structure for addresses and bankdata)
public class Phone
{
public Guid Id { get; set; }
public DateTime dateCreated { get; set; }
public string phone { get; set; }
public bool main { get; set; }
// Foreign Key for Contacts
public Guid ContactId { get; set; }
//Related Organization entity
[ForeignKey("ContactId")]
public virtual Contact Contact { get; set; }
}
Database is created with the PK and FK in correctly in place (code-first)
And now I am lost. Does anybody could help with this?
How do I query (LINQ) for organization -> child -> 4 grandchildren ?
I have read literally dozens of threads here and follow tutorials in Udemy and Pluralsight without success.
EDIT
# Hadi Hassan
I followed your instructions, I created new classes OrganizationDTO, ContactDTO, AddressDTO, PhoneDTO and BankdataDTO.
I created the DTO for Bank, Phone and address because even if am sure just one is main (there is a radio button at the front end) I still will need the whole list when I get the contactsdetails.html. It will allows me to make searches by country-code and so on.
I have changed, as per your advice, but it dd not even compile. I get error telling:
*DbSet<Organization>ContactManagement.Context.Organizations {get; set;}
Cannot assign method group to an implicitly-typed variable.*
I have decided to test changing the Context class like this:
DBSet. So I changes all DBset properties with the suffix DTO. Like this:
public DbSet<OrganizationDTO> OrganizationsDTO { get; set; }
public DbSet<ContactDTO> ContactsDTO { get; set; }
public DbSet<BankdataDTO> BankdatasDTO { get; set; }
public DbSet<AddressDTO> AddressesDTO { get; set; }
public DbSet<PhoneDTO> PhonesDTO { get; set; }
But the I have got copilations error everywhere.
Best way to get your data is by relying on the DTO
so your code will appear like this
public class OrganizationDTO
{
public OrganizationDTO()
{
Contacts = new List<ContactDTO>();
}
public Guid Id{get;set;}
public string Name{get; set;}
// other needed properties goes here
public IList<ContactDTO> Contacts{get;set;}
}
public class ContactDTO
{
public ContactDTO
{
Phones = new List<PhoneDTO>();
Addresses = new List<AddressDTO>();
Banks = new List<BankDTO>();
}
public Guid Id{get;set;}
public string FirstName{get;set;}
public string LastName {get;set;}
// other required properties goes here
public IList<PhoneDTO> Phones {get;set;}
public IList<BankDTO> Banks{get;set;}
public IList<AddressDTO> Addresses{get;set;}
}
public class PhoneDTO
{
public Guid Id{get;set;}
public string Phone{get;set;}
}
public class AddressDTO
{
public Guid Id{get;set;}
public string Description{get;set;}
}
public class BankDTO
{
public Guid Id{get;set;}
public string Name{get;set;}
}
Your query then will look like
var query = _context.Organizations
.Select t=> new OrganizationDTO{
Id = t.Id,
Name = t.organizationName,
Contacts = t.Contacts.Select(c => new ContactDTO{
Id = c.Id,
FirstName = c.firstName,
LastName = c.lastName,
Phones = c.Phones.Where(p=>p.Main).Select(p=>new PhoneDTO{Id = p.Id, Phone= p.phone}),
Banks = c.Banks.Where(p=>p.Main).Select(p=>new BankDTO{Id = p.Id, Name= p.BankName}),
Addresses = c.Addressess.Where(p=>p.Main).Select(p=>new AddressDTO{Id = p.Id, Description= p.Description}),
})
}).OrderBy(t=>t.Name);
Implementing the DTO will provide many advantages for your data
Avoid Circular references
Return the required data and not all the object (Data Projection)
Allows you to combine many objects together to appear in same result, for example, in the solution I returned object BankDTO and PhoneDTO and AddressDTO which allows your to return list of object from these entities, but if you are sure that only one phone, bank , address is main, then you can instead define these data as properties in the class ContactDTO and by this you combined 4 entities data in one result.
Your ContactDTO will become as
public class ContactDTO
{
public Guid Id{get;set;}
public string FirstName{get;set;}
public string LastName{get;set;}
public string Phone{get;set;}
public string Bank{get;set;}
public string Address{get;set;}
}
and your code will be like
var query = _context.Organizations
.Select t=> new OrganizationDTO{
Id = t.Id,
Name = t.organizationName,
Contacts = t.Contacts.Select(c => new ContactDTO{
Id = c.Id,
FirstName = c.firstName,
LastName = c.lastName,
Phone = c.Phones.Where(p=>p.Main).Select(p=>p.phone).FirstOrDefault(),
Bank = c.Banks.Where(p=>p.Main).Select(p=>p.Name).FirstOrDefault(),
Address = c.Addressess.Where(p=>p.Main).Select(p=>p.Discreption).FirstOrDefault(),
})
}).OrderBy(t=>t.Name);
and in case you want to get the phone as object and not list, may be you want to get the benefit of the phone Id, you can instead of IList<PhoneDTO> to define directly PhoneDTO.
Hope this will help you
Database Models of my Application are:
public class Restaurant
{
public int Id { get; set; }
.........
}
public class Review
{
public int Id { get; set; }
public string ReviewTitle { get; set; }
public string ReviewContent { get; set; }
public int UserId { get; set; }
public int RestaurantId { get; set; }
}
public ReviewHelpful
{
public int Id { get; set; }
public int ReviewId { get; set; }
public bool IsHelpfull { get; set; }
}
public ReviewImage
{
public int Id { get; set; }
public string ImageLink { get; set; }
public int ReviewId { get; set; }
}
There is no navigation property in any table. In ReviewHelpful table, If user finds helpfull of this review than value is true otherwise false.
Now I want to create a view-model Like this:
public class ReviewViewModel
{
public int ReviewId { get; set; }
public int RestaurantId { get; set; }
public string ReviewTitle { get; set; }
public string ReviewContent { get; set; }
public int UserId { get; set; }
public int NumberOfHelpfull { get; set; }
public int NumberOfNotHelpfull { get; set; }
public List<string> ImagesLinks { get; set; }
}
For that reason, I want to write this kind of query :
var reviews = (from review in _foodTrackerContext.RestaurantReviews
join helpful in _foodTrackerContext.Helpfuls on review.Id equals helpful.ReviewId
join reviewPicture in _foodTrackerContext.ReviewPictures on review.Id equals reviewPicture.ReviewId
where review.ResturantId == 2
select new ReviewViewModel()
{
Id = review.Id,
RestaurantId = 2,
ReviewTitle = review.ReviewTitle,
ReviewContent = review.ReviewContent,
NumberOfHelpfull = .. ??,
NumberOfNotHelpfull = ... ??,
ImagesLinks = ... ???
}
I can not retrieve HelpfulYes, HelpfulNo, ImagesLinks with this query. What would be query for finding these variables?.
This query produces multiple rows for single review with each ReviewImage and each ReviewHelpful.
The query that ypu need to do is this one:
var model =
from review in ctx.Reviews
where review.RestaurantId == 2
join helpful in ctx.ReviewHelpfuls
on review.Id equals helpful.ReviewId into helpfuls
join image in ctx.ReviewImages
on review.Id equals image.ReviewId into images
select new RestaurantReviewViewModel
{
Id = review.Id,
RestaurantId = 2,
ReviewTitle = review.ReviewTitle,
ReviewContent = review.ReviewContent,
NumberOfHelpfull = helpfuls.Count(h => h.IsHelpfull),
NumberOfNotHelpfull = helpfuls.Count(h => !h.IsHelpfull),
ImagesLinks = (from image in images select image.ImageLink).ToList()
};
Please, note that when you do a one to manyh join you need to include an into to give a nameto the joined entities to be able to work on them.
I've used the dot syntax for selecting the count, but you could use the query syntax if you wanted. Over time, I've found dot synatx more natural.
NOTE: if you used navigation properties this would become much easier. Why are you not using them? With navigation properties you don't need to make the joins explicitly, as they are already available.
List<ReviewViewModel> listModel = new List<ReviewViewModel>();
context.dbRestaurant
.include("Review")
.include("Review.ReviewHelpful")
.include("Review.ReviewImage").ToList().ForEach((item) =>
{
ReviewViewModel model = new ReviewViewModel();
model.ID = item.ID
listModel.Add(model);
});
I have three tables
Products { pid, pname, price }
Orders { oid, odate }
OrderDetails { odid, oid, pid, qty, total }
I've a form which upon submission passes an array of OrderDetails which I want to store in database. But the tragedy here is I want to store current date in Order table at the same time. Now my Order table is getting populated with odate but nothing is getting inserted in my orderdetails table. And I'm using web api and the array is fetched correctly in controller. I guess this line
aOrder.AllOrders.Add(od);
in the controller has to be replaced with somethting else.
Order.cs
public class Order
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int oid { get; set; }
[DataType(DataType.DateTime)]
public DateTime odate { get; set; }
public virtual List<OrderDetail> AllOrders { get; set; }
public Order()
{
AllOrders = new List<OrderDetail>();
}
}
OrderDetail.cs
public class OrderDetail
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int odid { get; set; }
public int oid { get; set; }
public virtual Order order { get; set; }
public int pid { get; set; }
public int qty { get; set; }
public int total { get; set; }
public virtual Product Aproduct { get; set; }
public OrderDetail()
{
Aproduct = new Product();
}
}
OrderDetailsController.cs
private static readonly IOrderDetailRepository _orders = new OrderDetailRepository();
public Order Post(List<OrderDetail> orderDetails)
{
Order aOrder = new Order();
foreach(OrderDetail orderDetail in orderDetails)
{
OrderDetail od = new OrderDetail();
od.oid = orderDetail.oid;
od.pid = orderDetail.pid;
od.qty = orderDetail.qty;
od.total = orderDetail.total;
aOrder.AllOrders.Add(od);
}
aOrder.odate = DateTime.Now;
return _orders.Add(aOrder);
}
OrderDetailRepository.cs
public Order Add(Order order)
{
_db.Orders.Add(order);
_db.SaveChanges();
return order;
}
Can you try specifying a foreign key for OrderDetails:
public class OrderDetail
{
[ForeignKey("Order")]
public int oid { get; set; } //I'm guessing this is Order's PK
Beginner with entity framework and mvc here.
I have 2 table models:
UserProfile
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
and
ChatLogs
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
public override string ToString()
{
return "Person: " + Message + " " + UserId;
}
}
UserId in table ChatLogs is foreign key to UserPorfile UserId primary key.
I am trying to join these 2 tables in Asp.NET MVC 4
Tested SQL query:
select * from UserProfile as a join ChatLogs as b on a.UserId = b.UserId
Tested Linq query:
from b in db.ChatLogs
select new {
ChatLogId = b.ChatLogId,
Message = b.Message,
UserId = b.UserId,
Column1 = (int?)b.UserProfile.UserId,
UserName = b.UserProfile.UserName,
Email = b.UserProfile.Email
}
I am using software called "Linqer" for learning purposes. It conversts SQL to Linq.
ActionResult code:
private ChatLogContext db = new ChatLogContext();
public ActionResult Index()
{
var list = from b in db.ChatLogs
select new
{
ChatLogId = b.ChatLogId,
Message = b.Message,
UserId = b.UserId,
Column1 = (int?)b.UserProfile.UserId,
UserName = b.UserProfile.UserName,
Email = b.UserProfile.Email
};
var vm = new ChatLogsViewModel { LogListString = string.Join("\n", list) };
return View(vm);
}
ChatLogViewModel has only a string variable for printing list in view.
But I get an error:
'Chat.Models.ChatLogs' does not contain a definition for 'UserProfile' and no extension method 'UserProfile' accepting a first argument of type 'Chat.Models.ChatLogs' could be found (are you missing a using directive or an assembly reference?)
So do I have to connect those 2 entities somehow so they would know about each other?
Try this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
**public virtual ICollection<ChatLogs> ChatLogs { get; set; }**
}
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
**public virtual UserProfile UserProfile { get;set; }**
public override string ToString()
{
return "Person: " + Message + " " + UserId;
}
}
The easiest way to connect is to make Chatlogs available on the user as a List:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public List<ChatLog> ChatLogs{ get; set;}
}
Now you can do the following:
var users = Users.Include("ChatLogs");
Every user will now have its list of ChatLogs filled in correctly.