C# Asp.net WebApi how to not populate ID field for the Entity when post in CRUD - c#-4.0

I am creating simple webapi with CRUD functionality.
I have an entity say for eg: Product.
I am using FluentNHibernate to map the tables. The Id is generated by sequence.
public ProductMap()
{
Table("PRODUCT");
Id(x => x.Id).GeneratedBy.Sequence("SEQ_REC_SCH_INFO").Column("SCH_ID");
Map(x => x.Name, "Name");
Map(x => x.Category, "Category");
Map(x=>x.Price,"Price");
}
So I dont want the Id parameter of the Product to be populated when i post the data.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
How to make other fields passed to the api controller except Id field?
Thanks

The best way to pass models between systems is ViewModels, with this approach you can ignore some properties in certain cases and include them in other cases, but if you want to ignore this property in all cases you can use JsonIgnore attribute to prevent these properties from serializing.

Related

ServiceStack Locode Multi select option

I am developing an app and got a feature where an user can assign multiple User's to a Feature . I want the Create Feature page (locode) to populate the list of available users so that the end-user can assign multiple users to a feature. As of now it is not able to populate the User list.
Is there an alternate way or do I need to write the custom template and populate the data on mount() lifecycle?
Below is the DTO,
[Route("/feature", "POST")]
public class CreateFeatureFlag : ICreateDb<Feature>, IReturn<FeatureCreated>
{
[ValidateNotEmpty]
public string Name { get; set; }
[ValidateNotEmpty]
public List<Guid> Users{ get; set; }
}
and the Domain Feature,
[UniqueConstraint(nameof(Name))]
public class Feature : AuditBase
{
[AutoId]
public Guid Id { get; set; }
public string Name { get; set; }
[Reference]
public List<User> Users { get; set; } = new();
}
In Locode it would require a custom Form component to implement it in the same form, but you can add related records by navigating to the child relation then adding the child record where it will preserve and prepopulate the parent id.
This is used a lot in https://talent.locode.dev like navigating to a Job's Job Applications defined by its POCO Reference:
public class Job : AuditBase
{
//...
public List<JobApplication> Applications { get; set; } = new();
}
Which will prepopulate the Job Id reference making it easy to add multiple 1:Many Job Applications.
Checkout its Talent.cs DTOs for more Reference examples.

AutoQuery insight needed

So, I'm working with ServiceStack and love what it offers. We've come to a point where I'm needing to implement a queryable data API... prior to my coming to this project, a half backed OData implementation was done. I'd rather not try and weed through that to make it work.
Which brings me to AutoQuery. I'd like to try it with our SQL Server database. I'm looking at the examples at http://docs.servicestack.net/autoquery-rdbms - but I cannot for the life of me get this to work. Is there something I'm missing here?
I'm using ORMLite to query SQL, and my integration tests I've written show it to be working as I would expect. I have registered the OrmLiteConnectionFactory in the container, as well as my repository which uses it by way of dependency injection.
Specific to code so far, I have a type, and a message that is based on QueryDb:
public class Detail
{
public string Div { get; set; }
public string Reg { get; set; }
}
[Route("/report/detail")]
public class DetailQuery : QueryDb<Detail>
{
public string[] Div { get; set; }
public string[] Reg { get; set; }
}
The message, DetailQuery, is used by my service:
public class ReportService : Service
{
public object Get(DetailQuery dq)
{
// not sure what to put here?
}
}
With all of that, I am able to see the AutoQuery service instance in the admin interface. When I play with the query interface, I hit my service endpoint, and I see the data I expect - filter values in the 'Div' and 'Reg' collections. What am I missing for this to 'just work' here? I have done plenty in ServiceStack accessing my repositories from the Service itself, but I'm trying to gain some insight into what AutoQuery brings to the table here. I have yet to see a 'straight forward' example of how this works... or am I looking for a pot of gold that just isn't there?
AutoQuery works with just the Request DTO i.e. it doesn't need any Service implementation, so your query:
[Route("/report/detail")]
public class DetailQuery : QueryDb<Detail>
{
public string[] Div { get; set; }
public string[] Reg { get; set; }
}
When called from /report/detail will query the Detail RDBMS Table. But your properties here either need to match a column on the Detail table (e.g. Div or Reg) in order to have an exact match (default), however exact matches aren't normally done with arrays they're done with scalar values like a string, e.g:
public string Div { get; set; }
public string Reg { get; set; }
If you're querying a collection you'd be instead making an IN Query where the values would contain list of values, in which case they're normally pluralized:
public string[] Divs { get; set; }
public string[] Regs { get; set; }
and can be called with:
/report/detail?Divs=A,B&Regs=C,D
Which will perform a query similar to:
SELECT * FROM Detail WHERE Div IN ('A','B') AND Rev IN ('C','D')
If that's not the behavior you want it needs to match an implicit convention, e.g:
public string[] DivBetween { get; set; }
Which will then query:
SELECT * FROM Detail WHERE Div BETWEEN 'A' AND 'B'
If you wanted to you could override the AutoQuery service with a custom implementation, e.g:
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
//Override with custom implementation
public object Any(DetailQuery query)
{
var q = AutoQuery.CreateQuery(query, base.Request);
return AutoQuery.Execute(request, q);
}
}
But you'd only need to do that when you want to customize the default behavior, e.g. add an extra filter to the populated SqlExpression.

ServiceStack - [Reference] or [Ignore]?

We have a DTO - Employee - with many (> 20) related DTOs and DTO collections. For "size of returned JSON" reasons, we have marked those relationships as [Ignore]. It is then up to the client to populate any related DTOs that they would like using other REST calls.
We have tried a couple of things to satisfy clients' desire to have some related Employee info but not all:
We created a new DTO - EmployeeLite - which has the most-requested fields defined with "RelatedTableNameRelatedFieldName" approach and used the QueryBase overload and that has worked well.
We've also tried adding a property to a request DTO - "References" - which is a comma-separated list of related DTOs that the client would like populated. We then iterate the response and populate each Employee with the related DTO or List. The concern there is performance when iterating a large List.
We're wondering if there a suggested approach to what we're trying to do?
Thanks for any suggestions you may have.
UPDATE:
Here is a portion of our request DTO:
[Route("/employees", "GET")]
public class FindEmployeesRequest : QueryDb<Employee> {
public int? ID { get; set; }
public int[] IDs { get; set; }
public string UserID { get; set; }
public string LastNameStartsWith { get; set; }
public DateTime[] DateOfBirthBetween { get; set; }
public DateTime[] HireDateBetween { get; set; }
public bool? IsActive { get; set; }
}
There is no code for the service (automagical with QueryDb), so I added some to try the "merge" approach:
public object Get(FindEmployeesRequest request) {
var query = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<Employee> response = AutoQuery.Execute(request, query);
if (response.Total > 0) {
List<Clerkship> clerkships = Db.Select<Clerkship>();
response.Results.Merge(clerkships);
}
return response;
}
This fails with Could not find Child Reference for 'Clerkship' on Parent 'Employee'
because in Employee we have:
[Ignore]
public List<Clerkship> Clerkships { get; set; }
which we did because we don't want "Clerkships" with every request. If I change [Ignore] to [Reference] I don't need the code above in the service - the List comes automatically. So it seems that .Merge only works with [Reference] which we don't want to do.
I'm not sure how I would use the "Custom Load References" approach in an AutoQuery service. And, AFAIKT, the "Custom Fields" approach can't be use for related DTOs, only for fields in the base table.
UPDATE 2:
The LoadSelect with include[] is working well for us. We are now trying to cover the case where ?fields= is used in the query string but the client does not request the ID field of the related DTO:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
.
.
.
[References(typeof(Department))]
public int DepartmentID { get; set; }
.
.
.
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
.
.
.
}
So, for the request
/employees?fields=id,departmentid
we will get the Department in the response. But for the request
/employees?fields=id
we won't get the Department in the response.
We're trying to "quietly fix" this for the requester by modifying the query.SelectExpression and adding , "Employee"."DepartmentID" to the SELECT before doing the Db.LoadSelect. Debugging shows that query.SelectExpression is being modified, but according to SQL Profiler, "Employee"."DepartmentID" is not being selected.
Is there something else we should be doing to get "Employee"."DepartmentID" added to the SELECT?
Thanks.
UPDATE 3:
The Employee table has three 1:1 relationships - EmployeeType, Department and Title:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
[References(typeof(EmployeeType))]
public int EmployeeTypeID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
[References(typeof(Title))]
public int TitleID { get; set; }
.
.
.
}
public class EmployeeType {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
}
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
[Reference]
public List<Title> Titles { get; set; }
}
public class Title {
[PrimaryKey]
public int ID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
public string Name { get; set; }
}
The latest update to 4.0.55 allows this:
/employees?fields=employeetype,department,title
I get back all the Employee table fields plus the three related DTOs - with one strange thing - the Employee's ID field is populated with the Employee's TitleID values (I think we saw this before?).
This request fixes that anomaly:
/employees?fields=id,employeetypeid,employeetype,departmentid,department,titleid,title
but I lose all of the other Employee fields.
This sounds like a "have your cake and eat it too" request, but is there a way that I can get all of the Employee fields and selective related DTOs? Something like:
/employees?fields=*,employeetype,department,title
AutoQuery Customizable Fields
Not sure if this is Relevant but AutoQuery has built-in support for Customizing which fields to return with the ?fields=Field1,Field2 option.
Merge disconnected POCO Results
As you've not provided any source code it's not clear what you're trying to achieve or where the inefficiency with the existing solution lies, but you don't want to be doing any N+1 SELECT queries. If you are, have a look at how you can merge disconnected POCO results together which will let you merge results from separate queries based on the relationships defined using OrmLite references, e.g the example below uses 2 distinct queries to join Customers with their orders:
//Select Customers who've had orders with Quantities of 10 or more
List<Customer> customers = db.Select<Customer>(q =>
q.Join<Order>()
.Where<Order>(o => o.Qty >= 10)
.SelectDistinct());
//Select Orders with Quantities of 10 or more
List<Order> orders = db.Select<Order>(o => o.Qty >= 10);
customers.Merge(orders); // Merge disconnected Orders with their related Customers
Custom Load References
You can selectively control which references OrmLite should load by specifying them when you call OrmLite's Load* API's, e.g:
var customerWithAddress = db.LoadSingleById<Customer>(customer.Id,
include: new[] { "PrimaryAddress" });
Using Custom Load References in AutoQuery
You can customize an AutoQuery Request to not return any references by using Db.Select instead of Db.LoadSelect in your custom AutoQuery implementation, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.Select(q),
Total = (int)Db.Count(q),
};
return response;
}
Likewise if you only want to selectively load 1 or more references you can change LoadSelect to pass in an include: array with only the reference fields you want included, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.LoadSelect(q, include:new []{ "Clerkships" }),
Total = (int)Db.Count(q),
};
return response;
}

Option for Include to only return foreign keys

Does Entity Framework provide an option to retrieve child objects that are only populated with fields that are foreign keys to the parent object?
Sample code might illustrate this better.
Assuming you have the following POCO classes...
public abstract class Base
{
public Guid Id { get; set; }
}
public class User : Base
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Photo : Base
{
public string Description { get; set; }
public User UploadedBy { get; set; }
}
... and assuming you've configured a DbContext correctly, how do you query for a list of all Photos including the UploadedBy object, but where that UploadedBy object only contains the Id property?
I know I can do this...
return await _dbContext.Photos.Include(p => p.UploadedBy).ToListAsync();
... but that returns the entire User object.
I'd like to do something like this...
return await _dbContext.Photos.Include(p => p.UploadedBy.Id).ToListAsync();
... to indicate that I only want the Id property back.
If we could chain those includes we would be able to pick each property on the child object that we want returned.
Or even better, I'd love to be able to configure a setting at a more global level that would make it so that anytime I ask for Photos, give me all members of photos, even child objects, but only populate their foreign keys and nothing more.
The last request is less important though because I could just create the following extension method for each POCO object...
public static IQueryable<Photo> IncludeForigenKeys(this PhotoAlbumDbContext context){
return context.Photos
.Include(photo => photo.UploadedBy.Id);
}
As far as I understand there is no way to partially load a Navigation Property.
However for foreign keys the standard way of accessing these without loading the Nav property is to include the actual key in your model. Eg:
public class Photo : Base
{
public string Description { get; set; }
public int UploadedById { get; set; }
public User UploadedBy { get; set; }
}
This id will be populated even if you don't actually load the whole navigation property.
In the case where you load both you can update either the value on the local or remote end of the nav property and that update will be persisted to the database on save. In my experience EF is very clever around this. The only scenario where it becomes a little more tricky is in unit tests where EF is not maintaining this state.

Entity Framework - Single Entity to Multiple Tables

I am having some difficulty in mapping single entity to a two different tables in a Entity Framework out of which one is optional to give a quick overview.
I have one main table which is of a core table that lot of our applications in our company uses it, so we really don't want to make any changes to this table.
In our new application we needed a few more columns to support some of the features we are adding.
I have created a single Entity Model that will save information to both these tables, it is working fine when both these tables has the records (related by primary key and foreign key)
But for the historical record this new table will not have a associated record and not able to fetch any entity set.
Below is the code snippet.
public class ModelTable
{
public string PatientID { get; set; }
public string Diagnosis1 { get; set; }
public string Diagnosis2 { get; set; }
public string Diagnosis3 { get; set; }
public string Diagnosis4 { get; set; }
public string Diagnosis5 { get; set; }
public string Diagnosis6 { get; set; }
public string Diagnosis7 { get; set; }
public string Diagnosis8 { get; set; }
}
public class ModelTableMap : EntityTypeConfiguration<ModelTable>
{
public ModelTableMap()
{
//Table1
this.Map(model =>
{
model.Properties(table1 => new
{
table1.Diagnosis1,
table1.Diagnosis2,
table1.Diagnosis3,
table1.Diagnosis4,
table1.Diagnosis5,
table1.Diagnosis6
});
model.ToTable("Table1");
});
//Optional Table
this.Map(model =>
{
model.Properties(table2 => new
{
table2.Diagnosis7,
table2.Diagnosis8,
});
model.ToTable("Table2");
});
this.HasKey(type => type.PatientID);
this.Property(type => type.PatientID).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(type => type.Diagnosis1).HasColumnName("Diag1");
this.Property(type => type.Diagnosis1).HasColumnName("Diag2");
this.Property(type => type.Diagnosis1).HasColumnName("Diag3");
this.Property(type => type.Diagnosis1).HasColumnName("Diag4");
this.Property(type => type.Diagnosis1).HasColumnName("Diag5");
this.Property(type => type.Diagnosis1).HasColumnName("Diag6");
this.Property(type => type.Diagnosis1).HasColumnName("Diag7");
this.Property(type => type.Diagnosis1).HasColumnName("Diag8");
}
}
If I split these tables into a two different POCO classes and specify the relationshipt it is working fine.
But I want to achieve this with Single Entity, since functionally it is a same table.
Please provide any guidance or if I am doing any wrong and please bare with my English is not that good.
Thanks
Sathish
Entity splitting in current EF version requires records in both tables. If you want to use entity splitting you must create empty record for all existing records from the first table. Otherwise you cannot use entity splitting.

Resources