migrating old ado.net web app to EF - entity-framework

consider this query
Select (some properties from all 3 Tables)
From PageWidgets pw LEFT JOIN PageWidgetDefinition pwd
On pwd.ID = pw.WidgetID
LEFT JOIN PageWidgetSkin pws
ON pws.ID = pw.SkinInstanceID
LEFT JOIN PageWidgetSkinRows pwsr
On pwsr.SkinID = pws.ID Where pw.PageID = *x*
Order By (some properties)
in old implementation, it reads widgets on a page & their skin & i have a function looping through rows returned & make a pagewidget by its skin & its widget instance.
each widget has three row for its skin, and finally we receive a List that has everything it needs to operate
i have these classes in EF
public partial class Widget: BaseEntity {
public int ID { get; set; }
public int PageTemplateID { get; set; }
public PageTemplate PageTemplate { get; set; }
public int WidgetDefinitionID { get; set; }
public WidgetDefinition WidgetDefinition { get; set; }
public int WidgetSkinID { get; set; }
public WidgetSkin WidgetSkin { get; set; }
//other properties omitted
}
public partial class WidgetDefinition: BaseEntity {
public int ID { get; set; }
public string Title { get; set; }
//other properties omitted
public virtual ICollection<Widget> Widgets { get; set; }
}
public partial class WidgetSkin: BaseEntity {
public int ID { get; set; }
public string Name { get; set; }
//other properties omitted
public virtual ICollection<Widget> Widgets { get; set; }
public virtual ICollection<WidgetSkinRow> WidgetSkinRows { get; set; }
}
public partial class WidgetSkinRow: BaseEntity {
public int ID { get; set; }
public int WidgetSkinID { get; set; }
public virtual WidgetSkin WidgetSkin { get; set; }
}
do i need an extra bussiness layer doing the same thing?
using EF, I want to have only one trip to DB.

You can use "eager loading" method to do this.
Your query will then look something like this:
using (var entities = new WidgetEntities() )
{
var query = from w in entities.Widgets.Include("WidgetDefinition").Include("WidgetDefinition.Widgets").Include("WidgetSkins").Include("WidgetSkins.WidgetSkinRows")
where w.Page = *x*
order by w.someproperty
select w;
Widget myWidget = query.First();
}

Related

How To Make An Editable EF Select Query for DevExpress Grid Control?

I'm working on a cinema application which allows users to surf through movies, cinema places and allows them to buy or reserve tickets. If a user reserved a ticket online, then the ticket must be activated in 12 hours by sellerperson who also uses the same program. I need to show the ticket informations on grid and need to make editable. Here's my database classes that must be included in query and have relationship with Sale class. (I want to select objects from Sale class which includes ti's related classes: Ticket, customer, movie, status and saloon infos.
Sale Class:
public class Sale
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
public int CustomerId { get; set; }
[ForeignKey("StatusId")]
public virtual Status Status { get; set; }
public int StatusId { get; set; }
public virtual Seller Seller { get; set; }
public DateTime SellDate { get; set; }
public double Price { get; set; }
[ForeignKey("TicketID")]
public virtual Ticket Ticket { get; set; }
public int TicketID { get; set; }
}
Ticket Class:
public class Ticket
{
public Ticket()
{
Seats = new List<Seat>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("MovieId")]
public virtual Movie Movie { get; set; }
public int MovieId { get; set; }
public virtual List<Seat> Seats { get; set; }
public virtual TimeSpan SeanceTime { get; set; }
public bool IsActive { get; set; }
public DateTime BuyDate { get; set; }
[ForeignKey("SaloonId")]
public virtual Saloon Saloon { get; set; }
public int? SaloonId { get; set; }
public string TicketNumber { get; set; }
}
Customer Class:
public class Customer
{
public Customer()
{
Sales = new List<Sale>();
CreditCards = new List<CreditCard>();
}
[Key]
public int UserID { get; set; }
public virtual List<Sale> Sales { get; set; }
public virtual User User { get; set; }
[DataType(DataType.CreditCard)]
public virtual List<CreditCard> CreditCards { get; set; }
}
User Class:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
Status Class(Holds info of tickets. Bought or reserved.)
public class Status
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public bool IsRez { get; set; }
public bool IsBuy { get; set; }
public bool IsCancel { get; set; }
public bool IsPaid { get; set; }
}
Saloon Class:
public class Saloon
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public double salePrices { get; set; }
}
Movie Class:
public class Movie
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
}
I can't edit because in my select query I'm using anonymous type for selection. My query code:
var Source = entities.Sales.Where(w => w.Ticket.Saloon.CinemaPlace.ID == seller.CinemaPlace.ID).Select(s => new
{
CustomerName = s.Customer.User.Name,
CustomerSurname = s.Customer.User.Surname,
SalePrice = s.Price,
s.Status.IsBuy,
s.Status.IsCancel,
s.Status.IsPaid,
s.Status.IsRez,
MovieName = s.Ticket.BuyDate,
s.Ticket.Movie.Name,
SaloonName = s.Ticket.Saloon.Name,
s.Ticket.SeanceTime,
s.Ticket.TicketNumber
}).ToList();
RezervationsGrid.DataSource = Source3;
But in the grid, the datas couldn't be edited. Then I tried to join every single table using Linq to Entities queries but it didn't help either. Is there a way make a datasource from my related objects that allows edit option in grid? Thanks.
Anonymous types (those that you can declare via the new operator in the Select method) cannot have writable properties in .NET. That's why the grid is not editable. To take advantage of in-place editing, you need to instantiate objects of a real CLR type.
For this, you can declare a special ViewModel class with public properties that you should populate with values in the Select method using object initializer.
.Select(s => new SaleViewModel() {
CustomerName = s.Customer.User.Name,
SalePrice = Price
})
Note that you should not move the property initialisation logic to the ViewModel constructor to use it this way:
.Select(s => new SaleViewModel(s))
The object initialiser is the expression tree, which Entity Framework can translate into an SQL query. The constructor is just a method reference, so Entity Framework will reject such an expression. If you would like to use this approach, you will need to call the ToList method before the Select.
SaleViewModel can have the method accepting the DbContext class to save changes.
You also can select the Sale instances and use complex property paths in columns' field names (such as "Customer.User.Name"). This can probably help you to simplify the saving logic, as you will not need to find a model specific to a certain view model and copy modified property values.

Returning Entity with its children

Hi I am trying to return all vehicles with their recorded mileage through an api using ASP.Net Core with the following code:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.Include(m=>m.Mileages).ToList();
}
However this only returns the first vehicle with its mileages and not the others (there are five dummy vehicles in the db all with an initial mileage).
If I change the code to:
// GET: api/values
[HttpGet]
public IEnumerable<Vehicle> Get()
{
return _context.Vehicles.ToList();
}
it returns the full list of vehicles but no mileage.
My class files are:
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<Mileage> Mileages { get; set; }
}
and
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public Vehicle Vehicle { get; set; }
}
thanks for looking!
Tuppers
you can have them auto-load (lazy loading) using proxies... but for that, your foreign entities and collections must be marked virtual in your POCOs:
public class Mileage
{
public int Id { get; set; }
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
//Navigation Properties
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
public class Vehicle
{
public Vehicle()
{
Mileages = new List<Mileage>();
}
public int Id { get; set; }
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public virtual ICollection<Mileage> Mileages { get; set; }
}
The proxy creation and lazy loading turned on, but that's the default in EF6.
https://msdn.microsoft.com/en-us/data/jj574232.aspx
Let me know if this works.
Well after a lot of searching I managed to find a solution. I used the following:
[HttpGet]
public IEnumerable<VehicleDto> Get()
{
var query = _context.Vehicles.Select(v => new VehicleDto
{
Registration = v.Registration,
Make = v.Make,
Model = v.Model,
Marked = v.Marked,
Mileages = v.Mileages.Select(m => new MileageDto
{
MileageDate = m.MileageDate,
RecordedMileage = m.RecordedMileage
})
.ToList(),
})
.ToList();
return (IEnumerable<VehicleDto>) query.AsEnumerable();
this doesn't seem to be the most elegant way of doing this, if anyone could offer any advice but it does return what is required.
The DTO's look like:
public class VehicleDto
{
public string Registration { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Marked Marked { get; set; }
public ICollection<MileageDto> Mileages { get; set; }
}
and
public class MileageDto
{
public DateTime MileageDate { get; set; }
public string RecordedMileage { get; set; }
}
Thanks for taking the time to look at this
Tuppers

Eager loading of derived class in Entity Framework

I have a model like this:
public abstract class Point
{
public int Id { set; get; }
public string Name_F { set; get; }
public byte Side { set; get; }
}
public class Place : Point
{
public bool IsATM { set; get; }
public bool Is24h { set; get; }
public string Tel { set; get; }
public virtual Category Category { set; get; }
}
public class Street : Point
{
public bool IsWalkway { set; get; }
}
I want to load all of the Point table records including records for Place and Street which are derived from Point class.
I used this but I didn't get any data:
var points = Context.Points
.OfType<Place>()
.Include(p => p.SubCategory)
.Concat<Points>(Context.Points.OfType<Street>());
I would like to get Places and Streets in the same query.
Any idea?

Get multiple tables data through Entity Framework with Generic Repository and Unit Of work

I am working on Web-API project and using Entity Framework with Generic Repository and Unit Of work. Basically i follow a tutorial for this.
Here is my table architecture.
Entity
public class ProductEntity
{
public int ProductId { get; set; }
public string ProductCode { get; set; }
public string ProductName { get; set; }
public string ProductDescription { get; set; }
public string ProductImgName { get; set; }
public bool IsActive { get; set; }
public int PrimaryCatId { get; set; }
public int SecondaryCatId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public System.DateTime CreateDate { get; set; }
public List<PrimaryProductEntity> objPrimaryProduct { get; set; }
public List<SecondaryProductEntity> objSecondaryProduct { get; set; }
}
public class PrimaryProductEntity
{
public int PrimaryCatId { get; set; }
public string PrimaryCatName { get; set; }
}
public class SecondaryProductEntity
{
public int SecondaryCatId { get; set; }
public string SecondaryCatName { get; set; }
public int PrimaryCatId { get; set; }
}
Services Code
public IEnumerable<BusinessEntities.ProductEntity> GetAllProducts()
{
var products = _unitOfWork.ProductRepository.GetAll().ToList();
var primaryProducts = _unitOfWork.PrimaryProductRepository.GetAll().ToList();
var secondaryProducts = _unitOfWork.SecondaryProductRepository.GetAll().ToList();
if (products.Any())
{
Mapper.CreateMap<tblProduct, ProductEntity>();
var proInfo = from P in products
join PP in primaryProducts on P.PrimaryCatId equals PP.PrimaryCatId
join SP in primaryProducts on P.SecondaryCatId equals SP.SecondaryCatId
select P;
var productsModel = Mapper.Map<List<tblProduct>, List<ProductEntity>>(proInfo);//getting error
return productsModel;
}
return null;
}
i know my implementation is wrong, i don't know what to write in code for fetch data from multiple tables. Please help me.
Required Data
ProductID,ProductName, PrimaryCatName, SecondaryCatName,Price, Quantity
Your Product Entity class Doesn't require a List<PrimaryProductEntity> and List<SecondaryProductEntity>. I suppose according to your class diagram Each Product is associated with one PrimaryProductEntity and one SecondaryProductEntity.
Once your model class is corrected, you would be able to access the properties of the navigation. I am not so good with writing a Query the way you want. But i hope you could get an idea of what you should be doing

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. - for chiled entity

Following is my entities.
public class Expense
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ExpenseId { get; set; }
[Required]
public int CategoryId{ get; set; }
[ForeignKey("CategoryId")]
public virtual Category Category { get; set; }
public virtual List<EditedExpense> EditedExpenses { get; set; }
}
public class EditedExpense
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int EditedExpenseId { get; set; }
[Required]
public int CategoryId{ get; set; }
[ForeignKey("CategoryId")]
public virtual Category Category { get; set; }
public int ExpenseId { get; set; }
}
public class Category
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int CategoryId{ get; set; }
public string Title
public virtual List<Expense> Expenses { get; set; }
public virtual List<EditedExpense> EditedExpenses { get; set; }
}
I have used this code to get all Expanses and EditedExpanses
var expenses = db.Expenses.Include(exp => exp.EditedExpenses).Include(exp => exp.Category);
return View(expenses.ToList());
Now I want to go through all Expanses and thier EditedExpanse using two foreach loop like following, but it casts an exception when it tries to get the Category of the EditedExpense:
foreach(exp in expansesList)
{
foreach(editedExp in exp.EditedExpanses)
{
<text>#editedExp.Category.Title</text>
}
}
var expenses = db.Expenses
.Include(exp => exp.EditedExpenses)
.Include(exp => exp.Category);
This includes the Category property of Expense, not the one of EditExpense.
Use:
var expenses = db.Expenses
.Include(exp => exp.EditedExpenses.Select(z => z.Category));
EDIT:
Just to clarify about the exception you had: you currently are using lazy loading, so you can't lazy load navigation properties once your EF context has been disposed. As a side note, I always recommend deactivating lazy loading. See https://stackoverflow.com/a/21379510/870604