C# Web API DTO combining two Objects in MVC - c#

Hi I'm a newbie to C# and DTO's and I'm looking for a bit of advice in writing a method. Basically I have two transfer objects, Members and Source. What I'm trying to achieve is display a list of Members from a specific Source.
The only problem is I need to be able to display Members associated with a SourceId from a SourceRef. As I dont want to pass the sensitive MemberID and SourceId so each has a reference id and thats how I will be identifying them in my API
Member Object
public class MemberObj
{
public int memId { get; set; }
public String memRef { get; set; }
public String fName { get; set; }
public String lName { get; set; }
public String email { get; set; }
public String detail { get; set; }
public int sourceId { get; set; }
}
Source Object
public class SourceObj
{
public int sourceId { get; set; }
public String sourceRef { get; set; }
}
So I would like to go to the address for example
http://..../api/Member/Source/{sourceRef}
and display the list of Members associated to the sourceId via the sourceRef
I came up with something along these lines....
public IEnumerable<MemberObj> GetMem(String code)
{
var sc = db.Sources;
var thisSrc = sc.Where(s => s.sourceRef == code).SingleOrDefault();
return db.Members.Select(s => new MemberObj
{
memId = s.memId,
firstName = s.firstName,
lastName = s.lastName,
email = s.emailAddress,
memRef = s.memRef
}).AsEnumerable().Where(s => s.sourceRef== thisSrc.sourceRef);
But this returns nothing.

The following accepts code as the sourceRef and returns the SourceID that the ref corresponds too.
From here, it simply filters all members to only the ones with the matching sourceID. (I don't have a copy of VS near me so the syntax may be out! If only Notepad++ had intelisense...)
public IEnumerable<MemberObj> GetMem(String code)
{
int soureID = db.Sources.Where(s => s.sourceRef == code).SingleOrDefault().sourceID; //I'm assuming code is the source ref??
//Insert and handle your sourceID == 0 checks here.
//...
return db.Members.Where(m => m.sourceId == sourceID);
}

This should work:
public IEnumerable<MemberObj> GetMem(String code)
{
var thisSrc = db.Sources
.Where(s => s.sourceRef == code)
.SingleOrDefault();
if(thisSrc == null)
return Enumerable.Empty<MemberObj>();
return db.Members.Where(m => m.sourceId == thisSrc.sourceId);
}
Take in account, that you should handle the case when there are more than one source by given code (SingleOrDefault throws an exception in that case.)
If you are sure that is not a case use FirstOrDefault instead.

Just building into Hamlet's answer, you could do something like this to return a DTO instead of your Member Entity:
public IEnumerable<MemberDTO> GetMem(String code)
{
//Get the source object by sourceRef
var thisSrc = db.Sources
.Where(s => s.sourceRef == code)
.SingleOrDefault();
if(thisSrc == null)
return Enumerable.Empty<MemberObj>();
//Filter Members by sourceId and map the results to a MemberDTO with less fields
var data = (from m in db.Members
where m.sourceId == thisSrc.sourceId
select new MemberDTO
{
MemberId = m.memId,
//map the rest of your DTO fields..
}).ToList();
return data;
}

Related

Count the number of element present in the inner list

public Class Users
{
string UserName,
List<UserDesktop> userDesktop
}
public class UserDesktop
{
int ID,
string DesktopName,
string type -- here Type value is A, B;
}
Here I have to find the users count having desktop assigned of type A
Users variable containing all data.
List<Users> finalUsers = users.Count(s=>s.userDesktop.where(x=>x.type == "A"))
Please let me know the correct way of doing it.
You might want to use SelectMany to flatten the list then count it:
int numberOfUsersWithDesktopOfTypeA = users.SelectMany(u => u.userDesktop)
.Count(ud => ud.type == "A");
If you want the list of users that have an A desktop:
var usersWithDesktopA = Users.Where(u => u.Any(ud => ud.type == "A"));
var numberOfUsersWithDesktopA = usersWithDesktopA.Count();
To query the user(s) who has UserDesktop of type 'A':
List<Users> hasDesktopAUsers = users.Where(u => u.userDesktop.Any(ud => ud.type == "A"))
.ToList();
Next, you can get the number of count from the previous result as:
int numberOfDesktopAUsers = hasDesktopAUsers.Count;
Out of topic:
It is recommended to have a proper naming convention for handling single and plural terms to avoid confusion. For example:
public class User
{
public string UserName { get; set; }
public List<UserDesktop> UserDesktops { get; set; }
}
public class UserDesktop
{
public int ID { get; set; }
public string DesktopName { get; set; }
public string Type { get; set; }
}
From the above, you should name as User as it is an User object while UserDesktops with 's' as it is a collection set.
Final solution:
List<User> hasDesktopAUsers = users.Where(u => u.UserDesktops.Any(ud => ud.Type == "A"))
.ToList();
int numberOfDesktopAUsers = hasDesktopAUsers.Count;

How to create a where condition to an object inside an object in c# linq?

Let's say I have a class like:
public class TrainingPlan
{
public int Id { get; set; }
public int ProjectId { get; set; }
public string TrainingPlanName { get; set; }
public List<Training> TrainingList { get; set; }
public bool IsDeleted { get; set; }
}
And a Training object inside it:
public class TrainingViewModel : AuditViewModel
{
public int Id { get; set; }
public int ProjectId { get; set; }
public int TrainingPlanId { get; set; }
public bool IsDeleted { get; set; }
public ProjectViewModel ProjectObject { get; set; }
public TrainingPlanViewModel TrainingPlanObject { get; set; }
}
I could write something like this in order to retrieve TrainingPlans where IsDeleted = false and also retrieve the Training Objects attached to it.
var result = _trainingPlanRepository.FindBy(t => t.ProjectId == projectId && t.IsDeleted == false).ToList();
But how do I set a condition for the Training objects where IsDeleted = false also?
You could use Any() like below. Assumption Your t has a List<Training> which you want to query as well to get the non-deleted ones.
var result = _trainingPlanRepository.
Where(t => t.ProjectId == projectId && !t.IsDeleted &&
t.TrainingList.Any(x => !x.IsDeleted)).ToList();
var trainingPlans = _trainingPlanRepository
.Where(t => t.ProjectId == projectId && t.IsDeleted == false)
.ToList();
Now, In trainingPlans variable, All related training objects exist in it, so we want to filter it bases on IsDelted Property. so you can use below code:
foreach (var item in trainingPlans)
{
item.trainingList = item.trainingList.Where(t => !t.IsDelete).ToList();
}
good luck.
I guess there is a one-to-many relation (possibly many-to-many) between TrainingPlan and Training: every TrainingPlan has zero or more Tranings, and every Training belongs to exactly one TrainingPlan, namely the Training that the foreign key TrainingId (or something similar) refers to.
So somewhere deep inside your repository, you'll have two IQueryable sequences:
IQueryable<Training> trainings = ...
IQueryable<TrainingPlan> trainingPlans = ...
You want to query all trainingplans that are not deleted and that have a ProjectId equal to projectId, together with all its not deleted TrainingPlans.
Using method syntax this is fairly straightforward:
var result = trainingPlans
// keep only the training plans you want to keep:
.Where(trainingPlan => trainingPlan.ProjectId == projectId && !trainingPlan.IsDeleted)
// GroupJoin each trainingPlan with its trainings:
.GroupJoin(trainings,
trainingPlan => trainingPlan.Id, // from each training plan take the Id
training => training.TrainingPlanId, // from each training take the foreign key
(trainingPlan, matchingTrainings) => new // take the training plan with all its matching trainings
{ // to make a new
// Select the trainingplan properties you plan to use
Id = trainingPlan.Id,
Name = trainingPlan.Name,
...
// Keep only the non-deleted trainings
Trainings = matchingTrainings.Where(training => !training.IsDeleted)
.Select(training => new
{
// Select only the training properties that you plan to use:
Id = training.Id,
Date = training.Date,
...
// not needed, you know the value:
// TrainingPlanId = training.TrainingPlanId,
})
.ToList(),
});
Another method would be to remove the non-deleted trainings before the GroupJoin:
var validTrainingPlans = trainingPlans.Where(...);
var validTrainings = trainings.Where(training => !training.IsDeleted);
var result = validTrainingPlans.GroupJoin(validTrainings,
... etc

Linq Value cannot be null on FK

Using C# MVC5 Visual studio 2015.
I have a method that contains the following code:
public List<OffersOnPropertyViewModel> Build(string buyerId)
{
var filtered = _context.Properties.Where(x => x.Offers.Any(c => c.BuyerUserId == buyerId)).ToList();
var model = filtered.Select(c =>
{
var item = new OffersOnPropertyViewModel()
{
PropertyType = c.PropertyType,
NumberOfBedrooms = c.NumberOfBedrooms,
StreetName = c.StreetName,
Offers = c.Offers.Where(d => d.BuyerUserId == buyerId).Select(x => new OfferViewModel
{
Id = x.Id,
Amount = x.Amount,
CreatedAt = x.CreatedAt,
IsPending = x.Status == OfferStatus.Pending,
Status = x.Status.ToString(),
BuyerUserId = x.BuyerUserId
}),
};
return item;
}).ToList();
//TODO: refactor, shorten linq, duping where clause
return model;
}
Here is the model:
public class Property
{
[Key]
public int Id { get; set; }
[Required]
public string PropertyType { get; set; }
[Required]
public string StreetName { get; set; }
[Required]
public string Description { get; set; }
[Required]
public int NumberOfBedrooms { get; set; }
[Required]
public string SellerUserId { get; set; }
public bool IsListedForSale { get; set; }
public ICollection<Offer> Offers { get; set; }
}
In the DB Offers table has the property id as its FK.
The method fails at runtime saying the Value cannot be null.
When I step through I notice the filtered results (in the example its 1 result), is saying offers is null. Although the query just filtered the results based on "x.Offers".
I simply need a way to retrieve a list of property's that have offers made by the buyerId provided. Is my approach wrong? or am i missing a one liner?
Thanks
You will need to add Include() to your LINQ query to bring in child objects, as follows:
var filtered = _context.Properties.Include("Offers")
.Where(x => x.Offers.Any(c => c.BuyerUserId == buyerId)).ToList();
The reason your filter works with the Any() is because when generating the SQL query, this part forms the WHERE clause and is not included in the SELECT.

Create nested model from List

I have an existing class
public class Employee
{
public int? EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int? LocationId { get; set; }
public string LocationName { get; set; }
public int? DesignationId { get; set; }
public string DesignationName { get; set; }
}
Code for fetching data from database:
using (var ds = new EmployeeDS())
{
using (var dataAdapter = new DataSet.EmployeeDSTableAdapters.EmployeeTableAdapter())
{
dataAdapter.FillEmployee(ds.Employee,Id);
var result = (from DataRow row in ds.Employee.Rows
select new Employee
{
EmployeeId = (row["EmployeeID"] == DBNull.Value) ? null : (int?)row["EmployeeID"],
EmployeeName = (row["EmployeeName"] == DBNull.Value) ? string.Empty : (string)row["EmployeeName"],
LocationId = (row["LocationId"] == DBNull.Value) ? null : (int?)row["LocationId"],
LocationName = (row["LocationName"] == DBNull.Value) ? string.Empty : (string)row["LocationName"],
DesignationId = (row["DesignationId"] == DBNull.Value) ? null : (int?)row["DesignationId"],
DesignationName = (row["DesignationName"] == DBNull.Value) ? string.Empty : (string)row["DesignationName"],
}).ToList();
}
}
Its working fine.But this returning multiple rows for a employee having multiple location or designation.So I need to return the data as nested model like :
public class EmployeeNested
{
public int? EmployeeId { get; set; }
public string EmployeeName { get; set; }
public List<Location> Locations { get; set; }
public List<Designation> Designations { get; set; }
}
public class Location
{
public int? LocationId { get; set; }
public string LocationName { get; set; }
}
public class Designation
{
public int? DesignationId { get; set; }
public string DesignationName { get; set; }
}
I am using this code for returning nested model:
var tempList=result.GroupBy(x => x.EmployeeId, (key, g) => g.OrderBy(e => e.EmployeeId).First());
foreach (var item in tempList)
{
item.Designations = result
.Where(x => x.EmployeeId== item.EmployeeId)
.Select(x => new Designation
{
DesignationId = x.DesignationId,
DesignationName = x.DesignationName
}).ToList();
item.Locations = result
.Where(x => x.EmployeeId== item.EmployeeId)
.Select(x => new Location
{
LocationId = x.LocationId,
LocationName = x.LocationName
}).ToList();
}
Question:
Is there any better solution to convert a flat list to nested List?.
Is it possible to create a generic method for converting flat list to
nested list?. So that I can reuse it for another functions too.
Is it possible to create a nested list directly from dataset?
I'm sure there is a good solid pattern for doing this, I just can't find it.
I don't think there is a way to create a nested list from a dataset other than looping through the results and some programming logic. If your using a relational database I would recommend using entity framework which would generate something similar to your EmployeeNested class once you have your database properly configured.
Is there any better solution to convert a flat list to nested List?.
As per my knowledge better solution is to create utility methods which take input list and processed it into another type of list. Please refer below sample code.
public static List<EmployeeNested> ProcessEmployeeData(List<Employee> result)
{
return result.Where(x => x.EmployeeId.HasValue).GroupBy(x => x.EmployeeId.Value).Select(x => new EmployeeNested
{
EmployeeId = x.Key,
EmployeeName = x.First().EmployeeName,
Locations = x.Select(s => new Location
{
LocationId = s.LocationId,
LocationName = s.LocationName
}).ToList(),
Designations = x.Select(s => new Designation
{
DesignationId = s.DesignationId,
DesignationName = s.DesignationName
}).ToList()
}).ToList();
}
Is it possible to create a generic method for converting flat list to
nested list?. So that I can reuse it for another functions too.
We can't make generic methods unless if there is common property which inherits all property, and how that generic method knows that in which collection need to add some specific object. If there is the way then it going to be complex to implement.
Is it possible to create a nested list directly from dataset?
As per my understanding, we can't make nested collection directly from a dataset, because dataset has no knowledge about in which type of collection we want to bind data which is fetched from the database. if you want to make direct collection then there must be an underlying structure which query database according to our model and bind, that is how ORM framework works like entity framework.

Remove Item From Object SubList (LINQ)

I have an object that looks like this :
public class Consortium
{
public string Id { get; set; }
[JsonConverter(typeof(EnumDescriptionConverter))]
public SourceType Type { get; set; }
public List<UserLibrary> Branches { get; set; }
}
Each Consortium has a list of UserLibrary's associated with it, and that class looks like this :
public class UserLibrary
{
public string LibraryId { get; set; }
public string RetailerId {get; set;}
public string UserId { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
}
I have a method which will allow the user to remove a library from one of their consortium (note: There may be many branches associated to the consortium).
However, I'm only being supplied a LibraryId, so I'm forced to traverse their list of Consortium, find which one contains the given id, then iterate over the branches and remove the one that matches the id.
Here is how I'm currently accomplishing this :
// Get the current list of consortiums
var user = _mediator.Send(new GetUserProfileCommand { UserProfileId = _principle.UserProfileId });
var userConsortia = user.SavedConsortia;
// the consortium to remove the library from
var branchToRemove = _libraryService.GetLibrary(id);
var consortRemove = new UserConsortium();
foreach (var userConsortium in userConsortia)
{
if (userConsortium.Branches.FirstOrDefault(c => string.Equals(c.LibraryId, id, StringComparison.OrdinalIgnoreCase)) != null)
{
consortRemove = userConsortium;
}
}
// if the consortium id is null, something is f*
if (consortRemove.Id == null)
{
return new JsonDotNetResult(HttpStatusCode.BadRequest);
}
// first remove the consortia
userConsortia.Remove(consortRemove);
// remove the branch from the consortium
consortRemove.Branches.RemoveAll(ul => string.Equals(ul.LibraryId, id, StringComparison.OrdinalIgnoreCase));
// add it back in without the branch
userConsortia.Add(consortRemove);
Question :
Is there a LINQ expression I'm missing here that can help me consolidate this logic, or is there a better way of doing this?
Yes, there are a few approaches you can take depending on taste. The easiest way to simplify what you've got would be this:
var branchToRemove = _libraryService.GetLibrary(id);
// .Single() will throw an exception unless there is one and only one match.
var consortRemove = userConsortia.Single(
c => c.Branches.Any(
b => string.Equals(b.LibraryId, id, StringComparison.OrdinalIgnoreCase));
// remove the consortia
userConsortia.Remove(consortRemove);
Why not something like this? It looks to me from your code that you want to remove the targeted "removal branch" from all consortiums in you collection.
foreach (UserConsortium userConsortium in userConsortia)
{
userConsortium.Branches.RemoveAll(c => string.Equals(c.LibraryId, id, StringComparison.OrdinalIgnoreCase));
}

Categories

Resources