I am trying to map various markers on google map with a info window. It is all working fine till I try to pass a string through the controller.
I get the error below:
"LINQ to Entities does not recognize the method 'System.String GetMainPhoto(Int32)' method, and this method cannot be translated into a store expression."
I have read this is mainly caused because ToString method or some other methods are not valid to use. But, I am not really sure how to correct this error in this case.
Basically, I have a db - PropertyPhoto that holds the filename of the pictures. GetMainPhoto basically looks up all the rows and returns the main pic file name.
public string GetMainPhoto(int id)
{
return db.PropertyPhotos.Single(p => p.PropertyId == id && p.MainPic == true).PhotoLocation;
}
Controller is as follows:
public ActionResult Map()
{
if (Request.IsAjaxRequest())
{
var properties = websiteRepository.FindAllProperties();
var jsonProperties = from property in properties
select new JsonProperty
{
PropertyId = property.PropertyId,
NoOfBedroom = property.NoOfBedrooms,
Price = property.Price,
Address1 = property.PropertyAddress.Address1,
MainPicSrc = websiteRepository.GetMainPhoto(property.PropertyId),
Latitude = property.PropertyAddress.Latitude,
Longitude = property.PropertyAddress.Longitude
};
return Json(jsonProperties.ToList(), JsonRequestBehavior.AllowGet);
}
else
{
return View();
}
}
Try to eagerly load the data first by calling .ToList() before projecting into an anonymous type, otherwise EF doesn't know how to translate the websiteRepository.GetMainPhoto call to SQL:
var properties = websiteRepository.FindAllProperties().ToList();
Be careful though. By doing this you might be experiencing the SELECT N+1 problem because for each element of the initial resultset you will be sending a SQL query to fetch the MainPicSrc property.
A better approach would be to perform this join directly by the database and don't use the websiteRepository.GetMainPhoto call.
Related
I am trying to cast my http.get response to actual object -> in my specific case array of complex objects.
In a normal scenario, where you don't need any specific casting, you could do the following (simplified):
return this.httpClient.get(api, this._options_get)
.pipe(
map((response: any) => {
return response.value as NewProduct[];
})
);
As my need is to actually cast this to an object, I have created this static method which does that:
static toProduct(otherProduct: any): NewProduct {
let item = new NewProduct();
Object.keys(otherProduct).forEach(prop => {
if (typeof otherProduct[prop] === "object" && otherProduct[prop]) {
if (!item.hasOwnProperty(prop))
item[prop] = otherProduct[prop];
Object.assign(item[prop], otherProduct[prop]);
}
else
item[prop] = otherProduct[prop];
})
return item;
}
Under Object.assign I am taking already existing object which was initialized under first line and I am simply copying all the properties from the otherProduct to it. However I start to face problem when it comes to array of objects. Example (with simplified class):
export class Person {
name:string;
age:number;
addresses:Address[] = [];
}
export class Address {
street:string;
city:string;
fullAddress() : string { return this.street + this.city; }
}
As soon as I have this sort of array, I don't have any initial object in item. This means that there is no initial constructor of a class which results in simple Object. This is no error for JavaScript or TypeScript; however when I am trying to access internal method of a class (in our simplified case fullAddress(), I won't be able to.
The reason why I need that is that I am overriding toString() method on my sub-classes, which is necessary for MatTableDataSource when you use the filter method (which works with strings).
Is there a way how to retrieve elements from http.get() and properly map results to typed objects?
You're being too generic. You're creating objects of objects, not objects of Product with children of Addresses.
If you want to create a new product you're going to have to understand the relationship between the api's results and the data you want in the UI.
Because you're using classes and not interfaces and want to inherit the functions, the only way to get new Addresses into the new object is with the new keyword.
And you'll have to loop through. You're not going to find a shortcut for this. You're going to need to loop through the data and transform it. If your api is giving you an ApiPerson then you'll want to do something like this:
const addresses = apiPerson.addresses.map((apiAddress) => {
const address = new Address();
// map properties of apiAddress to address...
return address;
});
Now that you have the addresses, you can map the apiPerson to a new Person()'s properties and then set the newPerson.addresses = address.
In my API, I have to return a json composed of 2 distinct objects, one is a unique entity and the other a list. When I return each separatly there is no problem, but when I try to return them as a single json, the result is empty, even though my var in the return ok(var) contains what is supposed to be returned.
The controller looks like this:
[System.Web.Mvc.HttpGet]
public IHttpActionResult GetOne(int id)
{
var x = Service.GetOne(id);
return Ok(x)
}
(If I put a breakpoint on the return line, I can see that x contains the single object and the list that I want in my json)
"Service" is where I merge the 2 objects by caling a model constructor that takes the single object and the list as input, it looks like this:
public class mergedObject
{
IEnumerable<SingleObject> y;
IEnumerable<ListObject> z;
public mergedObject GetOne(int Id)
{
mergedObject x = new mergedObject(RepoSingleObject.getOne(Id) a,
RepoList.GetAll(Id) b)
this.y = a
this.z = b
}
It calls the corresponding repository(which contains the sql query) for each object and I know this part is working, since like I said earlier, I can properly return each object individually.
The console returns absolutely no error, I did everything the same way as with the individual objects, except for the merging and when I use breakpoints everything seems to go fine, but yet, using postman to test it, I always get an empty return. Does anyone have an explanation?
EDIT
The controller is derived from ApiController + added full definition of the mergedObject class up there. I use IEnumerable even on the single object because I reuse some part of the code where this single element is in fact a list, so I figured it would only be a list of 1 element and wouldn't cause any problem.
I want to create a JSON file for use as part of a simple web prototyping exercise. LinqPAD is perfect for accessing the data from my DB in just the shape I need, however I cannot get it out as JSON very easily.
I don't really care what the schema is, because I can adapt my JavaScript to work with whatever is returned.
Is this possible?
A more fluent solution is to add the following methods to the "My Extensions" File in Linqpad:
public static String DumpJson<T>(this T obj)
{
return
obj
.ToJson()
.Dump();
}
public static String ToJson<T>(this T obj)
{
return
new System.Web.Script.Serialization.JavaScriptSerializer()
.Serialize(obj);
}
Then you can use them like this in any query you like:
Enumerable.Range(1, 10)
.Select(i =>
new
{
Index = i,
IndexTimesTen = i * 10,
})
.DumpJson();
I added "ToJson" separately so it can be used in with "Expessions".
This is not directly supported, and I have opened a feature request here. Vote for it if you would also find this useful.
A workaround for now is to do the following:
Set the language to C# Statement(s)
Add an assembly reference (press F4) to System.Web.Extensions.dll
In the same dialog, add a namespace import to System.Web.Script.Serialization
Use code like the following to dump out your query as JSON
new JavaScriptSerializer().Serialize(query).Dump();
There's a solution with Json.NET since it does indented formatting, and renders Json dates properly. Add Json.NET from NuGet, and refer to Newtonsoft.Json.dll to your “My Extensions” query and as well the following code :
public static object DumpJson(this object value, string description = null)
{
return GetJson(value).Dump(description);
}
private static object GetJson(object value)
{
object dump = value;
var strValue = value as string;
if (strValue != null)
{
var obj = JsonConvert.DeserializeObject(strValue);
dump = JsonConvert.SerializeObject(obj, Newtonsoft.Json.Formatting.Indented);
}
else
{
dump = JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.Indented);
}
return dump;
}
Use .DumpJson() as .Dump() to render the result. It's possible to override more .DumpJson() with different signatures if necessary.
As of version 4.47, LINQPad has the ability to export JSON built in. Combined with the new lprun.exe utility, it can also satisfy your needs.
http://www.linqpad.net/lprun.aspx
Seeing as EF doesn't fully support SQL spatial data types i was hoping to work around it for the time being.
Although working with code first I have a stored procedure that uses the sql2008 r2 spatial datatype range formulas. My objective was to return the distance from each object to the queried location.
With some help from the answers below I've modified my code to the follwing
I have a simple class 'Vacancy'
public class Vacancy
{
public int Id { get; set; }
public string Title { get; set; }
}
public class VacancyWithRange : Vacancy
{
public int Calculated { get; set; }
}
I'm then querying the context directly in a method like this
public List<Vacancy> Get()
{
Context db = new Context();
var context = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)(db)).ObjectContext;
db.Database.Connection.Open();
DbCommand cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "select *, [Calculated distance from query to vacancy object] as Calculated from Vacancies";
cmd.CommandType = System.Data.CommandType.Text;
var result = context.Translate<VacancyWithRange>(cmd.ExecuteReader()).ToList();
foreach (VacancyWithRange v in result)
{
db.Vacancies.Attach(v);
}
return result;
}
The foreach loop at the end of the Get() method successfully attaches the VacancyWithRange to Vacancies so I have access to related objects via lazy loading.
As far as I can tell this seems to be a viable solution for the time being to get 'custom' calculated data back from the database with code first.
Thanks for reading.
I didn't try it but I expect it will not work. The reason is that Vacancy is mapped entity and once you try to Translate query result into mapped entity, EF will use defined mapping for the translation. Here comes the problem: in the mapping you specified that the Calculated property as not mapped so the mapping will not populate this property.
What you can try is defining a new type which is not mapped and includes your Calculated property (you can also try to derive custom type from Vacancy and just add the calculated property). Then you can try to use:
using (Context db = new Context())
{
string queryText = "select *, [Some random calculated value] as Calculated from Vacancies";
var data = db.Database.SqlQuery<VacancyWithCalculation>(queryText);
}
Names and types of properties in your new type must match names and types of columns in result sets because there is no explicit mapping. EF only use convention by matching result set columns and class properties by name.
Btw. context is disposable so don't forget to release it.
You should specify in your mapping DatabaseGeneratedOption.Computed i.e. using HasDatabaseGeneratedOption method.
Depending on how I map my linq queries to my domain objects, I get the following error
The member 'member' has no supported translation to SQL.
This code causes the error:
public IQueryable<ShippingMethod> ShippingMethods {
get {
return from sm in _db.ShippingMethods
select new ShippingMethod(
sm.ShippingMethodID,
sm.Carrier,
sm.ServiceName,
sm.RatePerUnit,
sm.EstimatedDelivery,
sm.DaysToDeliver,
sm.BaseRate,
sm.Enabled
);
}
}
This code works fine:
public IQueryable<ShippingMethod> ShippingMethods
{
get
{
return from sm in _db.ShippingMethods
select new ShippingMethod
{
Id = sm.ShippingMethodID,
Carrier = sm.Carrier,
ServiceName = sm.ServiceName,
EstimatedDelivery = sm.EstimatedDelivery,
DaysToDeliver = sm.DaysToDeliver,
RatePerUnit = sm.RatePerUnit,
IsEnabled = sm.Enabled,
BaseRate = sm.BaseRate
};
}
}
This is my testmethod I am testing with:
[TestMethod]
public void Test_Shipping_Methods() {
IOrderRepository orderRepo = new SqlOrderRepository();
var items = orderRepo.ShippingMethods.Where(x => x.IsEnabled);
Assert.IsTrue(items.Count() > 0);
}
How does the way in which I instantiate my object affect the linq to sql translation?
Thanks
Ben
It tries to map the entire linq query to SQL, including all method and property calls. The only exceptions are the object initializer syntax (both for anonymous as named types) and extension methods that themselves map to SQL (.Count() for instance).
Short story: you cannot use non-default constructors with Linq to SQL or Entity Framework.
The most significant issue here is that you are mixing predicate and projection semantics.
Once you project (i.e. with select), it is no longer safe to use the Where extension until you materialize the results with ToList(), ToArray() or similar. The second case just happens to work because the projection is completely transparent - all you are doing is property assignments, and to the same class. Constructors don't fall into this category; as the error message says, there's no equivalent representation of a constructor invocation in SQL Server.
Why do you need to do this projection anyway? The whole property could be replaced with just:
return _db.ShippingMethods.AsQueryable();