ServiceStack ORMLite - How to Select All to match the request DTO's properties automatically - request

I have several ServiceStack ORMLite POCO, one is Company below.
public class Company
{
[AutoIncrement]
public int id { get; set; }
public string company { get; set; }
public int? companyNo { get; set; }
public bool? active { get; set; }
}
If two properties are valid in the following request: req.company="ABC Company", req.active=ture, and all other properties are null. Then it can return all records matching the two properties. The code may look like below:
public object Get(Company req)
{
return Db.Select<Company>().Where<Company>(req);
}
Does ServiceStack ORMLite have such a WHRER to auto-match the valid properties in the request DTO?

This is not a feature in OrmLite, but it's available in AutoQuery where you just need to define the Request DTO you want to query, e.g:
[Route("/company/search")]
public class QueryCompany : IQuery<Company>
{
public int Id { get; set; }
public string Company { get; set; }
public int? CompanyNo { get; set; }
public bool? Active { get; set; }
}
With just the Request DTO, ServiceStack automatically creates the Service for you which you can query like any other Service.
Enable AutoQuery
You can enable AutoQuery by registering the AutoQuery Feature, e.g:
Plugins.Add(new AutoQueryFeature { MaxLimit = 100 });
AutoQuery is available in the ServiceStack.Server NuGet package:
PM> Install-Package ServiceStack.Server

Thanks mythz. It works for me. My code is like below:
// ====== Model.cs ========
[Route("/company/search")]
public class QueryableCompany : QueryBase<Company>
{
public int? Id { get; set; }
public string Company { get; set; }
public int? CompanyNo { get; set; }
public bool? Active { get; set; }
}
public class Company
{
[AutoIncrement]
public int id { get; set; }
public string company { get; set; }
public int companyNo { get; set; }
public bool active { get; set; }
}
// ====== Service.cs ========
public IAutoQuery AutoQuery { get; set; }
public object Get(QueryableCompanies dto)
{
var q = AutoQuery.CreateQuery(dto, Request.GetRequestParams());
var r = AutoQuery.Execute(dto, q);
return r.Results;
}
// ====== Global.asax.cs ========
public override void Configure(Container container)
{
//...
Plugins.Add(new AutoQueryFeature { MaxLimit = 100 });
//...
}
Then, I have two more questions based on the code above.
1) Since I have a lot of request DTOs, their code in Get(QueryableXXX dto) is all the same; How can I use a single generic Get() method to return all different types of DTO, like:
public object Get<T>(T dto) where T : IQuery
{
var q = AutoQuery.CreateQuery(dto, Request.GetRequestParams());
return AutoQuery.Execute(dto, q).Results;
}
2) In the Company example above, class QueryableCompany seems so similar to class Company, can AutoQuery provide some Attributes to class Company's members, and avoid to create another similar QueryableCompany?

Related

EF Core One to One relationship on many tables allows duplicate entries

I am using EF Core and I tried to create a one-to-one relationship between three tables (Car, ElectricCar and PetrolCar)
public class Car
{
public int Id { get; set; }
public string RegistrationNumber { get; set; }
public ElectricCar Company { get; set; }
public PetrolCar Trust { get; set; }
}
public class ElectricCar
{
public int ElectricCarId { get; set; }
public double BatteryCapacityWattage{ get; set; }
public int CarId { get; set; }
public Car Car { get; set; }
}
public class PetrolCar
{
public int PetrolCarId { get; set; }
public double TankCapacity { get; set; }
public int CarId { get; set; }
public Car Car { get; set; }
}
public partial class CarDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public CarDbContext()
{
}
public CarDbContext(DbContextOptions<CarDbContext> options)
: base(options)
{
}
public DbSet<ElectricCar> ElectricCar { get; set; }
public DbSet<Car> Car { get; set; }
public DbSet<PetrolCar> PetrolCar { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Server=DESKTOP-PC\\SQLLOCAL;Database=OneToOneEFCoreCar;Trusted_Connection=True;");
}
}
}
and the code that inserts the data:
CarDbContext context = new CarDbContext();
context.Car.Add(new Car
{
RegistrationNumber = "EL123",
Company = new ElectricCar() { BatteryCapacityWattage = 2000 }
});
context.Car.Add(new Car
{
RegistrationNumber = "PETR123",
Trust = new PetrolCar() { TankCapacity = 50 }
});
context.SaveChanges();
That works without any issue and creates the following data
When I go to the PetrolCar I insert a new row with CarId = 1 and it accepts it without giving any error although that CarId is used in the ElectricCar table as CarId.
Is there any way to restrict this?
If you're entirely set on keeping your object models / data structure the same as it is above then a unique constraint across the two tables isnt really natively achievable.
One possible in code solution (though its not particularly clean, so I would suggest restructuring your data over this, though that seems to be something you would like to avoid) is to override the SaveChanges method.
something along the lines of:
public override SaveChanges()
{
var petrolCars = ChangeTracker.Entries().Where(e is PetrolCar).ToList();
foreach(var pCar in petrolCars)
{
if(query the database for electric cars to see if car id exists)
{
do some sort of error processing and avoid saving;
}
}
base.SaveChanges();
}
it does mean creating a context class that inherits from the default context, though it adds a lot of flexibility in terms of doing something like this (obviously you would want to handle the other cases too of cars having the same id in the other direction)

How to to make entity relationships in WEB API database?

I'm making a task management tool using AngularJS for the frontend and ASP.NET WEB API 2 for the backend. I have two entities in the database, a "Task" and a "Type". Each task has one type associated. The user fills a form when he can create a new task, and he has to select a type for that task.
Here's the C# code:
// KBTM_Task.cs file
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
}
// KBTM_Type.cs file
public class KBTM_Type
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
So my question is: how do I "connect" the two in the database? What I mean is, let's say I want to POST data to the database. I have to make two POSTs, right? One for the Task and one for the Type, since they're two separate entities.
But since they're stored with two different IDs, how do I know that a certain task has a certain type? In other words, if I send a GET request to KBTM_Task, how do I get the type of that task?
Modify your KBTM_Task entity to include the Type Id and foreign key relationship
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
public int TypeID { get; set; }
[ForeignKey("TypeID")]
public virtual KBTM_Type Type { get; set; }
}
This way when you get the data from the API your task object will already include the key ("TypeID") that can be updated and related object ("Type") that you can access its properties (Name, Description, ...).
When you update TypeID on the client object (model) you can simply push the updated task object to the API using $http.put() to handle the database update.
1) Add foreign key using fluent api (or data annotation)
// KBTM_Task.cs file
public class KBTM_Task
{
public int ID { get; set; }
public string TaskID { get; set; } // User defined ID
public string Title { get; set; }
public string Description { get; set; }
public int KBTM_TypeID {get;set}
public virtual KBTM_Type {get; set}
}
// KBTM_Type.cs file
public class KBTM_Type
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public KBTM_Task KBTM_Task { get; set;}
}
Add the following in the class inheriting from DbContext
public class KbtmContext : DbContext
{
...
//public virtual DbSet<KBTM_Task> KbtmTasks {get; set;}
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure KBTM_TypeID as FK for KBTM_Task
modelBuilder.Entity<KBTM_Task>()
.HasRequired(k => k.KBTM_Type)
.WithRequiredPrincipal(ad => ad.KBTM_Task);
}
}
2) If exposing the entity class in API response or request then you need to exclude navigation property from being serialized.
// KBTM_Task.cs file
public class KBTM_Task
{
...
[JsonIgnore]
public virtual KBTM_Type Type { get; set; }
}
To use the [JsonIgnore] atttribute use Install-Package Newtonsoft.Json in package manager console.(One of the popular solutions to manage serialization)

Update database schema to allow multiple users

I'm working on an ASP.net Web API application and would like to allow multiple users to have access to their own data without changing the database schema too much.
My tables look a little like this:
public class Asset
{
public string Name { get; set; }
[Required]
public int Id { get; set; }
public string AssetTag { get; set; }
public string Serial { get; set; }
public int Model { get; set; }
}
public class Organisation
{
[Required]
public int Id { get; set; }
[Required]
public int DefaultLocation { get; set; }
public location Location { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string Contact { get; set; }
}
public class AssetModel
{
public string Name { get; set; }
[Required]
public int Id { get; set; }
public int CategoryId { get; set; }
public int ManufacturerId { get; set; }
public string ModelNumber { get; set; }
}
*fields omitted for brevity
Each user should be able to create their own assets / organisations / etc, but should not have access to any other users fields.
I'm yet to add authorization / authentication however I'm probably going to use token based auth as outlined here:
http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/
Should this be as easy as tacking each users GUID onto each column and applying some logic? Or will I need to completely re-design the database?

Web ApI Entity Framework (Code First) Value not appearing in database

My database will run correctly, and I can input the data manually via SQL Server, however, when I try and pass the value in via my API (testing using Postman), the value won't pass into the database, it appears as "NULL".
I have a reports and a bookings tables.
This is the code for the reports:
public class Report
{
public Report()
{
Injuries = new List<Injury>();
this.Bookings = new HashSet<Booking>();
}
public int Id { get; set; }
public string Club1 { get; set; }
public string Club2 { get; set; }
public virtual ICollection<Injury> Injuries { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
Bookings:
public class Booking
{
//public Booking()
//{
// Reports = new List<Report>();
//}
public int Id { get; set; }
public string Club { get; set; }
public string PlayerName { get; set; }
public string PlayerNumber { get; set; }
public string Reason { get; set; }
public string Description { get; set; }
//public int? Report_Id { get; set; }
public Nullable<int> Report_Id { get; set; }
public virtual Report Report { get; set; }
//public virtual ICollection<Report> Reports { get; set; }
}
Controller:
//POST: api/Reports
[ResponseType(typeof(Report))]
public async Task<IHttpActionResult> PostReport(Report report)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Reports.Add(report);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = report.Id }, report);
}
I put the test information via Postman:
I'm not sure why Report_Id is showing as it's not required, however, Report_Id1 is the field that is connecting the Report and Booking together.
Since your foreign key doesn't follow convention (ReportId), you need to use the annotation [ForeignKey] or a fluent api configuration:
modelBuilder.Entity<Booking>()
.HasRequired(b => b.Report)
.WithMany(b => b.Bookings)
.HasForeignKey(p => p.Report_Id);
That is why EF is adding the second Report_ID1. https://msdn.microsoft.com/en-us/data/hh134698.aspx

Any obvious reason why my breeze entity child nodes won't expand?

I can't figure out why my child nodes are either null or have a count of 0, even though there's associated data in the db.
Parent Class "Project"
public partial class Project
{
public Project()
public int Id { get; set; }
public string Name { get; set; }
public int ProjectOwnerId { get; set; }
public int CurrentMilestoneId { get; set; }
public int StatusId { get; set; }
public virtual Milestone CurrentMilestone { get; set; }
public virtual ProjectStatus Status { get; set; }
public virtual ICollection<ProjectContact> Contacts { get; set; }
public virtual ProjectAddress Address { get; set; }
}
Child node property "CurrentMilestone" comes back null
public partial class Milestone
{
public int Id { get; set; }
public int MasterMilestoneId { get; set; }
public string Name { get; set; }
public virtual MasterMilestone MasterMilestone { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
Child node property "Contacts" comes back with an array of 0, even though there's valid matching data.
public partial class ProjectContact
{
public int Id { get; set; }
public int ProjectId { get; set; }
public int PersonId { get; set; }
public string Title { get; set; }
public virtual Person Person { get; set; }
public virtual Project Project { get; set; }
}
Using the HotTowel angular/breeze I'm running this..
return EntityQuery.from("Projects")
.orderBy(orderBy)
.expand("currentMilestone.masterMilestone, projectOwnerCompany, contacts, address")
.using(self.manager).execute()
.then(querySucceeded, self._queryFailed);
function querySucceeded(data) {
projects = data.results;
return projects;
}
Controller:
[HttpGet]
public IQueryable<Project> Projects()
{
return _contextProvider.QueryAllReadOnly<Project>();
}
What's weird is some of them work, like Status works but leaseStatus doesn't (not shown, setup the same way) without expanding it.
Just to add more info, if I run this against the API, ..../Projects?$expand=Status%2CCurrentMilestone%2CLeaseStatus%2CCurrentMilestone%2FMasterMilestone%2CContacts%2CContacts%2FPerson&
In Fiddler the child nodes aren't expanded.
Figured it out..
Change the Controller from ReadOnly to non-readonly.
From
[HttpGet]
public IQueryable<Project> Projects()
{
return _contextProvider.QueryAllReadOnly<Project>();
}
To
[HttpGet]
public IQueryable<Project> Projects()
{
return _contextProvider.QueryAll<Project>();
}

Resources