Web API Get with query parameters on Mongodb collection - mongodb

I have WebAPI written and it is using mongodb collection as a database.
it looks like
[
{
"Id":"5a449c148b021b5fb4cb1f66",
"airline":[
{
"airlineID":-1,
"airlineName":"Unknown",
"airlineAlias":"",
"airlineIATACode":"-",
"airlineICAOCode":"N/A",
"airlineCallsign":"",
"airlineBaseCountry":"",
"airlineActiveIndicator":"Y"
},
{
"airlineID":1,
"airlineName":"Private flight",
"airlineAlias":"",
"airlineIATACode":"1T",
"airlineICAOCode":"N/A",
"airlineCallsign":"",
"airlineBaseCountry":"",
"airlineActiveIndicator":"Y"
},
{
"airlineID":2,
"airlineName":"135 Airways",
"airlineAlias":"",
"airlineIATACode":"2T",
"airlineICAOCode":"GNL",
"airlineCallsign":"GENERAL",
"airlineBaseCountry":"United States",
"airlineActiveIndicator":"N"
}
]
}
]
I'm trying to get data using airlineIATACode attribute
public airlineModel Get(string i)
{
_collection = _db.GetCollection<airlineModel>("airline");
var res = Query<airlineModel>.EQ(p => p.airline[0].airlineIATACode, i);
return _collection.FindOne(res);
}
My controller implementation
public HttpResponseMessage Get(string IATAcode)
{
var result = objds.Get(IATAcode);
if (result != null)
return Request.CreateResponse(HttpStatusCode.OK, result);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Data not found");
}
My Model class:
public class airlineModel
{
public ObjectId Id { get; set; }
[BsonElement("airline")]
public List<airlinedata> airline { get; set; }
}
public class airlinedata
{
[BsonElement("airlineID")]
public int airlineID { get; set; }
[BsonElement("airlineName")]
public string airlineName { get; set; }
[BsonElement("airlineAlias")]
public string airlineAlias { get; set; }
[BsonElement("airlineIATACode")]
public string airlineIATACode { get; set; }
[BsonElement("airlineICAOCode")]
public string airlineICAOCode { get; set; }
[BsonElement("airlineCallsign")]
public string airlineCallsign { get; set; }
[BsonElement("airlineBaseCountry")]
public string airlineBaseCountry { get; set; }
[BsonElement("airlineActiveIndicator")]
public string airlineActiveIndicator { get; set; }
}
When I run app and browse http://localhost:60387/api/airlineAPI?IATAcode=1T
it says, Data not found
What can I do to solve this problem?

You can try this.
public airlinedata Get(string i)
{
var _collection = database.GetCollection<airlineModel>("airline");
var filter = Builders<airlineModel>.Filter
.ElemMatch(model => model.airline, airline => airline.airlineIATACode == i);
var projection = Builders<airlineModel>.Projection
.Include(model => model.airline[-1]);
var airlineModel = _collection.Find(filter)
.Project<airlineModel>(projection)
.Single();
return airlineModel.airline.Single();
}
Note that you don't need to put mapping attributes on each field. The default mapping does exactly what you did with attributes.
The only attribute I suggest you to use is [BsonId] to tell MongoDB which is your _id field.
public class airlineModel
{
[BsonId(IdGenerator = typeof(ObjectIdGenerator))]
public ObjectId Id { get; set; }
...
}
Finally, in case of a large collection, don't forget to create an index on airlineIATACode field, otherwise the search would perform an expensive COLLSCAN.

Related

Using LINQ to query nested object in CosmosDB

I'm trying to query on a LicenseId, which is unique and lives in a doubly nested object. I want to return the entire SoftwareOrderEntity object and filter out everything except the result with the matching Id. So far nothing I have tried works due to limitations with Cosmos...
Classes:
public class SoftwareOrderEntity : IUpdateAuditable, IInsertAuditable
{
public string? OrderId { get; set; }
public SoftwareOrderEntityExternalProperties? ExternalProperties { get; set; }
}
public class SoftwareOrderEntityExternalProperties
{
public List<ProductsOnOrder>? ProductsOnOrder { get; set; }
}
public class ProductsOnOrder
{
public string? ProductId { get; set; }
public List<ProductLicenseIds>? ProductLicenseIds { get; set; }
}
public class ProductLicenseIds
{
public string? LicenseId { get; set; }
public string? AssignedEntityId { get; set; }
}
I've tried many variations of LINQ, such as...
SoftwareOrders.Where(x => x.ExternalProperties.ProductsOnOrder.Where(y => y.ProductLicenseIds.Where(z => z.LicenseId.Contains(licenseId)).Count() > 0).Count() > 0);
And
var res = from c in domainContext.SoftwareOrders
from m in c.ExternalProperties.ProductsOnOrder
from x in m.ProductLicenseIds
where x.LicenseId == licenseId
select c;
EDIT: Any() is not supported by cosmos, which is part of the issue

One API call to retrieve all items in the model

I created a simple web api using Net Core 2.2. I have this api controller below, that gets one particular dungeon.
It is returning a dungeon as JSON, but it's not returning the MonsterList associated with the dungeon.
So this is my controller:
// GET: api/DungeonLists/5
[HttpGet("{id}")]
public async Task<ActionResult<DungeonList>> GetDungeonList(Guid id)
{
var dungeonList = await _context.DungeonList.FindAsync(id);
if (dungeonList == null)
{
return NotFound();
}
return dungeonList;
}
And here is my model for the Dungeon. As you can see, it has a MonsterList.
public partial class DungeonList
{
public DungeonList()
{
MonsterList = new HashSet<MonsterList>();
}
public Guid DungeonId { get; set; }
public string DungeonName { get; set; }
public string DungeonDesc { get; set; }
public string MapArea { get; set; }
public bool ShowProgress { get; set; }
public bool? DungeonResumable { get; set; }
public virtual ICollection<MonsterList> MonsterList { get; set; }
}
Here is my MonsterList model:
public partial class MonsterList
{
public string MonsterId { get; set; }
public Guid DungeonId { get; set; }
public string MonsterName { get; set; }
public byte? MonsterType { get; set; }
public bool IsBossMonster { get; set; }
public virtual DungeonList Dungeon { get; set; }
}
I want the JSON to also show the list of monsters associated with the dungeon.
Is there a way to do this? Or would I need to make a separate API call?
Thanks!
You need to change your code to the following:
[HttpGet("{id}")]
public async Task<ActionResult<DungeonList>> GetDungeonList(Guid id)
{
var dungeonList = await _context.DungeonList
.Include(i => i.MonsterList)
.FirstOrDefaultAsync(p => p.Id = id);
if (dungeonList == null)
{
return NotFound();
}
return dungeonList;
}
Additionally, since you arent using LazyLoading, you dont need the [virtual] on the MonsterList collection

C# MongoDB Driver: How to insert a new subdocument into an existing document

Docoument structure
public class Document:
{
[BsonRepresentation(BsonType.ObjectId)]
public String _id { get; set; }
[BsonIgnoreIfNull]
public List<Event> Events { get; set; }
}
public class Event
{
[BsonRepresentation(BsonType.ObjectId)]
public String _id { get; set; }
[BsonIgnoreIfNull]
public String Title { get; set; }
}
I would like to insert a new subdocument 'Event' into an existing Document using Document._id. How could I do that in csharp?
You can do it like follows:
var id = ObjectId.Parse("5b9f91b9ecde570d2cf645e5"); // your document Id
var builder = Builders<MyCollection>.Filter;
var filter = builder.Eq(x => x.Id, id);
var update = Builders<MyCollection>.Update
.AddToSet(x => x.Events, new MyEvent
{
Title = "newEventTitle",
Id = ObjectId.GenerateNewId()
});
var updateResult = await context.MyCollection.UpdateOneAsync(filter, update);
I changed your class names a bit, like this:
public class MyCollection
{
public ObjectId Id { get; set; }
public List<MyEvent> Events { get; set; }
}
public class MyEvent
{
public ObjectId Id { get; set; }
public string Title { get; set; }
}
since I thought Document and Event were not good names but you can change them back.
Also, note that the type of Id property is ObjectId and not string.

Error when creating mappings with AutoMapper

I'm trying to map objects with AutoMapper. I've created the HTTP POST controller method, which should create the new Part object to database. It should add data to both entities, Part and PartAvailabilites. Database is already existing and is scaffolded by EF Core. The error I'm receiving is:
AutoMapper created this type map for you, but your types cannot be mapped using the current configuration Part -> PartDto (Destination member list)PartManagement.Entities.Part -> PartManagement.Dto.PartDto (Unmapped properties:Balance)"
Does anyone know what could be the problem with this mapping? I tried to do the mapping in several ways but none of them is working.
Here is my mapping:
CreateMap<PartDto, PartEntity>()
.ForMember(dest => dest.FkPartAvailability,
opts => opts.MapFrom(src => new PartAvailabilities
{
Balance = src.Balance
}));
Example JSON request:
{
"name": "testPart",
"catalogNumber": 12345,
"balance": 10
}
Here are my entity classes:
public class Part
{
public long Id { get; set; }
public int PartNumber { get; set; }
public string Name { get; set; }
public PartAvailabilities FkPartAvailability { get; set; }
}
public class PartAvailabilities
{
public PartAvailabilities()
{
Parts = new HashSet<Part>();
}
public long Id { get; set; }
public decimal Balance { get; set; }
public ICollection<Part> Parts { get; set; }
}
public class PartDto
{
public string Name { get; set; }
public int PartNumber { get; set; }
public decimal Balance { get; set; }
}
This is Create method in the ManagementService class:
public async Task<PartDto> Create(PartDto request)
{
var part = _mapper.Map<PartDto, PartEntity>(request);
var createdPart = partRepository.Add(part);
await partRepository.UnitOfWork.SaveChangesAsync();
return _mapper.Map<PartDto>(createdPart);
}
And here is HttpPost method from controller:
[HttpPost]
public async Task<IActionResult> Part_Create([FromBody] PartDto request)
{
PartDto createdPart;
try
{
if (request != null)
{
createdPart = await _partManagementService.Create(request);
return Ok(request);
}
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
return Ok(new string[] { "Part created" });
}
The message is trying to tell you that there is no map between Part and PartDto. AM will create a map for you, but that map is not valid, because PartDto.Balance cannot be mapped. So you have to create the map and tell AM how to map Balance. Things might be easier to understand if you set CreateMissingTypeMaps to false.

Cannot deserialize the current JSON object - Newtonsoft

I'm having problems resolving this error message. I've looked at some other answers on here and changed some things but I still receive this error:
Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Clocker.Models.PeopleLocationForUser]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
This is my class:
namespace Clocker.Models
{
public class PeopleLocationForUser
{
string locationPeople { get; set; }
public users users { get; set; }
}
public class users
{
public int EB_Counter { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int TATokenValue { get; set; }
}
}
This is the method that errors on the deserialize line:
public static async Task<PeopleLocationForUser> GetPeopleLocationForUser(string UserName, int LocationId)
{
Uri uri = new Uri(URL + "GetPeopleLocationForUser" + "?username=" + UserName + "&locationid=" + LocationId);
HttpClient myClient = new HttpClient();
var response = await myClient.GetAsync(uri);
var content = await response.Content.ReadAsStringAsync();
var test = JsonConvert.DeserializeObject<List<PeopleLocationForUser>>(content);
//return something when it's working
return null;
}
This is the start of the Json data:
{"result":true,"locationPeople":[{"EB_Counter":101,"FirstName":"RSS","LastName":"13.11.1","TATokenValue":"TS_101_1_RSS_SWIPE"},{"EB_Counter":102,"FirstName":"RSS","LastName":"13.11.2","TATokenValue":"TS_102_1_RSS_SWIPE"},{"EB_Counter":93,"FirstName":"RSS","LastName":"13.7.1","TATokenValue":"TS_93_1_RSS_SWIPE"},{"EB_Counter":94,"FirstName":"RSS","LastName":"13.7.10","TATokenValue":"TS_94_1_RSS_SWIPE"},{"EB_Counter":95,"FirstName":"RSS","LastName":"13.8.2","TATokenValue":"TS_95_1_RSS_SWIPE"},{"EB_Counter":99,"FirstName":"RSS","LastName":"13.9.2","TATokenValue":"TS_99_1_RSS_SWIPE"},
This is what my Json data looks like when it arrives:
I hope you can help. The end result is that I'm trying to get this data into a list so I can use it in a Xamarin ListView.
You are receiving list and in the class you are expecting just one instance of user, this is how the class should be:
public class LocationPeople
{
public int EB_Counter { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string TATokenValue { get; set; }
}
public class RootObject
{
public bool result { get; set; }
public List<LocationPeople> locationPeople { get; set; }
}
var test = JsonConvert.DeserializeObject<RootObject>(content);