Adding manually defined service operations inside initService for odata service - jaydata

I have a WebApi controller derived from ApiController that has a service operation:
public class Airports2Controller : ApiController
{
protected airportEntities db = new airportEntities ();
[Queryable]
[HttpGet]
public IQueryable<Airport> GetAirportsWithinRadius(int airportId, int radius)
{
//var radius = (int)parameters["radius"];
//var airportId = (int)parameters["airportId"];
var resultAirports = GetAirportsWithinRadius2(airportId, radius);
return resultAirports;
}
private IQueryable<Airport> GetAirportsWithinRadius2(int airportId, int radius)
{
var airports = db.Airports.SqlQuery("select * from Airport a where (select GeoLocation from airport where Id = #p0).STDistance(a.GeoLocation)/1.852/1000.00 < #p1", airportId, radius);
var airportIds = airports.Select(a => a.Id);
var resultAirports = db.Airports.Where(a => airportIds.Contains(a.Id));
return resultAirports;
}
}
I also have an odata service for the Airport entity. Since .net webapi odata doesn't yet have support for odata functions (service operations?), I needed to create a secondary controller (that's not derived from ODataController).
What I want to do now with jaydata is to extend the context to have the service operation in addition to the odata stuff, once the db is initialized in initService:
$data.initService("http://localhost:2663/odata");
service.then(function (db) {
//now here manually extend the definition of the context to include the GetAirportsWithinRadius service operation.
});
}
This controller works nicely with get parameters and returns the correct json when manually invoked from fiddler. How do extend the jaydata context to have a GetAirportsWithinRadius(airportId,radius) method? Its url will need to be set manually and its type will need to be changed to GET. Also, will that method then be composable with odata params since it is declared with [Queryable]. Again that part of it works when manually invoked in fiddler. For example:
http://localhost:2663/api/Airports2/GetAirportsWithinRadius?airportId=2112&radius=50&?$inlinecount=allpages&$top=2
This returns two airport entity objects nicely...
Thanks

Thanks #robesz - there were some subtleties in creating parameterized odata actions in .net and this is what has been plaguing me (I have tried actions before just couldn't get it to work), but this time I got it:
[Queryable]
[HttpPost]
public IQueryable<Airport> GetAirportsWithinRadius([FromBody] ODataActionParameters parameters)
{
if (!ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var radius = (int)parameters["radius"];
var airportId = (int)parameters["airportId"];
var resultAirports = GetAirportsWithinRadius2(airportId, radius);
return resultAirports;
}
ActionConfiguration getAirportsWithinRadius = modelBuilder.Entity<Airport>().Collection.Action("GetAirportsWithinRadius");
getAirportsWithinRadius.Parameter<int>("airportId");
getAirportsWithinRadius.Parameter<int>("radius");
getAirportsWithinRadius.ReturnsCollectionFromEntitySet<Airport>("Airports");
With this in place I can now do the following with jaydata:
var service = $data.initService("http://localhost:2663/odata");
return service.then(function (db) {
airports = db.Airports.GetAirportsWithinRadius(2112,50);
airports.filter("it.Abbrev== a", {a: 'C44'}).forEach(function(a){console.log(a.Abbrev)});
});
I am jumping up and down in joy :)

Related

Assign Mapped Object to Expression Result in LINQ to Entities

I have the following child object that we use an expression to map our 'entity' to our 'domain' model. We use this when specifically calling our ChildRecordService method GetChild or GetChildren:
public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
DateTime = entity.DateTime,
Type = entity.Type,
};
public static async Task<List<ChildRecord>> ToCommonListAsync(this IQueryable<global::Database.Models.ChildRecord> childRecords)
{
var items = await
childRecords.Select(MapChildRecordToCommon).ToListAsync().EscapeContext();
return items;
}
public async Task<List<ChildRecord>> GetChildRecords()
{
using (var uow = this.UnitOfWorkFactory.CreateReadOnly())
{
var childRecords= await uow.GetRepository<IChildRecordRepository>().GetChildRecords().ToCommonListAsync().EscapeContext();
return childRecords;
}
}
So that all works just fine. However we have another object that is a parent to that child, that in SOME cases, we also wish to get the child during the materialisation and mapping process.
In other words the standard object looks as such:
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonBasic = (entity) => new Plot
{
Id = entity.Id,
Direction = entity.Direction,
Utc = entity.Utc,
Velocity = entity.Velocity,
};
However what I also want to map is the Plot.ChildRecord property, using the expression MapChildRecordToCommon I have already created. I made a second expression just to test this:
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonAdvanced = (entity) => new Plot
{
ChildRecord = MapChildRecordToCommon.Compile() (entity.ChildRecord)
};
This fails:
System.NotSupportedException
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
Is there a way to reuse my existing expression for ChildRecord, to materialise the object of ChildRecord (ie. one to one/singular not multiple) on the Plot object? I think my trouble is caused by there being just one object and being unable to use the .Select(Map) method. I am not too great at expressions and have hit a wall with this.
For reference, there are actually up to 5 or 6 other child objects on the "Plot" object that I also want to make expressions for.
I resolved this by using the third party library LinqKit.
The library allowed the use of 2 methods, .AsExpandable() (which allows for the expressions to properly compile and be invoked as I understand), and .Invoke() as an extension method to an expression, rather than calling Expression.Invoke(yourexpression). I included a null check just in case.
My code now looks as follows:
public static async Task<List<Plot>> ToCommonListAsync(this IQueryable<global::Database.Models.Plot> plots)
{
var items = await
plots.AsExpandable().Select(MapPlotToCommon).ToListAsync().EscapeContext();
return items;
}
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommon = (entity) => new Plot
{
Id = entity.Id,
Direction = entity.Direction,
Utc = entity.Utc,
Velocity = entity.Velocity,
ChildRecord = entity.ChildRecord != null ? MapChildRecordToCommon.Invoke(entity.ChildRecord) : default
};
public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
DateTime = entity.DateTime,
Type = entity.Type,
};

Leaking Quriable objects to upper layers

i have an application that is flexible, that the user can:
filter by any field
sort by any multiple of fields.
and because it will run in ASP.Net Site + some Xamarin C# Apps, i will also have paging in it.
For network performance, it will send projection on the required fields that will be shown.
So if i include in each "Service" method, a parameter "UQueryConstraints", that can send filter expression + oderBy expression + page numbers + Projection of the fields, to be used by the Repository, which will apply it to the DBContext, is this is going to be considered a Data leak to the domain services or not?
as seen in this Pic:
http://1drv.ms/1Ngi3Kn
e.g.:
notice:
"UQueryConstraints", it will not leak any "IQueryable".
The "AmbientDbContextLocator", from:
<http://mehdi.me/ambient-dbcontext-in-ef6/>
<https://github.com/mehdime/DbContextScope>
public class UIView
{
public static void Display()
{
object constraintsB = new UQueryConstraints<Car>().Filter(x => x.carNo <= 6).SortBy(x => x.eName).Page(1, 5);
//.Projection( field1, field2, field3)
Debug.WriteLine("---------------test CarModel -------------------");
CarModel carModel1 = new CarModel();
carModel1.printCars(constraintsB);
}
}
public class CarModel
{
private CarService _carService = new CarService();
void printCars(UQueryConstraints<Car> constraints)
{
foreach ( c in _carService.getCarsList("", constraints)) {
Debug.WriteLine("Reading from converted back: aName =" + c.aName + ", eName = " + c.eName);
}
}
}
public class CarService
{
public IList<Car> getCarsList(string Text, UQueryConstraints<Car> constraints)
{
object dbContextScopeFactory = new DbContextScopeFactory();
object ambientDbContextLocator = new AmbientDbContextLocator();
using (dbContextScope == dbContextScopeFactory.Create()) {
//after creating the Scope:
//1. create the repository
//2. call repository functions
object carRep = new CarRepository(ambientDbContextLocator);
return carRep.getCarsList("", constraints);
}
}
}
public class CarRepository : URepositoryFramwork.URepository
{
public CarRepository(IAmbientDbContextLocator contextLocator)
{
base.New(contextLocator);
}
public IList<Car> getCarsList(string Text, UQueryConstraints<Car> constraints)
{
object query = this.DataSet.Where(constraints.FilterExpression);
//.Select(constraints._projection2)
IList<Car> items;
if (constraints == null) {
items = query.ToList();
} else {
items = constraints.ApplyTo(query).ToList();
}
return items;
}
}
Regards.
Here are few points.
You don't need UQueryConstraints at all and you don't need to do any filtering in the UI at all.
I'd ague that the model is something that needs to be returned from the service so I wouldn't create CarModel in the UI layer and then pushed values to it, it doesn't make sense to me.
I'd have a method on the service that request some data and then returns it in some shape or form to the UI.
I'd inject the service to UIView.
I don't understand why there's so much noise around the context and why do you create it in getCarsList it seems like getCarList should be a class called RequestCars and both the repository and the service should be removed in favor of something like depicted in the command pattern.
I don't like the whole abstraction here at all, seems like over engineering to me and who says that IQueryable should be abstracted? it's like abstracting language/framework features whereas you should abstract domain features and only when necessary.
Abstracting 3rd-party frameworks can be fine to some extent but this isn't one of these cases.

Unit of Work, LazyLoading Disabled, Generic Repository, IncludeMultiple<T>, Http 500 error

I have a Vehicle with an association to Model, Model has an association to Make.
Here is my Generic Repository as pertaining to associations as LazyLoadingEnabled = false in my project:
public IQueryable<T> IncludeMultiple<T1>(params Expression<Func<T, object>>[] associations) where T1 : class
{
var source = (IQueryable<T>)DbContext.Set<T>();
if (associations != null)
{
foreach (Expression<Func<T, object>> path in associations)
source = DbExtensions.Include<T, object>(source, path);
}
return source;
}
In my api controller, I am using Unit of work pattern. Here is my GetAll method:
public IEnumerable<Vehicle> GetAll()
{
var vehicles = Uow.VehicleRepository.IncludeMultiple<Vehicle>(c => c.VehicleModel).ToList();
return vehicles;
}
Everything works fine and Json retrieves the Vehicle class data as well as the related VehicleModel class data.
However, Vehicle has no direct association to VehicleMake, only VehicleModel does. Now, if my GetAll method has this:
public IEnumerable<Vehicle> GetAll()
{
var vehicles = Uow.VehicleRepository.IncludeMultiple<Vehicle>(c => c.VehicleModel, c => c.VehicleModel.VehicleMake).ToList();
return vehicles;
}
while I see in debug that vehicles does indeed have the vehicles and their relevant VehicleModel and VehicleMake data, it returns a Http 500 error in Fiddler.
Update:
Added another association in Vehicle called "Test", with the GetAll method being:
(c => c.VehicleModel, c => c.Test)
No error, all data was returned via fiddler. So, it appears that a "Non-direct association" (ie Vehicle -> VehicleMake) is the cause of the error.
Question:
What would be the correct way to retrieving the relevant Vehicle data and its associated classes' data and return it to Json while not getting a Http 500 error?
*SOLVED *
This works:
public HttpResponseMessage GetAll()
{
var vehicles = from data in Uow.VehicleRepository.IncludeMultiple<Vehicle>(c => c.VehicleModel,c => c.VehicleModel.VehicleMake)
select new
{
VehDesc = data.Description,
VehVIN = data.VIN,
VehTransmissionType = data.TransmissionType,
VehFuelType = data.FuelType,
VehYear = data.Year,
VehMileage = data.Mileage,
VehCylinderSize = data.CylinderSize,
VehEngineSize = data.EngineSize,
VehVehicleModel = data.VehicleModel.Name,
VehMakeName = data.VehicleModel.VehicleMake.Name
};
return Request.CreateResponse(HttpStatusCode.OK, vehicles);
}
Basically,
1. I used an HttpResponseMessage as my return type;
2. I used projection to create an anonymous type;
Why did I have to do this?
As near as I can tell, the issue centered on JSON receiving a "circular" return with VehicleModel and VehicleMake. That is, VehicleModel had a association to VehicleMake and VehicleMake has a collection of VehicleModels. When I looked in my debug code I could see a cascade of VehicleModel to VehicleMake to VehicleModel, etc, etc, etc, so to me that meant it was circular.
If anyone knows a better way w/o using anonymous type nor removing the virtual keyword from my navigation properties, I would certainly like to know it. But this does truly work.
FinalNote: Be sure NOT to use the model's property names in anonymous type, ie replace property "TransmissionType" with something like "VehTransmissionType".

Compose part with specific instance

Is there any way I can compose (or get an exported value) with a specific instance as one of it's dependencies?
I have something like this:
public interface IEntityContext
{
IEntitySet<T> GetEntitySet<T>();
}
[Export(typeof(IEntitySet<MyEntity>))]
class MyEntitySet
{
public MyEntitySet(IEntityContext context)
{
}
}
// then through code
var container = ...;
using (var context = container.GetExportedValue<IEntityContext>())
{
var myEntitySet = context.GetEntitySet<MyEntity>();
// I wan't myEntitySet to have the above context constructor injected
}
I'm trying to mock something like entity framework for testability sake. Not sure though if I would want to go down this road. Anyway, should I be creating a new container for this very purpose. A container specific to the mocking of this one IEntityContext object.
So, if my understanding is correct, you want to be able to inject whatever IEntityContext is available to your instance of MyEntitySet?
[Export(typeof(IEntitySet<MyEntity>))]
public class MyEntitySet : IEntitySet<MyEntity>
{
[ImportingConstructor]
public MyEntitySet(IEntityContext context)
{
}
}
Given that you then want to mock the IEntityContext? If so, you could then do this:
var contextMock = new Mock<IEntityContext>();
var setMock = new Mock<IEntitySet<MyEntity>>();
contextMock
.Setup(m => m.GetEntitySet<MyEntity>())
.Returns(setMock.Object);
Container.ComposeExportedValue<IEntityContext>(contextMock.Object);
var context = Container.GetExportedValue<IEntityContext>();
var entitySet = context.GetEntitySet<MyEntity>();
(That's using Moq)
You can use your existing CompositionContainer infrastructure by adding an exported value.
Does that help at all? Sorry it doesn't seem exactly clear what you are trying to do...

EFPocoAdapter -- PopulatePocoEntity has null PocoEntity

I'm trying EF with the EFPocoAdapter for the first time. I have a relatively simple TPH scenario with one table and two types, each inheriting from an abstract base class.
My model validates through EdmGen, and my PocoAdapter.cs and xxxEntities.cs files generate fine as well. (well, actually, there are some namespace problems that I'm currently tweaking by hand until we figure out where to go next.)
When I run a simple test to retrieve data:
using (CINFulfillmentEntities context = new CINFulfillmentEntities())
{
// use context
var alerts = from p in context.Notifications.OfType<Alert>()
select p;
foreach (var alert in alerts)
{
Assert.IsNotNull(alert);
}
}
I get an error in the PocoAdapter class, claiming that PocoEntity is null is the following method inside my base class's adapter:
public override void PopulatePocoEntity(bool enableProxies)
{
base.PopulatePocoEntity(enableProxies);
PocoEntity.Owner = _Owner.CreatePocoStructure();
if (!(PocoEntity is IEntityProxy))
{
}
}
Any ideas from anyone?
So, after a little more debugging, I think this is related to proxies. Inside PocoAdapterBase we have the following method:
protected PocoAdapterBase(TPocoClass pocoObject)
{
_context = ThreadLocalContext.Current;
bool allowProxies = false;
if (_context != null)
{
allowProxies = _context.EnableChangeTrackingUsingProxies;
}
_pocoEntity = pocoObject ?? (TPocoClass)(allowProxies ? CreatePocoEntityProxy() : CreatePocoEntity());
Init();
InitCollections(allowProxies);
RegisterAdapterInContext();
}
The line that sets _pocoEntity calls CreatePocoEntityProxy, which returns null.
More info as I find it.