Map list manually from context - c#

Initially I was using automapper for this but its seems way harder for me to implement it.
Basically, I just want to return an empty list instead of null values. I can do this on projects level but not on teammates level. The API must not return a null because the UI that consumes it will have an error.
Sample of my implementation below:
Projects = !Util.IsNullOrEmpty(x.Projects) ? x.Projects : new List<ProjectsDto>(),
Ill highly appreciate if someone can guide me on how to manually map this with null/empty checking.
If you can also provide and example using automapper that too will be very helpful.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public List<ProjectsDto> Projects { get; set; }
}
public class ProjectsDto
{
public string Status { get; set; }
public List<TeammatesDto> Teammates { get; set; }
}
public class TeammatesDto
{
public string TeammateName { get; set; }
public string PreviousProject { get; set; }
}
//Get by Id
var employee = await _context.Employees
.Where(x => x.id.Equals(request.Id)
.FirstOrDefaultAsync(cancellationToken);
//Map employee
EmployeeDto ret = new EmployeeDto()
{
Id = employee.id,
Name = employee.Name,
Projects = null //TODO: map manually
}
//Get all employees
var employees = await _context.Employees.AsNoTracking()
.ToListAsync(cancellationToken);
//Map here
IList<EmployeeDto> list = new List<EmployeeDto>();
foreach (var x in employees)
{
EmployeeDto dto = new EmployeeDto()
{
Id = x.id,
Name = x.Name,
Projects = null //TODO: map manually
};
list.Add(dto);
}
return list;

Instead of materializing full entities, do the following:
var query = _context.Employees
.Select(e = new EmployeeDto
{
Id = e.id,
Name = e.Name,
Projects = e.Projects.Select(p => new ProjectDto
{
Status = p.Status,
Templates = p.Templates.Select(t => new TemplateDto
{
TeammateName = t.TeammateName,
PreviousProject = t.PreviousProject
}).ToList()
}).ToList()
}
);
var result = await query.ToListAsync();

Related

Get and Add/Update multilevel embedded/nested MongoDB document using C#

How can i make filter and add/update third, fourth level child of a mongodb document using C#.
I can add/Update till second level but not further. Please provide me a solution or any kind reference from where can get help. Is there any other way to do it except builders..Elemmatch.
Here is my classes and code:
namespace CrudWithMultilvelNestedDoc
{
public class Channel
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Episode[] Episodes { get; set; }
}
public class Episode
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Track[] Tracks { get; set; }
}
public class Track
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
public Like[] Likes { get; set; }
}
public class Like
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string Name { get; set; }
}
}
//First Method
//var filter = Builders<Channel>.Filter.And(Builders<Channel>
// .Filter.Where(x => x.Id == "5e4606e6ae7b090688671416"), // OR &
// Builders<Channel>.Filter.ElemMatch(e => e.Episodes, Builders<Episode>
// .Filter.Eq(e => e.Id, "5e460851d29c1b3df4d27b7d")));
//Second Method
//var filter = Builders<Channel>.Filter.Eq(e => e.Id, "5e4606e6ae7b090688671416")
// & Builders<Channel>.Filter.ElemMatch(e => e.Episodes, Builders<Episode>.Filter.Eq(e => e.Id, "5e46071d385a672b0cea0f86"));
//Third Method
var filter = channelFilter.ElemMatch(e => e.Episodes, episodeFilter.ElemMatch(e=> e.Tracks, trackFilter.Eq(e => e.Id, "5e460dbe2bc5e70c9cfeac21")));
var data = collection.Find(filter);
//Update Filter
var update = Builders<Channel>.Update.Push("Episodes[-1].Tracks[-1].Likes", like);
var result = collection.UpdateOne(filter, update);
//Data
{"_id":"5e4606e6ae7b090688671416","Name":"Channel 1","Episodes":[{"_id":"5e46071d385a672b0cea0f86","Name":"Episode 1","Tracks":[{"_id":"5e460dbe2bc5e70c9cfeac21","Name":"Trak 1","Likes":[]},{"_id":"5e4612d60747a2121870c815","Name":"Trak 2","Likes":[]}]},{"_id":"5e460851d29c1b3df4d27b7d","Name":"Episode 2","Tracks":[{"_id":"5e460e307ca6843758ce814e","Name":"Trak 1","Likes":[]}]}]}
As per documentation:
The positional $ operator cannot be used for queries which traverse more than one array
So using -1 is not a way forward here. The approach you should take is the $ positional filtered operator.
There's no strongly-typed representation in C# so your code can look like below:
var filter = Builders<Channel>.Filter.Eq(x => x.Id, "5e4606e6ae7b090688671416");
var like = new Like() {Name = "new like", Id = "1"};
var episodeId = "5e46071d385a672b0cea0f86";
var trackId = "5e460dbe2bc5e70c9cfeac21";
var update = Builders<Channel>.Update.Push("Episodes.$[e].Tracks.$[t].Likes", like);
var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> episodesFilter = new BsonDocument("e._id", new BsonDocument("$eq", episodeId));
ArrayFilterDefinition<BsonDocument> tracksFilter = new BsonDocument("t._id", new BsonDocument("$eq", trackId));
arrayFilters.Add(episodesFilter);
arrayFilters.Add(tracksFilter);
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var result = mongoDBCollection.UpdateOne(filter, update, updateOptions);

Fill Model Class With query results

I Have the Following Model
ProductDetails.cs
public class ProductDetails
{
public string ProductName { get; set; }
public string ProductDescription { get; set; }
public string ProductCodeID { get; set; }
public string CategoryName { get; set; }
public List<ProductDetails> lstProductDetails { get; set; }
}
Need to fill this model class with the query results that I'm getting from the DB
This is what I tried ..
var results = from a in db.ProductInfoes where productCodeID.Equals(productCodeID)
select new ProductDetails
{
ProductName = a.Product_Name.ToString(),
ProductCategoryID= a.Category_ID.ToString(),
ProductDescription = a.Product_Description.ToString()
};
What's the best way to update model class with query results?
you can try extending ProductInfoes and using automapper.
public **partial class** ProductInfoes {
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<AuthorModel, AuthorDTO>();
});
IMapper iMapper = config.CreateMapper();
public ProductDetails Map()
{
return iMapper.Map<ProductInfoes , ProductDetails>(this);
}
}
Remove the lstProductDetails property from the ProductDetails class.
to get all data (results will be of type: List<ProductDetails>)
var results = db.ProductInfoes
.Select(x => new ProductDetails()
{
ProductName = x.Product_Name.ToString(),
ProductCategoryID= x.Category_ID.ToString(),
ProductDescription = x.Product_Description.ToString()
}).ToList();
to get one record (result will be of type: ProductDetails):
var result = db.ProductInfoes.FirstOrDefault(x => x.productCodeID.Equals(productCodeID))
.Select(x => new ProductDetails()
{
ProductName = x.Product_Name.ToString(),
ProductCategoryID= x.Category_ID.ToString(),
ProductDescription = x.Product_Description.ToString()
};
(Add namespace System.Linq)
public ProductDetails getSelectedProductDetails(string productCodeID)
{
try
{
NaturesKingdomUKEntities db = new NaturesKingdomUKEntities();
List<ProductInfo> lst_ProductInfo = db.ProductInfoes.Where(x => x.Product_Code_ID.Equals(productCodeID)).ToList();
ProductDetails prd = new ProductDetails();
prd = lst_ProductInfo
.Where(y => y.Product_Code_ID.Equals(productCodeID))
.Select(x => new ProductDetails { ProductName = x.Product_Name, ProductCodeID = x.Product_Code_ID, UnitPrice=Convert.ToDouble(x.Unit_Price), ProductDescription=x.Product_Description, ProductAdditionalDescription=x.Product_Additional_Description, ProductType=x.Product_Type })
.FirstOrDefault();
return prd;
}
catch (Exception ex)
{
throw;
}
}
Finally it works with this way. Thanks a lot guys #Matt.G

How to get the newly created object inside the .Select() in a LINQ Query

How can I get a reference to the 'parent' object in the Linq below. Something like the way EF does it when you query for objects that are of EF Classes?
void Main()
{
IEnumerable<SomeModel> Brands = ....;
var list = Brands
.Select(b => new BrandModel()
{
ID = b.ID,
BrandName = b.Name,
Locations = b.Locations.Select(l => new LocationModel()
{
ID = l.ID,
LocationName = l.Name,
Brand = *here I would want the Brand object of this Location*
}).ToList()
}).ToList();
}
private class BrandModel
{
public int ID { get; set; }
public string BrandName { get; set; }
public List<LocationModel> Locations { get; set; }
}
private class LocationModel
{
public int ID { get; set; }
public string LocationName { get; set; }
public BrandModel Brand { get; set; }
}
You can create your BrandModel in two steps. First create it without locations, then set locations to it
To do so you need to convert your lambda b => new BrandModel() to block of statements b => { return new BrandModel() }. Try this code:
.Select(b =>
{
var model = new BrandModel
{
ID = b.ID,
BrandName = b.Name
};
model.Locations = b.Locations.Select(l => new LocationModel
{
Brand = model
}).ToList();
return model;
});

Insert new document in nested documents in MongoDB

I am a beginner in MongoDB. Please see my models below.
public class Technology
{
public Technology()
{
ProductGroups = new List<ProductGroup>();
}
[BsonRepresentation(BsonType.ObjectId)]
public ObjectId _id { get; set; }
public string Name { get; set; }
public IEnumerable<ProductGroup> ProductGroups { get; set; }
}
public class ProductGroup
{
[BsonRepresentation(BsonType.ObjectId)]
public ObjectId _id { get; set; }
public string Name { get; set; }
}
Now the data shows like below.
I am try to add the ProductGroup ( it's a BsonDocument Collection) collection in Technology.
Use generic types where you can. Because this code parent["ProductGroups"] is dangerous place for any refactoring.
Your task can be done in one query
var productGroup = new ProductGroup { Id = ObjectId.GenerateNewId(), Name = model.Name };
var collection = database.GetCollection<Technology>("Technology");
var update = Builders<Technology>.Update.AddToSet(x => x.ProductGroups, productGroup);
await collection.FindOneAndUpdateAsync(x => x.Id == model._id, update);
Change your Model of Technology as
[BsonElementAttribute("productgroups")]
public IList<ProductGroup> ProductGroups{ get; set; }
Then,
var productGroup = new BsonDocument().Add("_id", productGroup_id).Add("Name", name);
var technologies = database.GetCollection("technology");
var technology = technologies.FindOneById(ObjectId.Parse(technology_id));
technology["productgroups"] = new BsonArray().Add(BsonValue.Create(productGroup));
technologies.Save(technology);
#CodingDefined I change my code as per the v2.0.1.27
Please see my code below. Thank you very much for your help.
var productGroup = new BsonDocument()
.Add("_id", ObjectId.GenerateNewId())
.Add("Name", model.Name);
BsonDocument parent = null;
var _parent = Collection.FindOneByIdAs(typeof(BsonDocument), model._id);
if (_parent != null)
{
parent = _parent.ToBsonDocument();
parent["ProductGroups"] = new BsonArray().Add(BsonValue.Create(productGroup));
Collection.Save(parent);
}
Please make sure, the new child record is not clearing the existing records
parent["ProductGroups"] = parent["ProductGroups"].AsBsonArray.Add(productGroup);

Add a list of DTOs to the master DTO

I have two DTOs:
public class MasterDTO
{
public int Id { get; set; }
public string Name { get; set; }
public List<DetailDTO> Details { get; set; }
}
public class DetailDTO
{
public int Id { get; set; }
public string DetailName { get; set; }
}
Also, I have a function:
using (var context = new Context())
{
var r = context.MasterData
.Select(d => new MasterDTO
{
Id = d.Id,
Name = d.Name,
}
}
I need to fill the list of DetailDTOs too and do it in a single request.
At this moment, I have to get list of DetailsData data and add it through foreach to the MasterDTO, which, of course causes a lot of requests to the database server.
Is there a better solution?
In your data call, do an eager load on your DetailData.
Example:
var r = context.MasterData.Include("DetailData")
DetailData should be the name of your navigation property attached to your MasterData entity.
This will cause detail data to be pulled along with your call for MasterData.
The full call may look something like this:
using (var context = new Context())
{
context.LazyLoadingEnabled = false;
var r = context.MasterData.Include("DetailData")
.Select(d => new MasterDTO()
{
Id = d.Id,
Name = d.Name,
Details = d.Details.Select(dt => new DetailDTO()
{
Id = dt.Id,
DetailName = dt.DetailName
})
});
}

Categories

Resources