EF WebAPI Self Referencing Loop - c#

I am getting a self Referencing issue with EF and I'm trying to over come it but still allow the Service to be able to perform a GET passing in {[FromODataUri] int key} a key and return an IQuerable Obj to get the Expanded tables if necessary. Below is a slimmed down version of the tables. Any suggestions on how to handle the situation.
public class People
{
public int PeopleId {get;set;}
public string PeopleName {get;set;}
public int? ProductId{get;set;}
public virtual Product Product{get;set;}
}
The ProductId is a PK in Product but its not required. As per the convention it doesn't have to be Decorated with the PK DataAnnotation overide.
public class Product
{
public Product()
{
PeopleCollection = HashSet<People>();
}
public int ProductId {get;set;}
public string ProductName {get;set;}
public virtual ICollection<People> Peoples{get;set;}
}

In this case I recommend using DTO's or using anonymous objects, for example:
public IHttpActionResult Get() {
var response = db.YourTable.Select(x=>new{
x.PeopleId,
x.PeopleName,
x.ProductId,
Product= new {
x.ProductId,
x.ProductName
}
}).toList();
return Ok(response);
}
Thats how I would do it with anonymous objects, If you want to use DTO's you just need to map them, hope this is what you are looking for.
For just a specific id:
public IHttpActionResult Get(int id) {
var response = db.YourDb.Select(x=>new{
x.PeopleId,
x.PeopleName,
x.ProductId,
Product= new {
x.ProductId,
x.ProductName
}
})Where(x=>x.PeopleId == id).toList();
return Ok(response);
}
Note, this method is using query string parameters

I figured this out after some time. The self referencing issue will come up if you are Inheriting from APIController but if you switch to inherit from ODataController everything works.
So
public class MyController : ApiController
{
..... Bunch of code here
}
To
public class MyController : ODataController
{
..... Bunch of code here
}

Related

Map two separate objects in new class with AutoMapper

I have 2 models, Products and Categories, these are from the model on the repository:
IEnumerable<Product>
IEnumerable<Category>
I create a WebAPI and introduce AutoMapper eventually having code as below to return the products and categories in their own separate classes
public class ProductsController : ApiController
{
....
public IHttpActionResult Get()
{
try
{
IEnumerable<Product> products = GetProducts();
var mappedResult = _mapper.Map<IEnumerable<ProductModel>>(products);
return Ok(mappedResult);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
As i say same code in a new class for Category. Notice the new model for this class so its separate from the Domain model (ProductModel).
I now would like to create a page in my MVC application (not .Net Core) which needs to display a list of categories in a dropdown and list the products with a foreach loop. I create a new class as below
Public Class ProductsAndCategoriesModel
{
public ProductModel Products { get; set; }
public CategoryModel Categories { get; set; }
}
The idea here is to have a new method to load the same products and categories data but in its own class. Im following the same convention as above but how do i map two different data sources to this one class using AutoMapper?
var mappedResultProducts = _mapper.Map<IEnumerable<ProductsAndCategoriesModel>>(products);
var mappedResultCategories = _mapper.Map<IEnumerable<ProductsAndCategoriesModel>>(cats);
//return Ok(mappedResult);
I need to map products to Products found in ProductsAndCategoriesModel and Categories found in ProductsAndCategoriesModel. I tried to pass in categories but couldnt as it threw a compiler exception. How could i achieve this?
Edit 1
public ProductCategoryProfile()
{
CreateMap<Product, ProductsAndCategoriesModel>();
CreateMap<Category, ProductsAndCategoriesModel>();
}
No need to make things more complicated than they should be. Why don't you simply do it like this:
Model class:
public Class ProductsAndCategoriesModel
{
public IEnumerable<ProductModel> Products { get; set; }
public IEnumerable<CategoryModel> Categories { get; set; }
}
Mapping profile:
public ProductCategoryProfile()
{
CreateMap<Product, ProductModel>();
CreateMap<Category, CategoryModel>();
}
Controller:
public IHttpActionResult Get()
{
try
{
IEnumerable<Product> products = GetProducts();
IEnumerable<Category> categories = GetCategories();
var result = new ProductsAndCategoriesModel
{
Products = _mapper.Map<IEnumerable<ProductModel>>(products),
Categories = _mapper.Map<IEnumerable<CategoryModel>>(categories)
}
return Ok(result);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}

User defined order using EF and Postgresql

I have a category model class with category name, Id and admin order properties.
I want to provide an API endpoint to admin to reorder the categories as per his need.
I am not sure what is the most efficient way to tackle this using the EF Core and Postgresql (Core 2.2) - I'm a newbie to C# and .NET Core. Please correct me if my approach is wrong.
Here is what I tried:
My model class:
public class Category
{
public string Name { get; set; }
public int Id { get; set; }
public int adminOrder { get; set; }
}
I have my service repository as below with GetCategories and UpdateCategories methods:
public class CategoryRepository: ICategoryRepository
{
private ApplicationDbContext _categoryContext
public CategoryRepository(ApplicationDbContext categoryContext)
{
__categoryContext = _categoryContext
}
public ICollection<Category> GetCategories()
{
return _categoryContext.Categories.OrderBy(c=> c.Name).ToList();
}
public bool UpdateCategoryOrder(Category category)
{
_categoryContext.Update(category);
return Save();
}
public bool Save()
{
var changesMade = _categoryContext.SaveChanges();
return changesMade >= 0 ? true : false;
}
}
In my controller I am taking the list of categories in request body and comparing them with existing categories on field adminOrder.
If they are not equal I am updating the database
public IActionResult UpdateCategory([FromBody] List < Category > categoriesList)
{
var existingList = _categoryRepository.GetCategories();
foreach(var cat in categoriesList)
{
if (existingList.Where(c => c.Id == cat.Id && c.adminOrder != cat.adminOrder)
{
_categoryRepository.UpdateCategory(cat) // since it returns true I can use if statement to check if it fails
}
}
}
I am not sure it is the right way to do or not.
I went through few docs like
https://dba.stackexchange.com/questions/201898/user-defined-ordering-in-sql
https://begriffs.com/posts/2018-03-20-user-defined-order.html
Not sure how to implement them in EF

Include inner item when return JSon

I have an Employee class, taken from a database. In that Employee class, I want to add a Manager object, which is also an Employee, based on the managerCode that is in the Employee database. See below for the class.
The managerCode is not defined as a key, . I don’t need recursion, i.e. I don’t need the Manager’s manager, etc. Just one level, the Employee and his manager.
Using .NET 4.5, c#, OData v4
I am using OData to send back the Employee, but the Manager part isn’t added in the response, even if it’s there in the object I try to send.
What am I missing? Something in the WebApiConfig?
Thanks
Employee class, first 4 fields are directly taken from database.
Class Employee
{
[Key]
public int id { get; set; }
public string employeeCode { get; set; }
public string employeeName { get; set; }
public string managerCode { get; set; }
[NotMapped]
public Employee Manager { get; set; }
}
Controller class. GetEmployeeById(id) will get Employee(s) with their Manager.
[HttpGet]
[EnableQuery]
[ODataRoute("employeeById(id={id})")]
public IHttpActionResult employeeById([FromODataUri]int id)
{
var sets = dbContext.GetEmployeeById(id);
if (!sets.Any())
return NotFound();
return this.CreateOKHttpActionResult(sets);
}
WebApiConfig
public static void Register(HttpConfiguration config)
{
config.MapODataServiceRoute("ODataRoute", "odata",GetEdmModel(),
new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
config.EnsureInitialized();
}
private static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.Namespace = "employee_odata";
builder.ContainerName = "employee_odataContainer";
builder.EntitySet<Employee>("Employee");
builder.EnableLowerCamelCase();
var unboundGetEmployee = builder.Function("employeeById");
unboundGetEmployee.Returns<Employee>();
unboundGetEmployee.Parameter<int>("id");
unboundGetEmployee.Namespace = "employee_odata.Functions";
return builder.GetEdmModel();
}
SOLUTION
Remove unboundGetEmployee from WebApiConfig (not needed).
Make Manager item in Employee class virtual, without the [NotMapped]:
public virtual Manager Manager { get; set; }
Controller:
[EnableQuery]
[ODataRoute("Employee({id})")]
public IHttpActionResult employeeById([FromODataUri]int id)
{
//handle data return normally...
//I have to detect $expand=Manager, to fetch the actual Manager object.
//otherwise it's null (not linked with primary key)
}
With these fee changes, $expand is working well.
You need to add $expand to show navigation property, like
localhost\odata\employee_odata.Functions.employeeById(id=1)?$expand=Manager
And for get employee by Id, I suggest you to use this method in controller:
[EnableQuery]
public IHttpActionResult Get(int id)
{
var sets = dbContext.GetEmployeeById(id);
if (!sets.Any())
return NotFound();
return this.CreateOKHttpActionResult(sets);
}
Then request like localhost\odata\Employee(1) can route to that method.

How can I send a query to an IQuerable method on an ASP.NET controller?

I am using Entity Framework 6 and trying to get some data from my controller. The data is from a table of words which has a key of WordId (the actual words).
My controller has this method:
[Route("Get")]
public IQueryable<Word> Get()
{
return db.Words;
}
Here’s my Word object:
public class Word
    {
        public string WordId { get; set; } // WordId (Primary key) (length: 20)
        public int CategoryId { get; set; }
    }
Is there a way that I can use this method to just get those words that have a first character between A and E ?
Never return Entity or IQueryables from the controller, instead create a model and return it as mentioned below:
string matchStr = "abcde";
[Route("Get")]
public List<WordModel> Get()
{
return db.Words.Where(p=>matchStr.ToUpper().Contains(p.UniqueID.ToUpper().FirstOrDefault())).Select(p=>new WordModel(){
WordId = p.WordId,
CategoryId = p.CategoryId
}).ToList();
}
Create a model class WordModel, as
public class WordModel
{
public string WordId { get; set; }
public int CategoryId { get; set; }
}
I agree with Sophia. It is a good idea to expose view models (designed based on the view) rather than the actual entities. Lets assume WordVm is that in your case. The return type form the controller action will be a List. Add a reporsitory or a manager class that will be called from your action method that returns your result. I am creating a manager that has access to the db context class with the Word entity.
[Route("Get")]
public List<WordVm> Get()
{
var manager = new WordsManager()
return manager.GetWords();
}
public class WordsManager
{
public List<WordVm> GetWords()
{
return repo.Words.Where(a => {
var t = a.Trim().ToUpper().Substring(0, 1)[0];
return t >= 'A' && t <= 'E';
}).ToList();
}
}
Thank you,
Soma.

Unable to determine the serialization information for i => i.Id using interfaces

First up, I know there is already questions out there with this error message, but I haven't found any that relate to the use of using interfaces with this type of query.
I'm currently trying to update a MongoDB entity using the C# Driver 2.0. However, I'm getting an error when it's trying to build the query (I'm assuming it's the Builders<T>.Filter.Eq(i => i.Id, entity.Id) bit of code) and I'm getting the following error:
Unable to determine the serialization information for i => i.Id.
I've got the following class that I'm trying to update
public interface IEntity {
string Id { get; set; }
}
public interface ITrack : IEntity {
string Name { get; set; }
}
public class TrackDTO : ITrack
{
[BsonId]
public string Id { get; set; }
public string Name { get; set; }
}
I'm then using the interface to save the object into the database using the following method in a generic DAO class to replace the entire document. Note, in the example below T is coded as ITrack (i.e. TrackDao = new Dao<ITrack>) but when the object is passed in at runtime it's a TrackDTO object (which is correct):
public async Task<T> Save(T entity)
{
// Save the entity to the collection.
ReplaceOneResult result = await _collection.ReplaceOneAsync(Builders<T>.Filter.Eq(i => i.Id, entity.Id), entity, new UpdateOptions() { IsUpsert = true });
// return the saved user object.
return entity;
}
I don't know if the Id property of my IEntity class also requires the [BsonId] attribute, but I'd like to avoid this if possible, as I want to keep my model layer (where IEntity resides) free of any database platform specific references.
I've also tried adding the following class map which has had no effect either:
BsonClassMap.RegisterClassMap<TrackDTO>(cm =>
{
cm.AutoMap();
cm.MapMember(c => c.Id).SetSerializer(new StringSerializer(BsonType.ObjectId));
cm.SetIdMember(cm.GetMemberMap(c => c.Id));
});
For the same reason as not having the [BsonId] attributes in the Model layer, I don't want to have the Model classes decorated with [BsonKnownTypes] that reference DTO objects, however I don't mind if this needs to occur as part of a class map.
For
"Unable to determine the serialization information for i => i.Id."
Try to use: nameof().
Builders<T>.Filter.Eq(nameof(IEntity.Id), entity.Id)
2.
...but I'd like to avoid this if possible, as I want to keep my model
layer (where IEntity resides) free of any database platform specific
references.
My solution for problem like your and similar problems:
public interface IEntity {
[BsonId]
string Id { get; set; }
string IdEntity { get; set; }
}
[BsonIgnoreExtraElements(Inherited = true)]
public abstract class BaseEntity : IEntity
{
[BsonRepresentation(BsonType.ObjectId)]
public virtual string Id { get; set; }
[BsonIgnoreIfNull]
public string IdEntity { get; set; }
}

Categories