I have a method like this that works as expected.
[WebGet]
public IQueryable<BuildJobModel> GetCustomers()
{
var context = new MyDataContext(); // ADO.NET Entity Data Model
var query = from c in context.Customers
select new CustomerModel {
Id = c.Id,
Name = c.Name
};
return query;
}
But when I try to create a more complex query like this, it doesn't work.
[WebGet]
public IQueryable<BuildJobModel> GetCustomers()
{
var context = new MyDataContext(); // ADO.NET Entity Data Model
var query = from c in context.Customers
select new CustomerModel {
CustomerId = c.CustomerId,
Name = c.Name,
Orders = from o in c.Orders
select new OrderModel {
OrderId = o.OrderId,
Details = o.Details
}
};
return query;
}
The Models look like this:
public class CustomerModel
{
public int CustomerId { get; set; }
public string Name { get; set; }
public IEnumerable<OrderModel> Orders { get; set; }
}
public class OrderModel
{
public int OrderId { get; set; }
public string Details { get; set; }
}
The Exception:
Cannot serialize member Proj.CustomerModel.Logs of type System.Collections.Generic.IEnumerable`1[[Proj.OrderModel, Proj, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
My Goals:
I want to be able to expose an IQueryable interface.
I want to return nested data.
I want to use my own Models, not the ado.net entities
I want to hit the database with as few queries as possible (one would be best).
I would strongly recommend not exposing IQueryable as a return type of a WCF service.
WCF was designed to return data not queries
You lose control over the nature of the query: someone might potentially use this query in a way that is resource-intensive
Ideally, if you want to return collections use array or a generic list.
With respect to your list of goals:
Can you explain this? I don't see what IQueryable interface has to do with nested data.
You can still return arrays or lists of your models
You have better control over performance if you execute query locally and return results instead
Update: Have a look at WCF Data Services - that might be able to do what you want.
In case you're trying to return JSON: the build in JsonFormatter is not able to (de)serialize interfaces. You should try the JSON.NET Formatter from WebApiContrib.
I think you just need to use an array rather than IEnumerable, like so...
public class CustomerModel
{
public int CustomerId { get; set; }
public string Name { get; set; }
public OrderModel[] Orders { get; set; }
}
public class OrderModel
{
public int OrderId { get; set; }
public string Details { get; set; }
}
If you're serializing this to json/xml I suspect the built-in serializers don't know what to do with IEnumerable.
Related
I´m new to ASP.NET, I want to know if someone can explain how Insert works using a stored procedure. I'm using Dapper.
I have a product model like this:
using Dapper.Contrib.Extensions;
using System;
namespace Dto.Entities.Products
{
[Table("Product.ProductDetail")]
public class ProductDetail
{
[Key]
public int ProductDetailId { get; set; }
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
Now can someone explain me how can I do an insert in controller using a stored procedure? Regards
The quickest way is to handle all the logic in the controller as you said:
[HttpPost]
public async Task<IHttpActionResult> Post([FromBody]ProductDetail payload)
{
using (var connection = new SqlConnection("<your_connectionstring>"))
{
var parameters = new
{
ProductId = payload.ProductId,
Name = payload.Name,
Description = payload.Description
};
await connection.ExecuteAsync("<your_insert_sp>", parameters, commandType: CommandType.StoredProcedure).ConfigureAwait(false);
}
return Ok();
}
This is not the recommended way to go though.
You should always use a dto class instead of your real data model as the type going in or leaving your controller.
Also the db logic should be moved outside of the controller, for example to a repository class. Or you can use Mediatr to move your logic away from your action and keep your controllers thin.
I'm creating a web api odata controller and all is well until I try to add a derived field to the model. I don't want the field to exist in the database, and the field does not need to be queryable.
Given model;
public class Foo
{
public Int64 Id { get; set; }
public string SomeDatabaseSourcedField{ get; set; }
public string SomeDerivedField{ get; set; }
}
If I've got a controller like;
private FooDbContext db = new FooDbContext();
[Queryable]
public IQueryable<Foo> GetFoo()
{
return db.Foo;
}
How do it set the value of SomeDerivedField?
Note: I've tried marking the field as not mapped but it seems the field does not get returned in api calls.
Here is my answer, What I'm using is odata v4. http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/
I've successfully get the derived field in the response.
First, define a class which derives from foo:
public class MyFoo:Foo
{
public string SomeDerivedField{ get; set; }
}
Second, build the edm model:
public static IEdmModel GetModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MyFoo>("Foo");
builder.EntityType<MyFoo>().DerivesFromNothing();
builder.Namespace = typeof(MyFoo).Namespace;
return builder.GetEdmModel();
}
Third, retrieve the data:
public IHttpActionResult Get()
{
IQueryable<MyFoo> foos= db.Foos.Select(
t => new MyFoo()
{
Id = t.Id,
Name = t.Name,
SomeDerivedField= t.Name + SqlFunctions.StringConvert((double)t.Id)
});
return Ok(foos);
}
I am using Hot towel template and extended functionality of it by using breeze. I have used breeze.partial-entities.js file to conver breeze entities to proper dtos that can be used by knockout observables as shown below.
function dtoToEntityMapper(dto) {
var keyValue = dto[keyName];
var entity = manager.getEntityByKey(entityName, keyValue);
if (!entity) {
// We don't have it, so create it as a partial
extendWith = $.extend({ }, extendWith || defaultExtension);
extendWith[keyName] = keyValue;
entity = manager.createEntity(entityName, extendWith);
}
mapToEntity(entity, dto);
entity.entityAspect.setUnchanged();
return entity;
}
For few of the entities it is working properly and getting breeze data converted to entities but for one of the entity implementation is failing. Model for the same is given as below.
public class StandardResourceProperty
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int StandardResourceId{ get; set; }
public int InputTypeId{ get; set; }
public int ListGroupId{ get; set; }
public string Format{ get; set; }
public string Calculation{ get; set; }
public bool Required{ get; set; }
public int MinSize{ get; set; }
public int MaxSize{ get; set; }
public string DefaultValue{ get; set; }
public string Comment { get; set; }
public virtual StandardResource AssociatedStandardResource { get; set; }
public virtual List AssociatedList { get; set; }
}
The error i am getting is
TypeError: this[propertyName] is not a function
[Break On This Error]
thispropertyName;
breeze.debug.js (line 13157)
]
with code
proto.setProperty = function(propertyName, value) {
this[propertyName](value);
// allow set property chaining.
return this;
};
Please let me know . What can be possible issue with the implementation also , it would be great if i can get more suggestion on how to debug and trace such issues.
Let's back up. I do not understand what you mean by "convert breeze entities to proper dtos that can be used by knockout observables". Breeze entities are already configured as KO observables (assuming you are using the default Breeze model library configuration). What are you trying to do?
I suspect you are following along with the Code Camper Jumpstart course where it does a getSessionPartials projection query. That query (like all projections) returns DTOs - not entities - and maps them with the dtoToEntityMapper method into Session entities.
The CCJS dtoToEntityMapper method cannot be used with entities. It is for converting from a DTO to an Entity and takes DTOs - not entities - as input.
Goodbye to dtoEntityMapper
The dtoToEntityMapper method pre-dates the ability of Breeze to automate projection-to-entity mapping by adding .toType('StandardResourceProperty') to your projection query.
Here is what the CCJS getSessionPartials query could look like now:
var query = EntityQuery
.from('Sessions')
.select('id, title, code, speakerId, trackId, timeSlotId, roomId, level, tags')
.orderBy(orderBy.session)
.toType('Session');
If you go this way, be sure to set the default state of the isPartial flag to true in the custom constructor (see model.js)
metadataStore.registerEntityTypeCtor(
'Session', function () { this.isPartial = true; }, sessionInitializer);
Note that this.isPartial = true is the reverse of the CCJS example where the default was false.
Make sure that you set isPartial(false) when you query or create a full entity. In CCJS there are two places to do that: in the success-callback of getSessionById AND in createSession which would become:
var createSession = function () {
return manager.createEntity(entityNames.session, {isPartial: false});
};
I have a DTO which has a collection within it of another DTO which I populate server-side and send to the client. However, this inner DTO collection is not returned to the client.
I believe I need to use the [Include] and [Association] attributes so that WCF RIA services knows what to do, however my issue with this is there is no real association as such between the main DTO and the inner DTO collection, I am just using it to aggregate data from various sources for return to the client.
Is my understanding wrong in what I am trying to achieve, if not how do I get WCF RIA to send this inner DTO collection.
I should add that I am using automapper and want to achieve it using such.
Here is an example, I want to send back to the client in one chunk;
The competencies that the employee has.
The competencies that the employee requires for their job.
The GAP, which is the difference between 1 and 2.
public class CompetencyRequirementsDto
{
[Key]
public string CompanyId { get; set; }
[Key]
public string EmployeeNo { get; set; }
public string JobId { get; set; }
[Include]
[Association("EmployeeCompetencies","CompanyId, EmployeeNo","CompanyId, EmployeeNo")]
public IList<EmployeeCompetencyDto> EmployeeCompetencies { get; set; }
[Include]
[Association("JobCompetencies","JobId, CompanyId","JobId, CompanyId")]
public IList<JobCompetencyDto> JobCompetencies { get; set; }
[Include]
[Association("CompetencyGap", "JobId, CompanyId", "JobId, CompanyId")]
public IList<JobCompetencyDto> CompetencyGap { get; set; }
} }
Now item 1 works fine, but 2 and 3 don't? What I have found is that my DTO is created ok server side but when it gets to the client CompetencyGap(even when it has no values) has
been given JobCompetencies values.
If you are using ADO.Net Entity data model and using RIA Services against them then you have got an option to create associated metadata.
So to get the reference entities at you client side we need to modify both the our corresponding meta-data and as well as well the function of the domain service class which is fetching your data .
Here I am giving an example...
1. Just add [Include] attribute at the the top of the referenced data for example.
[MetadataTypeAttribute(typeof(Customer.CustomerMetadata))]
public partial class Customer
{
// This class allows you to attach custom attributes to properties
// of the Customer class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class CustomerMetadata
{
// Metadata classes are not meant to be instantiated.
private CustomerMetadata()
{
}
public int CustomerID { get; set; }
public string EmailAddress { get; set; }
public string FullName { get; set; }
[Include]
public EntityCollection<Order> Orders { get; set; }
public string Password { get; set; }
}
}
2. Modify the function in the domain service and add include there also for example.
public IQueryable<Customer> GetCustomers()
{
var res = this.ObjectContext.Customers.Include("Orders");
return res;
}
In your case the first part is done you just need to modify your domain service query to get reference entities.
I have an object from my domain model that has a child object. How can I use a criteria query to order based on a property of the child?
For example:
class FooType
{
public int Id { get; set; }
public string Name { get; set; }
public BarType Bar { get; set; }
}
class BarType
{
public int Id { get; set; }
public string Color { get; set; }
}
...
// WORKS GREAT
var orderedByName = _session.CreateCriteria<FooType>().AddOrder(Order.Asc("Name")).List();
// THROWS "could not resolve property: Bar.Color of: FooType"
var orderedByColor = _session.CreateCriteria<FooType>().AddOrder(Order.Asc("Bar.Color")).List();
What do I need to do to enable this scenario? I'm using NHibernate 2.1. Thanks!
You need to either add an alias or create a nested criteria for your child. Not sure how to do this in NHibernate, in Hibernate it's done via createCriteria() and createAlias() methods.
You would then use the alias as prefix in order by.
Update Hibernate code sample:
Criteria criteria = session.createCriteria(FooType.class);
criteria.createAlias("bar", "b");
criteria.addOrder(Order.asc("b.color"));
I imagine in NHibernate it would be quite similar, though with property/entity names uppercased. Here's an example from NHibernate documentation.