EntityFramework Code First, is it possible to map this - ef-code-first

is there even a possibility to map this to one entity ?
select x,y,z, (select count(*) from othertable where tableid=table.id) as othertablecount
from table t
i want to map this to a class that looks like this:
public class Stuff
{
public string x { get; set; }
public string y { get; set; }
public string z { get; set; }
public int count { get; set; }
}

No. You should map it properly as a collection and use a projection for querying:
class Stuff
{
...
public virtual ICollection<OtherStuff> { get; set; }
}
var stuffWithCount = from stuff in myContext.Stuff
select new
{
stuff.x, ...
count = stuff.OtherStuff.Count()
};

Related

ServiceStack OrmLite CustomSelect not working?

I'm trying to use the feature documented here :
https://github.com/ServiceStack/ServiceStack.OrmLite#custom-sql-customizations
This is how I'm using it:
var q = Db.From<MemberAccess>().LeftJoin<Member>();
return Db.Select<MemberResponse>(q);
Response object:
public class MemberResponse
{
public Guid Id { get; set; }
public string MemberFirstName { get; set; }
public string MemberLastName { get; set; }
public string MemberEmail { get; set; }
[Default(OrmLiteVariables.SystemUtc)]
public string AccessedOn { get; set; }
[CustomSelect("CONCAT(LEFT(Member.FirstName, 1),LEFT(Member.LastName,1))")]
public string MemberInitial { get; set; }
}
It seems like whatever I put in CustomSelect doesn't get used. Maybe, I'm not using this correctly? Also, the Default attribute doesn't work either.I tried that as it was an example from the doco.
Any idea will be appreciated.
Thanks in advance.
The [CustomSelect] only applies to the source table. Selecting the results in a custom type is used to map the returned resultset on the MemberResponse type, it doesn't have any effect on the query that gets executed.
Likewise with [Default(OrmLiteVariables.SystemUtc)] that's used to define the default value when creating the table which is only used when it creates the Column definition, so it's only useful on the source Table Type.
Both these attributes should only be added on the source MemberAccess to have any effect, which your mapped MemberResponse can access without any attributes, e.g:
public class MemberResponse
{
public Guid Id { get; set; }
public string MemberFirstName { get; set; }
public string MemberLastName { get; set; }
public string MemberEmail { get; set; }
public string AccessedOn { get; set; }
public string MemberInitial { get; set; }
}
Sql.Custom() API
The new Sql.Custom() API added in v4.5.5 that's available on MyGet will let you select a custom SQL Fragment, e.g:
var q = Db.From<MemberAccess>().LeftJoin<Member>()
.Select<MemberAccess,Member>((a,m) => new {
Id = a.Id,
MemberFirstName = m.FirstName,
MemberLastName = m.LastName,
MemberEmail = m.Email,
MemberInitial = Sql.Custom("CONCAT(LEFT(Member.FirstName,1),LEFT(Member.LastName,1))")
});
return Db.Select<MemberResponse>(q);

Post JSON Array in Asp.Net Web API

Hi every one I am new to ASP.Net Web API and I want to Post JSON array data any get there response.
My JSON POST Array format is
{
"User_Id":"admi12n#1234","Key_Code":"3F-47-AB-84-9F-EB-D6-6B-9C-62-CC-85-98-4D-28-6B",
"ProductDetails": [
{"Product_Id":"ELT-7035","Price":"999","Quantity":"5"},
{"Product_Id":"ELT-1254","Price":"1024","Quantity":"3"}
]
}
And I want response as follows
{
"User_Id":"admi12n#1234","Key_Code":"3F-47-AB-84-9F-EB-D6-6B-9C-62-CC-85-98-4D-28-6B",
"OrderID":"Ord-021","Name":"Sabyasachi"
"ProductDetails": [
{"Product_Id":"ELT-7035","Price":"999","Quantity":"5"},
{"Product_Id":"ELT-1254","Price":"1024","Quantity":"3"}
]
}
I generate OrderID as Random and Name from posted User_Id. Here I want to post multiple product in one order.
My Order class is as follows
public class Order
{
[Key]
public long ID { get; set; }
public string Order_Id { get; set; }
public string Product_Id { get; set; }
public long Quantity { get; set; }
public long Amount { get; set; }
public string User_Id { get; set; }
public string Key_Code { get; set; }
public DateTime Order_Date { get; set; }
public DateTime Modified_Date { get; set; }
}
And my Product class as follows
public class Product
{
[Key]
public int Id { get; set; }
public string Product_Code { get; set; }
public string Product_Name { get; set; }
public string Product_Category { get; set; }
public string Product_Description { get; set; }
public string Quantity { get; set; }
public string Price { get; set; }
public string Image { get; set; }
public DateTime Created_Date { get; set; }
public DateTime Modified_Date { get; set; }
}
I am not able to ind the best way to post the order
public Order Add(Order odrerDetails) //This will not give array of data for products
{
using (var context = new EcommerceDBContext())
{
odrerDetails.Order_Id = Helper.Random(7); //Generate random orderID from my class
odrerDetails.Created_Date = DateTime.Now;
odrerDetails.Modified_Date = DateTime.Now;
//How to Save other details
context.objOrderListing.Add(odrerDetails);
context.SaveChanges();
return odrerDetails;
}
}
In API controllers my code is as follows
public HttpResponseMessage PostOrder([FromBody] Order_Listing orderData)
{
orderData = repository.Add(orderData);
var response = Request.CreateResponse<Order_Listing>(HttpStatusCode.Created, orderData);
string uri = Url.Link("DefaultApi", new { customerID = orderData.ID });
response.Headers.Location = new Uri(uri);
return response;
}
Please help me how to achieve this.
There are several issues with your code:
Your Order and Product classes do not reflect the structure of
your JSON.
The Order class contains product details in a 1:1
relationship. Based on the JSON I assume you want a 1:n relationship.
Properties in your JSON need to have the same name as
in your classes or they won't be mapped.
Change your classes to the following and it should work.
Of course you could also change the property names in your JSON.
If you can't or don't want to change your property names, consider using DTOs
public class Order
{
public string User_Id { get; set; }
public string Key_Code { get; set; }
public string OrderID { get; set; }
public string Name { get; set; }
public List<Product> ProductDetails { get; set; }
// add the rest of your properties
}
public class Product
{
public string Product_Id { get; set; }
public string Price { get; set; }
public string Prd_Qty { get; set; }
// add the rest of your properties
}
Update: added code for Add method and Api method
Your Add method would look like this:
public Order Add(Order orderWithDetails)
{
using (var context = new EcommerceDBContext())
{
orderWithDetails.Order_Id = Helper.Random(7); //Generate random orderID from my class
orderWithDetails.Created_Date = DateTime.Now;
orderWithDetails.Modified_Date = DateTime.Now;
context.objOrderListing.Add(orderWithDetails);
// Save each Product
foreach (var detail in orderWithDetails.ProductDetails)
{
//whatever you need to do in your db-context
context.objOrderDetails.Add(detail); // just an example
}
context.SaveChanges();
return orderWithDetails;
}
}
The signature of your Api method looks wrong. What is Order_Listing? This should be Order, unless it's a DTO, in wich case you need a method to get an Order from Order_Listing.
public HttpResponseMessage PostOrder([FromBody] Order orderData)
{
orderData = repository.Add(orderData);
var response = Request.CreateResponse<Order_Listing>(HttpStatusCode.Created, orderData);
string uri = Url.Link("DefaultApi", new { customerID = orderData.ID });
response.Headers.Location = new Uri(uri);
return response;
}
A few more remarks:
If it is indeed a 1:n relationship, you probably need a property Product.OrderId.
The Order class should not have any reference to Product except for the list.
Quantity and Price should most likely not be String but numerical values, e.g. decimal.
If Order.ID is your primary key, then having Order.Order_ID is really confusing. Consider renaming it to Order.Order_Number.
public class Order
{
public string User_Id { get; set; }
public string Key_Code { get; set; }
public string OrderID { get; set; }
public string Name { get; set; }
public Product[] ProductDetails { get; set; }
}

Return List to View with only unique numbers

My application returns a list from the table OrderRows like this:
var allOrders = db.OrderRows.ToList();
And this is how my class looks like for the OrderRows:
public class OrderRows
{
public int ID { get; set; }
[Display(Name = "Produktid")]
public int ProductID { get; set; }
[Display(Name = "Orderid")]
public int OrderID { get; set; }
[Display(Name = "Pris")]
public float Price { get; set; }
public virtual Orders Orders { get; set; }
}
The Order class:
public class Orders
{
public int ID { get; set; }
[Display(Name = "Orderdatum")]
public DateTime Orderdate { get; set; }
public int CustomersID { get; set; }
public virtual Customers Customers { get; set; }
public virtual ICollection<OrderRows> OrderRows { get; set; }
}
I'm doing a webshop and I have made it very simple. The table can have several OrderID, but when I pass the list to the view, I would like to make some kind of sorting and only view unique orders. Instead of a list like this:
1
1
1
2
2
3
3
3
I would like it like this:
1
2
3
My thought is then to use this number and be able to select that order and change its content. Would it be possible to return a list with only unique numbers?
You can use Distinct() extension method
if you want unique orderIds
var allOrders = db.OrderRows.ToList().Select(o=>o.OrderID ).Distinct()
if you want unique orderIds with first order row
var allOrders=db.OrderRows.ToList().GroupBy(o=>o.OrderID ).Select(x=>x.First()).ToList()
try this:-
var allOrders = db.OrderRows.ToList();
var distinctOrders=allOrders.Distinct();
view(distinctOrders);
If I am not wrong, What you want is a GroupBy
Your query goes like-
var allOrders=db.OrderRows.GroupBy(o=>o.Orders).ToList()
Rendering it to View-
#foreach(var order in allOrders) {
<p>#order.Key.ID</p>
foreach(var orderItem in order.ToList()){
<ul>
<li>#orderItem.ProductID, #orderItem.Price</li>
</ul>
}
}

AutoMapper with auto-incremented values

public class OrderDTO
{
public string ClientName { get; set; }
public ICollection<OrderDetailDTO> Details { get; set; }
}
public class Order
{
public string ClientName { get; set; }
public ICollection<OrderDetail> Details { get; set; }
}
public class OrderDetailDTO
{
public int Quantity { get; set; }
public string ProductName { get; set; }
}
public class OrderDetail
{
public int OrderId { get; set; }
public int Quantity { get; set; }
public string ProductName { get; set; }
}
Let's say there are 4 OrderDetailDTO, I want to have the mapped OrderDetail instances with auto-incremented integer values. What I am doing now is post-process the mapped instance.
var mappedOrder = Mapper.Map<OrderDTO, Order>(orderDto);
var orderId = 1;
foreach (OrderDetail detail in mappedOrder.Details)
{
detail.OrderId = orderId++;
}
How can I configure the mapping options, so that the mapped ICollection<OrderDetail> contains 4 OrderDetail instances with OrderId as 1, 2, 3, 4?
You could configure AutoMapper to do this with AfterMap:
Mapper.CreateMap<OrderDTO, Order>()
.AfterMap((src, dest) =>
{
int orderId = 1;
foreach (OrderDetail detail in dest.Details)
{
detail.OrderId = orderId++;
}
});
I don't think there's really a "cleaner" way to do it using AutoMapper.
I use the following method which is much simpler and can be written in a base class or an extension method. The example here uses Generics but can be easily transformed
protected virtual IEnumerable<T> ConvertCsvLines(IEnumerable<TV> lines)
{
var lineNumber = 0;
return lines.Select(x =>
{
var retVal = Mapper.Map<TV, T>(x);
retVal.LineNumber = lineNumber++;
return retVal;
});
}

LinqPad Query to Visual Studio - how to use a nested query to populate a viewmodel

This is a follow up to an earlier question.
I want to populate a ViewModel, which has 3 properties, and one list of Occ class (which also has 3 properties.
public class RatesViewModel
{
public string TypeName { get; set; }
public long TypeID { get; set; }
public int TypeCount { get; set; }
public virtual IQueryable<Occ> Occs { get; set; }
}
public class Occ
{
public string occ { get; set; }
public decimal ratetocharge { get; set; }
public int numOfOcc { get; set; }
public virtual RatesViewModel RatesViewModel { get; set; }
}
When I run the following Linq query in LinqPad:
var rooms = tblRoom
.GroupBy(p => p.tblType)
.Select(g => new
{
TypeName = g.Key.type_name,
TypeID = g.Key.type_id,
TypeCount = g.Count(),
Occs = rates.Where(rt => rt.type_id == g.Key.type_id &&
(
(rt.type_id == g.Key.type_id)
))
.GroupBy(rt => rt.occ)
.Select(proj => new
{
occ = proj.Key,
ratetocharge = proj.Sum(s => s.rate),
numOfOcc = proj.Count()
})
});
rooms.Dump();
...as before, it correctly returns the data model I'm looking for:
...and when I click on Occs it drills down into the Occs class:
The complete view in LinqPad is:
My query in Visual Studio is:
var rooms = dbr.Rooms
.GroupBy(p => p.RoomTypes).Select(g => new RatesViewModel
{
TypeName = g.Key.type_name,
TypeID = g.Key.type_id,
TypeCount = g.Count()
,
Occs = db.Rates.Where(rt => rt.type_id == g.Key.type_id &&
(
(rt.type_id == g.Key.type_id)
))
.GroupBy(rt => rt.occ)
.Select(proj => new Occ
{
occ = proj.Key,
ratetocharge = proj.Sum(s => s.rate),
numOfOcc = proj.Count()
})
})
.ToList();
However when running this, I get an error:
The specified LINQ expression contains references to queries that are associated with different contexts.
I think I understand the error - but I'm not sure how to separate the query into 2 separate queries, and then join those query results together again to get my original results set.
My model classes are:
public class Rates
{
public int id { get; set; }
public long type_id { get; set; }
public DateTime ratedate { get; set; }
public decimal rate { get; set; }
public string occ { get; set; }
public List<RoomType> Type { get; set; }
}
public class Rental
{
[Key]
public long rental_id { get; set; }
public long room_id { get; set; }
public DateTime check_in { get; set; }
public DateTime check_out { get; set; }
public virtual Room Room { get; set; }
}
public class Room
{
[Key]
public long room_id { get; set; }
public long type_id { get; set; }
public virtual RoomType RoomTypes { get; set; }
public virtual ICollection<Rental> Rentals { get; set; }
}
public class RoomType
{
[Key]
public long type_id { get; set; }
public string type_name { get; set; }
public IQueryable<Rates> Rates { get; set; }
public virtual ICollection<Room> Room { get; set; }
}
Can anyone help either review my query or models, so it works with one query, or show me how to separate the query into two, and then combine the result sets?
Thank you,
Mark
apitest.Models.RoomContext' does not contain a definition for 'Rates'...
(your comment on hydr's answer)
Well, there you go: not only two different context instances but two different context classes. I suspect your linqpad query was directly against the database connection, which means it used one linq-to-sql DataContext (created on the fly).
You need to use one context class (and one instance of it) in your query. And connect to it in Linqpad to make sure you test the same query provider as Visual Studio.
dbr and db seem to be two different instances of the same context. But in one query you should only use one context. So I would suggest the following:
Occs = dbr.Rates.Where(rt => rt.type_id == g.Key.type_id && ....
If this doesn't help can you quote the lines where you initialize the contexts?

Resources